Quick Start
Connect to any TikTok LIVE stream and receive real-time events in under 5 lines of code.
import WebSocket from 'ws';
const ws = new WebSocket('wss://api.tik.tools?uniqueId=streamer_name&apiKey=YOUR_KEY');
ws.on('message', (data) => {
const event = JSON.parse(data);
if (event.event === 'chat') {
console.log(`${event.data.user.uniqueId}: ${event.data.comment}`);
}
});Sign up at tik.tools/pricing to get a free API key. No credit card required.
🚀 Try It Now - 5-Minute Live Demo
Copy-paste this script, run it, and see real-time TikTok LIVE events in your terminal. Works on the free Sandbox tier - no credit card needed.
Replace YOUR_API_KEY with your free key from tik.tools and LIVE_USERNAME with any currently live TikTok user. Runs for 5 minutes within sandbox limits.
// demo.mjs - TikTok LIVE in 5 minutes
// npm install ws
import WebSocket from 'ws';
const API_KEY = 'YOUR_API_KEY'; // Get free key → https://tik.tools
const LIVE_USERNAME = 'tv_asahi_news'; // Any live TikTok username
const ws = new WebSocket(wss://api.tik.tools?uniqueId=${LIVE_USERNAME}&apiKey=${API_KEY}`);
let events = 0;
ws.on('open', () => console.log(`\n✅ Connected to @${LIVE_USERNAME} - listening for 5 min...\n`));
ws.on('message', (raw) => {
const msg = JSON.parse(raw);
events++;
const d = msg.data || {};
const user = d.user?.uniqueId || '';
switch (msg.event) {
case 'chat': console.log(`💬 ${user}: ${d.comment}`); break;
case 'gift': console.log(`🎁 ${user} sent ${d.giftName} (${d.diamondCount}💎)`); break;
case 'like': console.log(`❤️ ${user} liked × ${d.likeCount}`); break;
case 'member': console.log(`👋 ${user} joined`); break;
case 'roomUserSeq': console.log(`👀 Viewers: ${d.viewerCount}`); break;
case 'roomInfo': console.log(`📡 Room: ${msg.roomId}`); break;
default: console.log(`📦 ${msg.event}`); break;
}
});
ws.on('close', () => console.log(`\n📊 Done! Received ${events} events.\n`));
setTimeout(() => ws.close(), );Node.js SDK
The @tiktool/live npm package provides a high-level client and utility functions for working with TikTok LIVE data.
npm install @tiktool/livePython SDK
The tiktok-live-api PyPI package provides a high-level Python client for TikTok LIVE streams - sync and async support, decorator-based event handling, and built-in live captions.
pip install tiktok-live-apiBasic Example
from tiktok_live_api import TikTokLive
client = TikTokLive("streamer_username")
@client.on("connected")
def on_connected(event):
print(f"Connected to @{event['uniqueId']}")
@client.on("chat")
def on_chat(event):
print(f"[chat] {event['user']['uniqueId']}: {event['comment']}")
@client.on("gift")
def on_gift(event):
diamonds = event.get("diamondCount", 0)
print(f"[gift] {event['user']['uniqueId']} sent {event['giftName']} ({diamonds} diamonds)")
@client.on("like")
def on_like(event):
print(f"[like] {event['user']['uniqueId']} liked (total: {event['totalLikes']})")
@client.on("follow")
def on_follow(event):
print(f"[follow] {event['user']['uniqueId']} followed")
@client.on("roomUserSeq")
def on_viewers(event):
print(f"[viewers] {event['viewerCount']} watching")
client.run()Live Captions Example
from tiktok_live_api import TikTokCaptions
captions = TikTokCaptions(
"streamer_username",
translate="en",
diarization=True,
)
@captions.on("connected")
def on_connected(event):
print(f"Listening to @{event['uniqueId']}")
@captions.on("caption")
def on_caption(event):
speaker = event.get("speaker", "")
text = event["text"]
is_final = event.get("isFinal", False)
status = "FINAL" if is_final else "partial"
print(f"[{status}] [{speaker}] {text}")
@captions.on("translation")
def on_translation(event):
print(f" -> {event['text']}")
captions.run()TikTokLiveThe main SDK class for connecting to TikTok LIVE streams via WebSocket. Handles authentication, reconnection, and event parsing automatically.
new TikTokLive({ uniqueId, apiKey, ... })
Parameters
| Name | Type | Description |
|---|---|---|
uniqueId | string | TikTok username (without @) |
apiKey | string | Your TikTool API key |
sessionId | string? | Optional TikTok session ID for authenticated features |
Returns
TikTokLive instance
Example
import { TikTokLive } from '@tiktool/live';
const client = new TikTokLive({
uniqueId: 'streamer_name',
apiKey: process.env.TIKTOOL_API_KEY
});
client.on('chat', (event) => {
console.log(`${event.nickname}: ${event.comment}`);
});
client.on('gift', (event) => {
console.log(`${event.nickname} sent ${event.giftName} x${event.repeatCount}`);
});
await client.connect();
// client.disconnect() to closecallApiHigh-level utility that handles the full sign-and-return API flow automatically: resolves room ID → calls API → fetches signed URL → returns parsed TikTok data. Ideal for server-side integrations.
callApi(options: CallApiOptions): Promise<any>
Parameters
| Name | Type | Description |
|---|---|---|
apiKey | string | Your TikTool API key |
endpoint | string | API endpoint path (e.g. '/webcast/room_info') |
uniqueId | string | TikTok username to query |
method | 'GET'|'POST' | HTTP method (default: POST) |
serverUrl | string? | API server URL (default: https://api.tik.tools) |
extraBody | Record? | Additional body fields for POST requests |
Returns
Parsed TikTok data object or null if user is not live
Example
import { callApi } from '@tiktool/live';
// Get room info
const roomInfo = await callApi({
apiKey: 'YOUR_KEY',
endpoint: '/webcast/room_info',
uniqueId: 'streamer_name'
});
console.log(roomInfo?.data?.title);
// Get stream video URLs
const video = await callApi({
apiKey: 'YOUR_KEY',
endpoint: '/webcast/room_video',
uniqueId: 'streamer_name'
});
console.log(video?.data?.stream_urls?.origin?.hls);resolveLivePageRetrieves metadata from a TikTok live page including the room ID, session cookie (ttwid), and cluster region. Results are cached for 5 minutes. Used internally by TikTokLive.connect().
resolveLivePage(uniqueId: string): Promise<LivePageInfo | null>
Parameters
| Name | Type | Description |
|---|---|---|
uniqueId | string | TikTok username (with or without @) |
Returns
LivePageInfo { roomId, ttwid, clusterRegion } or null
Example
import { resolveLivePage } from '@tiktool/live';
const info = await resolveLivePage('streamer_name');
if (info) {
console.log('Room ID:', info.roomId);
console.log('Region:', info.clusterRegion);
console.log('Cookie:', info.ttwid);
} else {
console.log('User is not live');
}resolveRoomIdResolves a TikTok username to a numeric room ID. Thin wrapper around resolveLivePage() - returns just the room ID string. Results are cached for 5 minutes.
resolveRoomId(uniqueId: string): Promise<string | null>
Parameters
| Name | Type | Description |
|---|---|---|
uniqueId | string | TikTok username (with or without @) |
Returns
Room ID string or null if not live
Example
import { resolveRoomId } from '@tiktool/live';
const roomId = await resolveRoomId('streamer_name');
if (roomId) {
console.log('Room ID:', roomId);
// Use with REST API endpoints that require room_id
}fetchSignedUrlExecutes a signed-URL response from the API server. When the API returns action: "fetch_signed_url", pass the response to this function to fetch the actual TikTok data. Used internally by callApi().
fetchSignedUrl(response: SignedUrlResponse): Promise<any>
Parameters
| Name | Type | Description |
|---|---|---|
response | SignedUrlResponse | The API response containing signed_url, headers, and cookies |
Returns
Parsed JSON data from TikTok
Example
import { fetchSignedUrl } from '@tiktool/live';
// Low-level usage (callApi handles this automatically)
const apiResponse = await fetch(
'https://api.tik.tools/webcast/room_info?apiKey=KEY',
{ method: 'POST', body: JSON.stringify({ room_id: '123' }) }
).then(r => r.json());
if (apiResponse.action === 'fetch_signed_url') {
const tiktokData = await fetchSignedUrl(apiResponse);
console.log(tiktokData);
}Authentication
All API requests require authentication via an API key or JWT token.
API Key
Pass your API key as a query parameter or header:
# Query parameter
GET /webcast/check_alive?apiKey=YOUR_KEY&unique_id=username
# Header
GET /webcast/check_alive?unique_id=username
x-api-key: YOUR_KEYJWT Token
For frontend WebSocket connections, generate a JWT token server-side and pass it as jwtKey:
wss://api.tik.tools?uniqueId=username&jwtKey=YOUR_JWTJWTs can be scoped to specific creators and have configurable expiry. See /authentication/jwt.
WebSocket Connection
Connect via WebSocket to receive real-time events from any TikTok LIVE stream.
Connection URL
wss://api.tik.tools?uniqueId=USERNAME&apiKey=YOUR_KEYMessage Format
Events are sent as JSON with the following structure:
{ "event": "chat", "data": { "type": "chat", "user": { ... }, "comment": "Hello!" } }First Message
Upon connection, the server sends a roomInfo event:
{ "event": "roomInfo", "roomId": "7123456789", "uniqueId": "streamer", "connectedAt": "..." }import WebSocket from 'ws';
const ws = new WebSocket('wss://api.tik.tools?uniqueId=streamer&apiKey=YOUR_KEY');
ws.on('open', () => console.log('Connected!'));
ws.on('message', (raw) => {
const { event, data } = JSON.parse(raw);
switch (event) {
case 'roomInfo': console.log('Room:', data.roomId); break;
case 'chat': console.log(data.user.uniqueId + ':', data.comment); break;
case 'gift': console.log(data.user.uniqueId, 'sent', data.giftName); break;
case 'like': console.log(data.user.uniqueId, 'liked ×' + data.likeCount); break;
case 'member': console.log(data.user.uniqueId, 'joined'); break;
}
});
ws.on('close', (code, reason) => console.log('Closed:', code, reason.toString()));REST API Reference
HTTP endpoints for signing, fetching room data, and managing authentication.
/webcast/sign_urlSign any TikTok webcast URL with the required X-Bogus and X-Gnarly anti-bot parameters. This is the foundation of the API - TikTok rejects unsigned requests, so this endpoint adds the cryptographic signatures needed for valid requests.
How it works: Send any raw TikTok webcast URL in the request body. The API returns the signed URL with all required parameters appended, plus the User-Agent and cookies you must use when fetching.
Use cases: Building custom TikTok integrations, signing WebSocket URLs for direct connections, or signing any TikTok API endpoint that requires X-Bogus validation.
Response data: Returns signed_url (the URL with signatures), x_bogus, x_gnarly, user_agent, and cookies fields. Always use the returned User-Agent when making the final request to TikTok.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
url | string | Yes | The TikTok URL to sign |
Response
{ "status_code": 0, "data": { "signed_url": "...", "x_bogus": "...", "x_gnarly": "...", "user_agent": "...", "cookies": "..." } }const res = await fetch('https://api.tik.tools/webcast/sign_url?apiKey=YOUR_KEY', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url: 'https://webcast.tiktok.com/...' })
});
const data = await res.json();
console.log(data.data.signed_url);/webcast/check_aliveCheck if one or more TikTok users are currently live streaming. Returns live status, room ID, stream title, and viewer count for each user.
Lookup methods: Pass unique_id (username) for single-user lookups, or room_ids (comma-separated) to check multiple rooms at once. At least one parameter is required.
Response data: Each entry includes room_id, alive (boolean), title, and userCount. Use this to build monitoring dashboards, trigger alerts when creators go live, or validate a stream before connecting via WebSocket.
Performance: This endpoint is lightweight and cached - ideal for polling. Combine with bulk_live_check for monitoring large lists of creators.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
room_ids | string | No | Comma-separated room IDs |
unique_id | string | No | TikTok username (without @) |
Response
{ "status_code": 0, "data": [{ "room_id": "...", "alive": true, "title": "...", "userCount": 1234 }] }const res = await fetch('https://api.tik.tools/webcast/check_alive?apiKey=YOUR_KEY&unique_id=username');
const { data } = await res.json();
console.log(data[0].alive ? 'Live!' : 'Offline');/webcast/rate_limitsCheck your current API key's rate limit status, tier information, and remaining quota across all features.
What's returned: Your current tier (free/pro/ultra), API request limits (per-minute sliding window), WebSocket connection limits, bulk check capacity, and feed daily quota. Also includes reset_at timestamp for when limits refresh.
Use cases: Monitor quota consumption in production, display remaining capacity in dashboards, or implement client-side throttling to avoid hitting limits.
Headers: Every API response also includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers - but this endpoint gives a comprehensive overview of all your limits in one call.
Response
{ "status_code": 0, "data": { "tier": "basic", "api": { "limit": 30, "remaining": 28, "reset_at": 1234567890 }, "websocket": { "limit": 10, "current": 2 } } }const res = await fetch('https://api.tik.tools/webcast/rate_limits?apiKey=YOUR_KEY');
const { data } = await res.json();
console.log(`${data.api.remaining}/${data.api.limit} requests remaining`);/webcast/fetchFetch live stream events via HTTP long-polling. Returns the same real-time data as WebSocket (chat, gifts, likes, battles) but over HTTP. Uses TikTok's binary protobuf protocol and returns decoded events.
How it works: The API signs and fetches TikTok's internal polling endpoint, decodes the protobuf response, and returns structured event data. Pass a cursor from the previous response for continuous polling.
Identification: Provide either unique_id (username) or room_id. If using unique_id, the server resolves the room ID automatically.
WebSocket vs Fetch: WebSocket connections are preferred for real-time use - they deliver events instantly with lower latency. Use /webcast/fetch when WebSocket is not possible (serverless environments, HTTP-only infrastructure) or for one-off data snapshots.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
unique_id | string | No | TikTok username |
room_id | string | No | Room ID (alternative to unique_id) |
cursor | string | No | Pagination cursor from previous response |
Response
{ "status_code": 0, "data": { "room_id": "...", "alive": true, "message_count": 5, "raw_data": "base64...", "cursor": "" } }const res = await fetch('https://api.tik.tools/webcast/fetch?apiKey=YOUR_KEY', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ unique_id: 'username' })
});
const { data } = await res.json();
console.log(`Messages: ${data.message_count}`);/webcast/room_infoGet comprehensive information about a live stream room including the host's profile, stream title, viewer count, start time, and stream configuration.
Response data: Returns owner profile (nickname, uniqueId, profilePictureUrl, follower count), room metadata (title, user_count, like_count, create_time), stream URLs, cover images, and room status.
Identification: Provide either unique_id (username) or room_id. Returns an error if the user is not currently live.
Use cases: Pre-flight checks before WebSocket connection, building stream info cards/embeds, monitoring dashboards, or extracting the room ID for use with other endpoints like rankings or gift_info.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
unique_id | string | No | TikTok username |
room_id | string | No | Room ID |
Response
{ "status_code": 0, "data": { "room_id": "...", "alive": true, "title": "...", "user_count": 500, "owner": {...}, "like_count": 1234, "share_count": 56 } }const res = await fetch('https://api.tik.tools/webcast/room_info?apiKey=YOUR_KEY', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ unique_id: 'username' })
});
const { data } = await res.json();
console.log(`${data.title} - ${data.user_count} viewers`);/webcast/room_videoGet live stream video playback URLs in multiple formats and quality levels. Returns HLS (.m3u8), FLV, and direct origin URLs suitable for embedding or processing.
Stream formats: hls_pull_url for adaptive bitrate (best for web players), flv_pull_url for low-latency playback, and origin URLs for the highest quality source stream. Multiple resolutions are available (SD, HD, FULL_HD).
Use cases: Building custom live stream players, recording/archiving streams, feeding audio to transcription services (used internally by our Live Captions feature), or creating multi-stream monitoring walls.
Important: Stream URLs are time-limited and will expire. Re-fetch periodically if you need to maintain long-running playback. The hls format provides the most reliable playback across browsers and devices.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
unique_id | string | No | TikTok username |
room_id | string | No | Room ID |
Response
{ "status_code": 0, "data": { "room_id": "...", "alive": true, "stream_urls": { "origin": { "hls": "...", "flv": "..." }, "sd": {...} }, "default_quality": "origin" } }const res = await fetch('https://api.tik.tools/webcast/room_video?apiKey=YOUR_KEY', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ unique_id: 'username' })
});
const { data } = await res.json();
const hlsUrl = data.stream_urls?.origin?.hls;/webcast/bulk_live_checkCheck live status for multiple TikTok users in a single request. Returns the same data as check_alive but for up to 100 users simultaneously.
Batch limits: Free: 1 user, Pro: up to 50 users, Ultra: up to 100 users per request. Pass usernames as a JSON array in the request body.
Response data: Returns an array with each user's unique_id, room_id, alive status, title, and userCount. Users who are offline return alive: false with an empty room_id.
Use cases: Creator monitoring dashboards that track dozens of streamers, automated alert systems, multi-stream aggregators, or periodically scanning talent rosters to detect who's live.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
unique_ids | string[] | Yes | Array of TikTok usernames |
Response
{ "status_code": 0, "data": { "user1": true, "user2": false, "user3": true } }const res = await fetch('https://api.tik.tools/webcast/bulk_live_check?apiKey=YOUR_KEY', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ unique_ids: ['user1', 'user2', 'user3'] })
});
const { data } = await res.json();
Object.entries(data).forEach(([user, live]) => console.log(`${user}: ${live}`));/webcast/ws_credentialsGet pre-signed WebSocket connection credentials for establishing a direct connection to TikTok's live stream WebSocket servers. Returns everything needed to connect without using the managed proxy.
What's returned: A signed websocket_url (with X-Bogus), cookies (ttwid, session), user_agent, room_id, and cluster_region. Use these to create your own WebSocket connection directly to TikTok.
When to use: Advanced users building custom WebSocket clients who need direct TikTok connections (without using our managed proxy). Most users should use the managed WebSocket connection (wss://api.tik.tools) or the Node.js SDK instead.
Important: Direct connections count toward TikTok's per-IP WebSocket limit (~4 concurrent). If you need more, use our managed proxy which distributes connections across multiple IPs.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
unique_id | string | Yes | TikTok username |
Response
{ "status_code": 0, "data": { "ws_url": "wss://...", "cookies": "ttwid=...", "room_id": "...", "ws_host": "...", "cluster_region": "US_TTP" } }const res = await fetch('https://api.tik.tools/webcast/ws_credentials?apiKey=YOUR_KEY', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ unique_id: 'username' })
});
const { data } = await res.json();
console.log('WS URL:', data.ws_url);/webcast/sign_websocketSign a TikTok WebSocket URL with X-Bogus parameters for direct connection to TikTok's webcast-ws servers. This is the WebSocket-specific equivalent of sign_url.
How it works: Provide a room_id and the API generates a fully signed WebSocket URL targeting the correct regional TikTok WebSocket server (webcast-ws.tiktok.com, webcast-ws.us.tiktok.com, etc.).
Response: Returns signed_url (the complete wss:// URL), cookies, and user_agent. Connect using these credentials with the provided cookies in the WebSocket handshake headers.
Use case: Building custom WebSocket clients in languages without our SDK, or when you need fine-grained control over the WebSocket lifecycle. The SDK handles this internally - most users won't need this endpoint directly.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
room_id | string | Yes | TikTok room ID |
cursor | string | No | Pagination cursor |
Response
{ "status_code": 0, "data": { "signed_url": "wss://...", "x_bogus": "...", "x_gnarly": "...", "user_agent": "...", "cookies": "..." } }const res = await fetch('https://api.tik.tools/webcast/sign_websocket?apiKey=YOUR_KEY', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ room_id: '7123456789' })
});
const { data } = await res.json();
// Connect with: new WebSocket(data.signed_url)/webcast/resolve_user_idsResolve numeric TikTok user IDs to their corresponding usernames (unique_id/display_id). Accepts up to 20 user IDs per request with server-side caching for fast lookups.
When you need this: TikTok's internal APIs and WebSocket events often send numeric user IDs (like 6892636847263982593) instead of readable usernames. This endpoint converts them back to @username format.
Caching: Results are cached server-side for 24 hours. Repeated lookups for the same user IDs are instant and don't count toward rate limits.
Response data: Returns an array of { user_id, unique_id, nickname, avatar_url } objects. Users whose IDs can't be resolved (deleted accounts, banned users) are returned with unique_id: null.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
user_ids | string[] | Yes | Array of numeric user IDs (max 20) |
Response
{ "status_code": 0, "data": { "123456": { "userId": "123456", "username": "john_doe", "cached": false } } }const res = await fetch('https://api.tik.tools/webcast/resolve_user_ids?apiKey=YOUR_KEY', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ user_ids: ['107955', '6789012345'] })
});
const { data } = await res.json();
Object.values(data).forEach(u => console.log(`${u.userId} → @${u.username}`));/authentication/jwtGenerate a JWT (JSON Web Token) for secure frontend WebSocket connections. JWTs allow your client-side code to connect without exposing your API key.
Security model: Generate JWTs server-side using your API key, then pass the jwtKey to your frontend. The JWT can be scoped to specific creators (allowed_creators) and limited to a set number of concurrent connections.
Expiry: Tokens expire after expire_after seconds (default: 3600 = 1 hour). After expiry, the client must request a new token from your backend.
Use cases: Web applications where the frontend connects directly to wss://api.tik.tools?uniqueId=USERNAME&jwtKey=TOKEN. This keeps your API key on the server while letting browsers connect to the WebSocket.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
expire_after | number | No | Seconds until expiry (default: 3600) |
allowed_creators | string[] | No | Restrict to specific creators |
max_websockets | number | No | Max concurrent WS connections (default: 1) |
Response
{ "status_code": 0, "data": { "token": "eyJ..." } }const res = await fetch('https://api.tik.tools/authentication/jwt?apiKey=YOUR_KEY', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ expire_after: 3600, allowed_creators: ['streamer1'] })
});
const { data } = await res.json();
// Use data.token for WebSocket: wss://api.tik.tools?jwtKey=TOKEN/webcast/rankingsGet real-time leaderboard data for a live room - top gifters, online audience rankings, and engagement scores. Uses the "sign-and-return" pattern for authenticated data.
Data available: In-room top gifters (online_audience - ranked by diamonds sent this session), all-time gifter leaderboard (anchor_rank_list), and entrance configuration (ranking tabs, timer info, class/league badges).
Authentication: For full leaderboard data, include your TikTok session cookies via x-cookie-header. Without authentication, the endpoint returns limited ranking data.
Response pattern: Returns a signed_url that you fetch with session cookies to get TikTok's ranking data. Each rank entry includes user (nickname, avatar, follower count), score (diamonds), and rank position.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
room_id | string | No | TikTok room ID |
unique_id | string | No | TikTok username (alternative to room_id) |
Response
{ "status_code": 0, "data": { "room_id": "...", "rankings": [...] } }const res = await fetch('https://api.tik.tools/webcast/rankings?unique_id=username&apiKey=YOUR_KEY');
const { data } = await res.json();
console.log(data.rankings);/webcast/room_idResolve a TikTok username to their current live room ID. The server resolves the unique_id to a room_id on your behalf - no client-side page parsing needed.
How it works: The server checks its cache first (30-minute TTL). For Pro/Ultra/Admin tiers, resolution uses a residential proxy for reliable results. For other tiers, the server attempts direct resolution (may fail from datacenter IPs).
Freshness guarantee (Pro+): If a cached room ID is found but the user is no longer live on that room, the server automatically invalidates all cache layers and re-resolves via proxy. This ensures you always get the current room ID, even if a streamer went offline and started a new stream (which creates a new room ID). When this happens, the response includes stale_fix: true.
Response data: Returns unique_id, room_id, alive (whether the user is currently live), cached (whether the result came from server cache), and optionally stale_fix (if a stale cached room was detected and re-resolved). Use the returned room_id with other endpoints like sign_websocket, rankings, gift_info, etc.
Use cases: When you only have a username and need the room_id for other API calls. Many endpoints accept unique_id directly, but some (like sign_websocket) require the room ID. This endpoint eliminates the need for client-side TikTok page parsing. Safe to poll every 5-10 minutes - cached results are instant and re-resolution only triggers when needed.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
unique_id | string | Yes | TikTok username (without @) |
Response
{ "status_code": 0, "data": { "unique_id": "username", "room_id": "7123456789", "alive": true, "cached": false, "stale_fix": false } }const res = await fetch('https://api.tik.tools/webcast/room_id?apiKey=YOUR_KEY', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ unique_id: 'username' })
});
const { data } = await res.json();
console.log(`Room ID: ${data.room_id}, Live: ${data.alive}`);/webcast/room_coverGet the high-quality cover image (thumbnail) URL of an active live stream. Returns the same image TikTok displays on the live feed before a user taps to watch.
Image quality: Returns the highest resolution available from TikTok's CDN. Cover images are typically 720x960 or higher, served as JPEG/WebP from TikTok's image CDN.
Use cases: Building live stream galleries, social media embeds, notification cards with stream previews, or monitoring dashboards that show stream thumbnails alongside viewer counts.
Note: Cover images are set by the streamer before going live. If no custom cover is set, TikTok uses a default or auto-generated thumbnail. Returns an error if the user is not currently live.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
unique_id | string | No | TikTok username |
room_id | string | No | TikTok room ID |
Response
{ "status_code": 0, "data": { "room_id": "...", "cover_url": "https://..." } }const res = await fetch('https://api.tik.tools/webcast/room_cover?unique_id=username&apiKey=YOUR_KEY');
const { data } = await res.json();
console.log(data.cover_url);/webcast/hashtag_listGet currently trending live stream hashtags from TikTok. Returns hashtag names, live viewer counts, and associated metadata.
Response data: Each hashtag includes id, title (the hashtag text), user_count (total viewers across all streams using this hashtag), and related metadata. Results are sorted by popularity.
Use cases: Discover trending topics for content strategy, build hashtag explorers, analyze live streaming trends over time, or filter the feed by popular hashtags.
Pagination: Use the count parameter to control results per page (default: 20, max: 50). The endpoint uses TikTok's internal trending algorithm, so results update frequently as trending topics shift.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
count | number | No | Number of results (default: 20, max: 50) |
Response
{ "status_code": 0, "data": [{ "id": "...", "title": "#gaming", "user_count": 15000 }, ...] }const res = await fetch('https://api.tik.tools/webcast/hashtag_list?count=10&apiKey=YOUR_KEY');
const { data } = await res.json();
data.forEach(tag => console.log(tag.title));/webcast/gift_infoGet the complete list of gifts available in a live room, including gift names, diamond costs, animated thumbnails, and gift types. Uses the "sign-and-return" pattern.
Response data: Each gift entry includes id, name, diamond_count (cost in diamonds), icon (thumbnail URL), type (1=standard, 2=animated, etc.), and combo/repeat information.
Use cases: Build gift catalogs for your application, calculate the real-money value of gifts (1 diamond ≈ $0.005 USD), display gift icons in chat overlays, or track which gifts are available in different regions.
Note: Gift availability varies by region and room. Some gifts are event-exclusive or require specific room configurations. The response returns all currently available gifts for the specified room.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
room_id | string | No | TikTok room ID |
unique_id | string | No | TikTok username |
Response
{ "status_code": 0, "data": { "room_id": "...", "gifts": [{ "id": 1, "name": "Rose", "diamond_count": 1 }, ...] } }const res = await fetch('https://api.tik.tools/webcast/gift_info?unique_id=username&apiKey=YOUR_KEY');
const { data } = await res.json();
data.gifts.forEach(g => console.log(`${g.name}: ${g.diamond_count} diamonds`));/webcast/chatSend a chat message to an active TikTok live stream room on behalf of an authenticated user. Requires valid TikTok session cookies - the message is sent as the logged-in user.
Authentication required: You must include TikTok session cookies via the x-cookie-header header. Get your sessionid from browser DevTools → Application → Cookies → tiktok.com.
How it works: The API signs the chat request with X-Bogus, attaches your session cookies, and sends the message to TikTok's chat endpoint. The message appears in the live room as if you typed it in the TikTok app.
Rate limiting: TikTok enforces internal chat rate limits (~1 message per 2 seconds). Sending too fast may result in temporary chat mutes. The API returns status_code: 0 on success.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
room_id | string | Yes | Target room ID |
text | string | Yes | Chat message text |
Response
{ "status_code": 0, "data": { "status_code": 0 } }const res = await fetch('https://api.tik.tools/webcast/chat?apiKey=YOUR_KEY', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-cookie-header': 'sessionid=YOUR_TIKTOK_COOKIES'
},
body: JSON.stringify({ room_id: '7123456789', text: 'Hello!' })
});
console.log(await res.json());/webcast/user_earningsRetrieve TikTok LIVE earnings data for a creator including diamonds received, coin equivalent, and period breakdowns. This is an authenticated endpoint that proxies TikTok's creator earnings API.
Authentication required: You must include TikTok session cookies via x-cookie-header. Only works when the session belongs to the creator whose earnings are being requested (you can't view other people's earnings).
Response data: Returns diamonds (total received), coins (equivalent coin value), and period breakdown. Diamond-to-USD conversion: 1 diamond ≈ $0.005 USD.
Use cases: Creator dashboards showing real-time earnings, financial analytics for agency talent management, or automated earnings reports for multi-stream operations.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
unique_id | string | Yes | TikTok username |
period | string | No | Period filter: "daily" (default) |
Response
{ "status_code": 0, "data": { "diamonds": 1500, "coins": 75000, "period": "daily" } }const res = await fetch('https://api.tik.tools/webcast/user_earnings?unique_id=creator&apiKey=YOUR_KEY', {
headers: { 'x-cookie-header': 'sessionid=YOUR_TIKTOK_COOKIES' }
});
const { data } = await res.json();
console.log(`Diamonds: ${data.diamonds}`);/webcast/feedBasic+Discover currently live TikTok LIVE streams. Uses a two-step "sign-and-return" pattern - the API returns a signed URL with headers and cookies that you fetch from YOUR own IP to get the actual TikTok feed data.
Authentication: Include your TikTok session_id cookie for personalized, populated results. Without it, results are anonymous and limited (~5 rooms).
Channels: 87 = Recommended - the main "For You" infinite scroll feed, 86 = Suggested - sidebar host recommendations (shown while watching), 89 or 1111006 = Gaming, 42 = Following (requires session).
Geo-targeting: Feed results are determined by the IP address making the final TikTok fetch (Step 2), NOT by the region parameter. Since you fetch the signed URL from your own client, you'll see content relevant to your geographic location. The region param is passed to TikTok but has minimal effect on results.
Pagination: The API automatically switches to TikTok's "load more" mode when you pass a max_time cursor. Fetch the signed URL → parse the TikTok JSON response → extract data.extra.max_time → pass it as max_time in your next call. Each page returns ~6-15 rooms; keep paginating for continuous discovery. The response also includes a load_more_url template - just replace {MAX_TIME} with the cursor.
Rate limits: Pro: 100 calls/day. Ultra: 2,000 calls/day. Response includes feed_remaining and feed_limit fields.
Response data: Each room includes owner.display_id (username), owner.nickname (display name), title, user_count (viewers), owner.avatar_thumb (profile photo), id_str (room ID), and more.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
session_id | string | No | Your TikTok sessionid cookie - strongly recommended. Get from browser DevTools → Application → Cookies → tiktok.com. Without it, results are sparse. |
channel_id | string | No | Feed channel: 87 = Recommended "For You" (default), 86 = Suggested sidebar, 89 or 1111006 = Gaming, 42 = Following |
count | number | No | Rooms per page (default: 20, max: 50) |
max_time | string | No | Pagination cursor from previous TikTok response (data.extra.max_time). Omit or "0" for first page. |
region | string | No | Hint passed to TikTok (default: US). Note: actual results are geo-targeted by the IP making the final fetch, not this param. |
ttwid | string | No | TikTok ttwid visitor cookie. Server provides one if omitted. |
ms_token | string | No | TikTok msToken cookie (optional, a placeholder is used if omitted). |
Response
{
"status_code": 0,
"signed_url": "https://webcast.tiktok.com/webcast/feed/?...",
"method": "GET",
"headers": { "User-Agent": "...", "Referer": "https://www.tiktok.com/", ... },
"cookies": "ttwid=...; sessionid=...; sessionid_ss=...",
"region": "US",
"channel_id": "87",
"feed_remaining": 99,
"feed_limit": 100,
"note": "Fetch signed_url from your client/IP with these headers and cookies."
}
// After fetching signed_url, TikTok returns:
{
"status_code": 0,
"data": [{
"data": {
"id_str": "7123456789",
"title": "🔴 Live Now!",
"user_count": 1250,
"owner": {
"display_id": "streamer_name",
"nickname": "Display Name",
"avatar_thumb": { "url_list": ["https://..."] }
}
}
}, ...],
"extra": { "max_time": "1773128824428" }
}// Step 1: Get signed URL with session for populated results
const res = await fetch(
'https://api.tik.tools/webcast/feed?' + new URLSearchParams({
apiKey: 'YOUR_KEY',
session_id: 'YOUR_TIKTOK_SESSION_ID', // from browser cookies
region: 'US',
channel_id: '87', // 87=recommended, 86=suggested, 1111006=gaming
count: '20',
})
);
const { signed_url, headers, cookies, feed_remaining, feed_limit } = await res.json();
console.log(`Quota: ${feed_remaining}/${feed_limit} calls remaining`);
// Step 2: Fetch TikTok data from YOUR IP
const tikRes = await fetch(signed_url, {
headers: { ...headers, Cookie: cookies }
});
const feed = await tikRes.json();
// Step 3: Parse live rooms
for (const entry of feed.data) {
const room = entry.data;
console.log(`🔴 @${room.owner.display_id} (${room.owner.nickname})`);
console.log(` "${room.title}" - ${room.user_count} viewers`);
}
// Step 4: Load more (pagination)
const nextCursor = feed.extra?.max_time;
if (nextCursor) {
const page2 = await fetch(
'https://api.tik.tools/webcast/feed?' + new URLSearchParams({
apiKey: 'YOUR_KEY', session_id: 'YOUR_SESSION_ID',
region: 'US', max_time: nextCursor, count: '20',
})
);
// ... repeat Step 2-3
}/webcast/live_analytics/video_listList historical live stream recordings for a TikTok account. Returns past streams with metadata including title, duration, viewer count, and stream date. Requires authenticated TikTok session cookies.
Authentication required: Include session cookies via x-cookie-header. Only accessible for the account that owns the session - you cannot view another creator's video history.
Response data: Each entry includes video_id (stream identifier), title, duration (seconds), viewer_count (peak viewers), create_time (start timestamp), and engagement metrics.
Use cases: Building creator analytics dashboards, tracking streaming performance over time, generating stream history reports, or identifying a specific video_id for use with the video_detail endpoint.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
unique_id | string | Yes | TikTok username |
count | number | No | Number of results (default: 20) |
Response
{ "status_code": 0, "data": [{ "video_id": "...", "title": "...", "duration": 3600, "viewer_count": 1200 }, ...] }const res = await fetch('https://api.tik.tools/webcast/live_analytics/video_list?unique_id=creator&apiKey=YOUR_KEY', {
headers: { 'x-cookie-header': 'sessionid=YOUR_TIKTOK_COOKIES' }
});
const { data } = await res.json();
data.forEach(v => console.log(`${v.title}: ${v.viewer_count} viewers`));/webcast/live_analytics/video_detailGet granular analytics for a specific past live stream session. Returns detailed performance metrics including total views, peak concurrent viewers, gifts received, new followers gained, and engagement rates. Requires authenticated TikTok session cookies.
Authentication required: Include session cookies via x-cookie-header. Access is restricted to the account's own streams.
Response data: Includes views (total), duration (seconds), peak_viewers, gifts (total diamond value), new_followers, likes, comments, and period-specific breakdowns. The video_id comes from the video_list endpoint.
Use cases: Post-stream analytics reports, A/B testing different stream formats, tracking creator growth metrics, or building agency reporting dashboards that aggregate performance across multiple creators.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
video_id | string | Yes | Video/stream ID |
Response
{ "status_code": 0, "data": { "video_id": "...", "duration": 3600, "views": 12000, "gifts": 350, "peak_viewers": 800 } }const res = await fetch('https://api.tik.tools/webcast/live_analytics/video_detail?video_id=VID123&apiKey=YOUR_KEY', {
headers: { 'x-cookie-header': 'sessionid=YOUR_TIKTOK_COOKIES' }
});
const { data } = await res.json();
console.log(`Duration: ${data.duration}s, Peak: ${data.peak_viewers}`);/webcast/live_analytics/user_interactionsGet the real-time online audience roster for a live room, ranked by gift score (diamonds sent during this session). Returns each viewer's rank, score, and full profile. Requires authenticated TikTok session cookies.
How it works: Uses the "sign-and-return" pattern. The API returns a signed_url that you fetch with your session cookies to get TikTok's audience data. The anchor_id (host's user ID) is auto-resolved from the room_id.
Response data: Each entry includes rank (1-based position), score (diamonds sent this session), and user profile with nickname, display_id, id_str, avatar_thumb, and follow_info (follower/following counts).
Use cases: Identifying top supporters in real-time, building live leaderboard overlays, tracking viewer engagement and spending patterns, or providing viewer analytics for agency clients.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
room_id | string | Yes | Room ID of the stream |
user_id | string | No | Filter to specific user ID |
count | number | No | Number of results (default: 50) |
Response
{ "status_code": 0, "signed_url": "https://...", "anchor_id": "...", "note": "Fetch signed_url with your session cookies" }
// After fetching signed_url with session cookies, TikTok returns:
{ "data": { "ranks": [{ "rank": 1, "score": 192, "user": { "nickname": "...", "display_id": "...", "id_str": "..." } }], "total": 49 } }const res = await fetch('https://api.tik.tools/webcast/live_analytics/user_interactions?room_id=7123456789&apiKey=YOUR_KEY', {
headers: { 'x-cookie-header': 'sessionid=YOUR_TIKTOK_COOKIES' }
});
const { signed_url, headers, anchor_id } = await res.json();
// Fetch from your IP with your session
const tikRes = await fetch(signed_url, {
headers: { ...headers, Cookie: `sessionid=YOUR_SESSIONID; ${headers.Cookie || ''}` }
});
const { data } = await tikRes.json();
data.ranks.forEach(r => console.log(`#${r.rank} ${r.user.nickname}: ${r.score} pts`));/webcast/moderation/mutesManage muted users in a live room. GET to list, PUT to mute, DELETE to unmute. Requires TikTok session.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
room_id | string | Yes | Target room ID |
user_id | string | No | User ID to mute/unmute (PUT/DELETE) |
duration | number | No | Mute duration in seconds (PUT, 0 = permanent) |
Response
{ "status_code": 0, "data": { "muted_users": [{ "user_id": "...", "duration": 300 }] } }// List muted users
const res = await fetch('https://api.tik.tools/webcast/moderation/mutes?room_id=7123456789&apiKey=YOUR_KEY', {
headers: { 'x-cookie-header': 'sessionid=YOUR_TIKTOK_COOKIES' }
});
console.log(await res.json());/webcast/moderation/bansManage banned users in a live room. GET to list, PUT to ban, DELETE to unban. Requires TikTok session.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
room_id | string | Yes | Target room ID |
user_id | string | No | User ID to ban/unban (PUT/DELETE) |
Response
{ "status_code": 0, "data": { "banned_users": [{ "user_id": "...", "username": "..." }] } }// List banned users
const res = await fetch('https://api.tik.tools/webcast/moderation/bans?room_id=7123456789&apiKey=YOUR_KEY', {
headers: { 'x-cookie-header': 'sessionid=YOUR_TIKTOK_COOKIES' }
});
console.log(await res.json());/api/live/connectBundled endpoint - fetches both a scoped JWT token and stream video URLs in a single request. Ideal for frontend apps that need to open a WebSocket and play video simultaneously. Rate limited to 10 req/min.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
uniqueId | string | Yes | TikTok username (without @) |
Response
{ "token": "eyJ...", "wsUrl": "wss://api.tik.tools", "uniqueId": "streamer", "stream": { "room_id": "...", "alive": true, "stream_urls": { "origin": { "hls": "...", "flv": "..." } }, "default_quality": "origin" } }const res = await fetch('https://tik.tools/api/live/connect?uniqueId=username');
const data = await res.json();
// data.token - short-lived JWT for WebSocket auth
// data.wsUrl - WebSocket server URL
// data.stream - video URLs (HLS/FLV)
const ws = new WebSocket(`${data.wsUrl}?uniqueId=username&jwtKey=${data.token}`);/captcha/solve/puzzleBasic+Solve a TikTok puzzle (slider) CAPTCHA. Send base64-encoded background and piece images. Returns the slide X position.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
puzzle | string | Yes | Base64-encoded background image (PNG/JPEG) |
piece | string | Yes | Base64-encoded puzzle piece image (PNG/JPEG) |
Response
{ "status_code": 0, "data": { "slide_x_proportion": 0.42, "slide_x_px": 168, "confidence": 0.95, "solve_time_ms": 12 } }const fs = require('fs');
const bg = fs.readFileSync('captcha-bg.png').toString('base64');
const piece = fs.readFileSync('captcha-piece.png').toString('base64');
const res = await fetch('https://api.tik.tools/captcha/solve/puzzle?apiKey=YOUR_KEY', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ puzzle: bg, piece })
});
const { data } = await res.json();
console.log(`Slide to X=${data.slide_x_px}px`);/captcha/solve/rotateBasic+Solve a TikTok rotate (whirl) CAPTCHA. Send base64-encoded outer ring and inner images. Returns the rotation angle.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
outer | string | Yes | Base64-encoded outer ring image (PNG/JPEG) |
inner | string | Yes | Base64-encoded inner rotated image (PNG/JPEG) |
Response
{ "status_code": 0, "data": { "angle": 127, "confidence": 0.82, "solve_time_ms": 45 } }const fs = require('fs');
const outer = fs.readFileSync('outer.png').toString('base64');
const inner = fs.readFileSync('inner.png').toString('base64');
const res = await fetch('https://api.tik.tools/captcha/solve/rotate?apiKey=YOUR_KEY', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ outer, inner })
});
const { data } = await res.json();
console.log(`Rotate ${data.angle}°`);/captcha/solve/shapesBasic+Solve a TikTok shapes (3D matching) CAPTCHA. Send a base64-encoded image with a grid of shapes. Returns the two matching shape coordinates.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
image | string | Yes | Base64-encoded CAPTCHA image with shape grid (PNG/JPEG) |
Response
{ "status_code": 0, "data": { "point1": { "x": 85, "y": 120 }, "point2": { "x": 245, "y": 120 }, "confidence": 0.78, "solve_time_ms": 30 } }const fs = require('fs');
const image = fs.readFileSync('shapes.png').toString('base64');
const res = await fetch('https://api.tik.tools/captcha/solve/shapes?apiKey=YOUR_KEY', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ image })
});
const { data } = await res.json();
console.log(`Match: (${data.point1.x},${data.point1.y}) ↔ (${data.point2.x},${data.point2.y})`);/webcast/ranklist/regionalBasic+Get regional LIVE leaderboard rankings - daily, hourly, popular, or league. Returns a signed URL that the client fetches with their own TikTok session cookie. Requires Pro or Ultra tier.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
unique_id | string | No | TikTok username (auto-resolves room_id and anchor_id) |
room_id | string | No | Room ID (alternative to unique_id) |
anchor_id | string | No | Anchor/owner user ID. Auto-resolved if unique_id is used. |
rank_type | string | No | Ranking period: "1" = Hourly, "8" = Daily (default), "15" = Popular LIVE, "16" = League |
type | string | No | "list" (default) for rankings, "entrance" for available tabs |
gap_interval | string | No | Gap interval filter (default: "0") |
Response
{
"status_code": 0,
"action": "fetch_signed_url",
"signed_url": "https://webcast.tiktok.com/webcast/ranklist/list/v2/?...",
"method": "POST",
"headers": { "User-Agent": "...", "Content-Type": "application/x-www-form-urlencoded" },
"body": "room_id=123&rank_type=8&anchor_id=456&gap_interval=0",
"cookies": "ttwid=...",
"note": "POST this URL with your sessionid cookie from your browser/IP."
}// Step 1: Get signed URL from the API
const res = await fetch('https://api.tik.tools/webcast/ranklist/regional?apiKey=YOUR_KEY', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
room_id: '7607695933891218198',
anchor_id: '7444599004337652758',
rank_type: '8' // Daily
})
});
const signData = await res.json();
// Step 2: Fetch from YOUR IP with YOUR session cookie
const tikResp = await fetch(signData.signed_url, {
method: signData.method,
headers: { ...signData.headers, Cookie: `sessionid=YOUR_SESSIONID; ${signData.cookies}` },
body: signData.body
});
const { data } = await tikResp.json();
data.rank_view.ranks.forEach((r, i) =>
console.log(`${i+1}. ${r.user.nickname} - ${r.score} pts`)
);WebSocket Events
All event types sent over the WebSocket connection.
Fields
| Name | Type | Description |
|---|---|---|
user | WebcastUser | The user who sent the message |
comment | string | The chat message text |
starred | object | undefined | Present only for starred (paid highlighted) messages. Contains { claps: number, score: number } |
Example Payload
{ "event": "chat", "data": { "type": "chat", "user": { "id": "123", "nickname": "John", "uniqueId": "john123" }, "comment": "Hello!", "starred": { "claps": 3, "score": 1200 } } }Fields
| Name | Type | Description |
|---|---|---|
user | WebcastUser | The user who liked |
likeCount | number | Number of likes in this event |
totalLikes | number | Total likes on the stream |
Example Payload
{ "event": "like", "data": { "type": "like", "user": { "id": "123", "uniqueId": "fan99" }, "likeCount": 15, "totalLikes": 4200 } }Fields
| Name | Type | Description |
|---|---|---|
user | WebcastUser | The gifter |
giftId | number | Gift identifier |
giftName | string | Display name of the gift |
repeatCount | number | Number of gifts in combo |
diamondCount | number | Diamond value per gift |
repeatEnd | boolean | Whether the gift combo has ended |
Example Payload
{ "event": "gift", "data": { "type": "gift", "user": { "uniqueId": "generous" }, "giftName": "Rose", "repeatCount": 5, "diamondCount": 1, "repeatEnd": true } }Fields
| Name | Type | Description |
|---|---|---|
user | WebcastUser | The user who joined |
action | string | Join action type |
Example Payload
{ "event": "member", "data": { "type": "member", "user": { "uniqueId": "viewer1" }, "action": "join" } }Fields
| Name | Type | Description |
|---|---|---|
totalViewers | number | Total unique viewers |
viewerCount | number | Current concurrent viewers |
Example Payload
{ "event": "roomUserSeq", "data": { "type": "roomUserSeq", "totalViewers": 15000, "viewerCount": 342 } }Fields
| Name | Type | Description |
|---|---|---|
user | WebcastUser | The subscriber |
subMonth | number | Subscription month count |
Example Payload
{ "event": "subscribe", "data": { "type": "subscribe", "user": { "uniqueId": "subscriber1" }, "subMonth": 3 } }Fields
| Name | Type | Description |
|---|---|---|
user | WebcastUser | The user |
emoteId | string | Emote identifier |
emoteUrl | string | URL of the emote image |
Example Payload
{ "event": "emoteChat", "data": { "type": "emoteChat", "user": { "uniqueId": "emojifan" }, "emoteId": "123", "emoteUrl": "https://..." } }Fields
| Name | Type | Description |
|---|---|---|
battleId | string | Battle identifier |
status | number | Battle status code |
battleDuration | number | Duration in seconds |
teams | BattleTeam[] | Array of competing teams |
Example Payload
{ "event": "battle", "data": { "type": "battle", "battleId": "456", "status": 1, "teams": [{ "hostUserId": "1", "score": 100 }] } }Fields
| Name | Type | Description |
|---|---|---|
user | WebcastUser | The user who asked |
questionText | string | The question text |
Example Payload
{ "event": "question", "data": { "type": "question", "user": { "uniqueId": "curious" }, "questionText": "What game is this?" } }Fields
| Name | Type | Description |
|---|---|---|
action | number | Control action code (3 = stream ended) |
Example Payload
{ "event": "control", "data": { "type": "control", "action": 3 } }Fields
| Name | Type | Description |
|---|---|---|
envelopeId | string | Envelope identifier |
diamondCount | number | Diamond value |
Example Payload
{ "event": "envelope", "data": { "type": "envelope", "envelopeId": "789", "diamondCount": 50 } }Fields
| Name | Type | Description |
|---|---|---|
user | WebcastUser | The user who wrote the pinned message |
comment | string | The pinned comment text |
action | number | Pin action: 1 = pin, 2 = unpin |
durationSeconds | number | How long the pin lasts (e.g. 60) |
pinnedAt | number | Timestamp when pinned (ms) |
originalMsgType | string | Original message type (e.g. "WebcastChatMessage") |
originalMsgId | string | ID of the original chat message |
operatorUserId | string | User ID of who pinned the message |
Example Payload
{ "event": "roomPin", "data": { "type": "roomPin", "user": { "uniqueId": "viewer1", "nickname": "Viewer" }, "comment": "Great stream!", "action": 1, "durationSeconds": 60, "pinnedAt": 1773564373063, "originalMsgType": "WebcastChatMessage", "originalMsgId": "7617400904630995734", "operatorUserId": "7444599004337652758" } }Rate Limits
Rate limits are applied per API key on a 1-minute sliding window.
| Tier | API Req/min | WS Conns | WS Duration | WS Connects | Bulk Check |
|---|---|---|---|---|---|
| Sandbox | 5 | 1 | 5 min | 5/day | 1 |
| Basic | 60 | 3 | 8 hours | 150/day | 10 |
| Pro | Unlimited | 50 | 8 hours | Unlimited | 50 |
| Ultra | 300 | 500 | 8 hours | Unlimited | 500 |
Rate limit headers are included in every response:
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 28
X-RateLimit-Reset: 1234567890⚠️ WebSocket Connection Limit - 4 per IP
TikTok enforces a limit of approximately 4 concurrent WebSocket connections per IP address. Exceeding this may result in rate limiting or temporary IP bans by TikTok's infrastructure.
For Pro and Ultra users building multi-stream applications (monitoring 5+ streams simultaneously): ensure each client connects from their own IP address, or use proxy rotation to distribute connections across multiple IPs. Our API allows up to {50/500} connections per API key, but the connections should be spread across different source IPs.
WebSocket Close Codes
Custom close codes used when the WebSocket connection is terminated.
| Code | Name | Description |
|---|---|---|
1000 | NORMAL | Normal closure |
4005 | STREAM_END | The live stream ended |
4006 | NO_MESSAGES_TIMEOUT | No messages received for 60 seconds |
4400 | INVALID_OPTIONS | Missing or invalid connection parameters |
4401 | INVALID_AUTH | Invalid API key or JWT |
4403 | NO_PERMISSION | JWT does not allow access to this creator |
4404 | NOT_LIVE | The user is not currently live |
4429 | TOO_MANY_CONNECTIONS | Exceeded WebSocket connection limit |
4500 | TIKTOK_CLOSED | Upstream TikTok connection closed |
4555 | MAX_LIFETIME | Connection exceeded 8-hour max lifetime |
4556 | WEBCAST_FETCH_ERROR | Failed to fetch webcast data |
Live Captions
Real-time speech-to-text transcription for TikTok LIVE streams. AI-powered with speaker diarization, multi-language support, and sub-second latency.
Connection
wss://api.tik.tools/captions?uniqueId=USERNAME&apiKey=YOUR_KEY&language=en&translate=en&diarization=true&max_duration_minutes=60Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
uniqueId | string | Yes | TikTok username to caption |
apiKey | string | Yes | Your API key |
language | string | No | Source language hint (empty = auto-detect) |
translate | string | No | Target translation language code. Only ONE language per session (multi-translate rejected) |
diarization | boolean | No | Enable speaker identification (default: true) |
max_duration_minutes | number | No | Auto-disconnect after N minutes (default: 60, max: 300). Forces reconnect to prevent runaway sessions. |
Events
| Type | Fields | Description |
|---|---|---|
caption | text, speaker, isFinal, language | Real-time caption text (partial and final) |
translation | text, speaker, targetLanguage | Translated caption text |
status | status, message | Session status updates (connecting, transcribing, ended) |
credits | total, used, remaining | Credit balance update (sent every 30s) |
credits_low | remaining, percentage | Low credit warning (≤20% remaining) |
error | code, message | Error event |
Example Payload
{ "type": "caption", "text": "Hello everyone, welcome to my stream!", "speaker": "Speaker 1", "isFinal": true, "language": "en" }Code Examples
import WebSocket from 'ws';
const ws = new WebSocket(
'wss://api.tik.tools/captions?uniqueId=streamer&apiKey=YOUR_KEY&translate=en&max_duration_minutes=120'
);
ws.on('message', (data) => {
const msg = JSON.parse(data.toString());
switch (msg.type) {
case 'caption':
const prefix = msg.speaker ? `[${msg.speaker}] ` : '';
console.log(`${prefix}${msg.text}${msg.isFinal ? ' ✓' : '...'}`);
break;
case 'translation':
console.log(` → ${msg.text}`);
break;
case 'credits':
console.log(`Credits: ${msg.remaining}/${msg.total} min remaining`);
break;
case 'status':
console.log(`Status: ${msg.status}`);
break;
}
});SDK Usage
import { TikTokCaptions } from '@tiktool/live';
const captions = new TikTokCaptions({
apiKey: process.env.TIKTOOL_API_KEY,
uniqueId: 'streamer_name',
translate: 'en',
diarization: true,
});
captions.on('caption', (event) => {
console.log(`[${event.speaker}] ${event.text}`);
});
captions.on('translation', (event) => {
console.log(` → ${event.text}`);
});
captions.on('credits', (event) => {
console.log(`${event.remaining}/${event.total} min remaining`);
});
captions.on('credits_low', (event) => {
console.warn(`Low credits! ${event.remaining} min left`);
});
await captions.start();
// captions.stop() to endPricing - Credit Top-Ups
1 Credit = 1 minute of audio transcribed/translated into ONE language. Translation to a second language requires a separate session and burns separate credits.
| Package | Credits | Price | Per Credit |
|---|---|---|---|
| Starter | 1,000 | $10 | $0.010 |
| Creator ⭐ | 5,000 | $35 | $0.007 |
| Agency | 20,000 | $100 | $0.005 |
Caption credits are pay-as-you-go add-ons: Starter (1,000 credits / $10), Creator (5,000 credits / $35), Agency (20,000 credits / $100). Requires Basic tier or higher.
See captions in action on a real TikTok LIVE stream at tik.tools/captions
Unreal Engine Plugin
Native Unreal Engine plugin with full Blueprint and C++ support. Access TikTok LIVE data directly inside your UE project - no HTTP calls, no external processes.
Installation
YourProject/
├── Plugins/
│ └── TikToolLive/
│ ├── TikToolLive.uplugin
│ └── Source/
│ └── TikToolLive/
│ ├── Public/
│ │ ├── TikToolLiveSubsystem.h
│ │ ├── TikToolSettings.h
│ │ └── TikToolTypes.h
│ └── Private/
│ └── TikToolLiveSubsystem.cpp- Copy the
TikToolLivefolder into your project'sPlugins/directory - Regenerate project files (right-click
.uproject→ Generate Visual Studio project files) - Open the editor → Edit → Plugins → verify TikToolLive is enabled
Project Settings
Configure via Edit → Project Settings → Plugins → TikTool Live:
| Setting | Type | Default | Description |
|---|---|---|---|
ApiKey | FString | "" | Your TikTool API key |
DefaultUniqueId | FString | "" | TikTok username (without @) |
bAutoConnect | bool | false | Connect automatically on game start |
bAutoReconnect | bool | true | Reconnect on disconnection |
MaxReconnectAttempts | int32 | 5 | Max reconnect retries |
HeartbeatInterval | float | 30.0 | Seconds between heartbeats |
bDebugLogging | bool | false | Enable verbose log output |
Blueprint Quick Start
Zero C++ required. Three steps to receive live events in any Blueprint:
- Search for "Get TikTool Live Subsystem" from any Blueprint node graph
- Call Connect (or enable Auto Connect in Project Settings)
- Bind to events: drag from the subsystem → Assign OnChatEvent, Assign OnGiftEvent, etc.
Connection API
| Function | Returns | Description |
|---|---|---|
Connect(UniqueId) | void | Connect to a TikTok LIVE stream. Uses DefaultUniqueId if empty. |
Disconnect() | void | Close the WebSocket connection |
IsConnected() | bool | Check if currently connected |
GetRoomId() | FString | Get the current room ID |
GetViewerCount() | int32 | Current concurrent viewer count |
GetEventCount() | int32 | Total events received this session |
Events
All events are BlueprintAssignable multicast delegates. Bind in Blueprint (Assign node) or C++ (AddDynamic):
| Delegate | Payload | Description |
|---|---|---|
OnChatEvent | FTikToolChatEvent | Chat message received |
OnGiftEvent | FTikToolGiftEvent | Gift sent by viewer |
OnLikeEvent | FTikToolLikeEvent | Likes received |
OnMemberEvent | FTikToolMemberEvent | Viewer joined stream |
OnFollowEvent | FTikToolFollowEvent | New follower |
OnShareEvent | FTikToolShareEvent | Stream shared |
OnSubscribeEvent | FTikToolSubscribeEvent | Subscription |
OnViewerCountUpdate | FTikToolViewerEvent | Viewer count changed |
OnBattleUpdate | FTikToolBattleEvent | Battle status update |
OnEmoteChatEvent | FTikToolEmoteChatEvent | Emote in chat |
OnQuestionEvent | FTikToolQuestionEvent | Q&A question |
OnEnvelopeEvent | FTikToolEnvelopeEvent | Treasure box / envelope |
OnStreamEnd | - | Stream ended |
OnConnected | - | Successfully connected |
OnDisconnected | - | Connection lost |
C++ Example
#include "TikToolLiveSubsystem.h"
void AMyChatActor::BeginPlay()
{
Super::BeginPlay();
auto* TikTool = GetGameInstance()->GetSubsystem<UTikToolLiveSubsystem>();
if (!TikTool) return;
// Bind to chat events
TikTool->OnChatEvent.AddDynamic(this, &AMyChatActor::HandleChat);
TikTool->OnGiftEvent.AddDynamic(this, &AMyChatActor::HandleGift);
// Connect (uses DefaultUniqueId from Project Settings)
TikTool->Connect();
}
void AMyChatActor::HandleChat(const FTikToolChatEvent& Event)
{
UE_LOG(LogTemp, Log, TEXT("%s: %s"),
*Event.User.Nickname, *Event.Comment);
}
void AMyChatActor::HandleGift(const FTikToolGiftEvent& Event)
{
UE_LOG(LogTemp, Log, TEXT("%s sent %s x%d"),
*Event.User.Nickname, *Event.GiftName, Event.RepeatCount);
}