Package Exports
- @tiktool/live
Readme
@tiktool/live
TikTok LIVE API SDK for Node.js, Bun, Deno, and Cloudflare Workers
Real-time TikTok LIVE events — chat messages, gifts, follows, likes, battles, viewer counts, and 18+ event types — streamed directly to your application via WebSocket.
Quick Start · Events · API Utilities · REST API · Pricing · Get API Key
Why @tiktool/live?
- Direct WebSocket connection to TikTok from your own IP — events are never proxied through third-party servers
- Zero dependencies — built-in protobuf parser, universal WebSocket resolution, gzip decompression
- Universal runtime support — Node.js 18+, Bun, Deno, Cloudflare Workers, Durable Objects, and browsers
- Full TypeScript — complete type definitions for all 18+ event types with IDE autocompletion
- Battle-tested — production-ready with auto-reconnect, heartbeat management, and error recovery
- Generous free tier — 2,500 requests/day, 1 WebSocket connection, no credit card required
⚡ Quick Start
npm install @tiktool/liveGet your free API key at tik.tools — no credit card required. The free tier includes WebSocket signing, live status checks, and 2,500 requests/day.
import { TikTokLive } from '@tiktool/live';
const live = new TikTokLive({
uniqueId: 'username',
apiKey: 'YOUR_API_KEY',
});
live.on('chat', e => console.log(`${e.user.uniqueId}: ${e.comment}`));
live.on('gift', e => console.log(`${e.user.uniqueId} sent ${e.giftName} (${e.diamondCount}💎)`));
live.on('member', e => console.log(`${e.user.uniqueId} joined the stream`));
live.on('roomUserSeq', e => console.log(`Viewers: ${e.viewerCount}`));
await live.connect();Architecture
Your App api.tik.tools TikTok
+-----------+ +--------------+ +--------------+
| |-- sign_url --> Signs URL | | |
| SDK |<-- X-Bogus --| (1 req) | | WebSocket |
| | | | | Server |
| |------------- Connect directly --------->| |
| |<------------ Live events (protobuf) <---| |
+-----------+ +--------------+ +--------------+
^ Only signing ^ Direct from
touches our API YOUR IPThe SDK calls the sign server once per connection to generate a cryptographic signature, then connects directly to TikTok's WebSocket server from your IP address. All event data flows directly between your app and TikTok — never through our infrastructure.
📡 Events
live.on('chat', (event) => {
event.user.uniqueId; // string
event.user.nickname; // string
event.comment; // string
});
live.on('event', (event) => {
console.log(event.type, event);
});Event Reference
| Event | Type | Description | Key Fields |
|---|---|---|---|
chat |
ChatEvent |
Chat message received | user, comment, emotes |
member |
MemberEvent |
User joined the stream | user, action |
like |
LikeEvent |
User liked the stream | user, likeCount, totalLikes |
gift |
GiftEvent |
Gift sent to streamer | user, giftName, diamondCount, repeatCount, combo |
social |
SocialEvent |
Follow or share event | user, action ('follow' / 'share') |
roomUserSeq |
RoomUserSeqEvent |
Viewer count update | viewerCount, totalViewers |
battle |
BattleEvent |
Battle started/ended | battleId, status, teams |
battleArmies |
BattleArmiesEvent |
Battle scores update | battleId, teams (with scores) |
subscribe |
SubscribeEvent |
New subscriber | user, subMonth |
emoteChat |
EmoteChatEvent |
Emote message | user, emoteId, emoteUrl |
envelope |
EnvelopeEvent |
Treasure chest | envelopeId, diamondCount |
question |
QuestionEvent |
Q&A question | user, questionText |
control |
ControlEvent |
Stream control signal | action (3 = stream ended) |
room |
RoomEvent |
Room status change | status |
liveIntro |
LiveIntroEvent |
Stream intro displayed | roomId, title |
rankUpdate |
RankUpdateEvent |
Ranking update | rankType, rankList |
linkMic |
LinkMicEvent |
Link Mic event | action, users |
unknown |
UnknownEvent |
Unrecognized message | method |
Connection Events
| Event | Callback | Description |
|---|---|---|
connected |
() => void |
Successfully connected to the livestream |
disconnected |
(code, reason) => void |
Disconnected from the livestream |
roomInfo |
(info: RoomInfo) => void |
Room metadata received |
error |
(error: Error) => void |
Connection or parsing error |
🛠️ API Utilities
The SDK includes server-side utilities for calling TikTool REST endpoints. These handle the sign-and-return flow automatically — resolving usernames to room IDs, fetching signed URLs, and returning actual TikTok data.
callApi(options) — High-Level Helper
The easiest way to call any TikTool endpoint. Handles the full flow automatically:
import { callApi } from '@tiktool/live';
// Get stream video URLs for a user
const streamData = await callApi({
apiKey: 'YOUR_API_KEY',
endpoint: '/webcast/room_video',
uniqueId: 'streamer_name',
});
// Get room info
const roomInfo = await callApi({
apiKey: 'YOUR_API_KEY',
endpoint: '/webcast/room_info',
uniqueId: 'streamer_name',
});
// Use a custom server URL
const data = await callApi({
serverUrl: 'https://your-server.com',
apiKey: 'YOUR_API_KEY',
endpoint: '/webcast/rankings',
uniqueId: 'streamer_name',
method: 'GET',
});CallApiOptions
| Option | Type | Default | Description |
|---|---|---|---|
apiKey |
string |
— | API key from tik.tools |
endpoint |
string |
— | API path (e.g. '/webcast/room_video') |
uniqueId |
string |
— | TikTok username to resolve |
serverUrl |
string |
https://api.tik.tools |
Custom API server URL |
method |
'GET' | 'POST' |
'POST' |
HTTP method |
extraBody |
object |
— | Additional POST body fields |
resolveRoomId(uniqueId) — Username to Room ID
Scrapes a TikTok live page to extract the room_id. Results are cached for 5 minutes. Returns null if the user is not live.
import { resolveRoomId } from '@tiktool/live';
const roomId = await resolveRoomId('streamer_name');
if (roomId) {
console.log(`Room ID: ${roomId}`);
}resolveLivePage(uniqueId) — Full Live Page Resolution
Returns all live page metadata including the room ID, session cookie (ttwid), and cluster region. Used internally by TikTokLive.connect(). Returns null if the user is not live.
import { resolveLivePage } from '@tiktool/live';
const info = await resolveLivePage('streamer_name');
if (info) {
console.log(`Room: ${info.roomId}, Region: ${info.clusterRegion}`);
}LivePageInfo
| Field | Type | Description |
|---|---|---|
roomId |
string |
Active room ID for the livestream |
ttwid |
string |
Session cookie for WebSocket auth |
clusterRegion |
string |
Server cluster region (e.g. 'us', 'eu') |
fetchSignedUrl(response) — Execute Signed URL Response
Takes a sign-and-return API response and fetches the actual TikTok data from the signed URL.
import { fetchSignedUrl } from '@tiktool/live';
// After calling an API endpoint that returns action: 'fetch_signed_url'
const tikTokData = await fetchSignedUrl(apiResponse);🧩 CAPTCHA Solver (Ultra)
Solve TikTok CAPTCHAs programmatically. Requires an Ultra tier API key.
solvePuzzle(apiKey, puzzleB64, pieceB64, serverUrl?) — Puzzle Slider
import { solvePuzzle } from '@tiktool/live';
const result = await solvePuzzle('YOUR_API_KEY', backgroundBase64, pieceBase64);
console.log(`Slide to X=${result.slideX}px (confidence: ${result.confidence})`);| Field | Type | Description |
|---|---|---|
slideX |
number |
Pixel X position for the slider |
confidence |
number |
Confidence score (0.0–1.0) |
solveTimeMs |
number |
Time taken to solve in ms |
solveRotate(apiKey, outerB64, innerB64, serverUrl?) — Rotate Whirl
import { solveRotate } from '@tiktool/live';
const result = await solveRotate('YOUR_API_KEY', outerBase64, innerBase64);
console.log(`Rotate ${result.angle}° (confidence: ${result.confidence})`);| Field | Type | Description |
|---|---|---|
angle |
number |
Rotation angle in degrees (0–360) |
confidence |
number |
Confidence score (0.0–1.0) |
solveTimeMs |
number |
Time taken to solve in ms |
solveShapes(apiKey, imageB64, serverUrl?) — 3D Shape Matching
import { solveShapes } from '@tiktool/live';
const result = await solveShapes('YOUR_API_KEY', imageBase64);
console.log(`Match: (${result.point1.x},${result.point1.y}) ↔ (${result.point2.x},${result.point2.y})`);| Field | Type | Description |
|---|---|---|
point1 |
{x, y} |
First matching shape center |
point2 |
{x, y} |
Second matching shape center |
confidence |
number |
Confidence score (0.0–1.0) |
solveTimeMs |
number |
Time taken to solve in ms |
🌐 REST API Endpoints
The sign server at api.tik.tools exposes a full REST API alongside WebSocket signing. Endpoints use a sign-and-return pattern — when called with a unique_id, they return instructions for resolving the room ID; when called with a room_id, they return a signed URL to fetch data directly from TikTok. Use callApi() to handle this automatically.
| Endpoint | Description |
|---|---|
POST /webcast/sign_url |
Generate X-Bogus signed URL |
GET /webcast/check_alive |
Check if a user is currently live |
GET /webcast/room_info |
Detailed room metadata (title, viewers, streamer info) |
GET /webcast/room_id |
Resolve username to room ID |
GET /webcast/room_cover |
Get stream cover image URL |
GET /webcast/room_video |
Get HLS/FLV stream URLs |
GET /webcast/rankings |
In-stream contribution rankings |
GET /webcast/gift_info |
Available gifts and diamond values |
GET /webcast/hashtag_list |
Trending live hashtags |
GET /webcast/bulk_live_check |
Check multiple users' live status in one request |
POST /webcast/fetch |
Fetch protobuf messages via HTTP polling |
GET /webcast/ws_credentials |
Get WebSocket connection credentials |
POST /webcast/sign_websocket |
Sign a WebSocket URL |
GET /webcast/resolve_user_ids |
Resolve display names to user IDs |
POST /webcast/chat |
Send chat messages (requires session cookies) |
GET /webcast/user_earnings |
Streamer earnings data (requires session cookies) |
GET /webcast/feed |
Live feed discovery |
GET /webcast/live_analytics/* |
Video list, video detail, user interactions |
GET /webcast/moderation/* |
Mute and ban management |
POST /authentication/jwt |
Generate scoped JWT for frontend WebSocket access |
POST /captcha/solve/puzzle |
Solve puzzle slider CAPTCHA (Pro+) |
POST /captcha/solve/rotate |
Solve rotate whirl CAPTCHA (Pro+) |
POST /captcha/solve/shapes |
Solve 3D shape matching CAPTCHA (Pro+) |
Full API documentation: tik.tools/docs
💰 Pricing
All plans include signatures and full WebSocket support. Higher tiers unlock priority routing, CAPTCHA solving, and increased throughput.
| Free | Pro | Ultra | |
|---|---|---|---|
| Weekly | Free | $15/wk | $45/wk |
| Monthly | Free | $48/mo | $169/mo |
| Yearly | Free | $39/mo | $149/mo |
| Daily Requests | 2,500 | 50,000 | 250,000 |
| WebSocket Connections | 3 | 50 | 1,000 |
| Endpoints | All | All | All |
| CAPTCHA Solver | — | 50/day | 500/day |
| Uptime SLA | — | — | 99.5% |
| Priority Routing | — | ✓ | ✓ |
| Overage | — | $0.001/req | $0.0005/req |
| Support | Community | Dedicated |
Get your Free API key at tik.tools.
Examples
Chat Bot
import { TikTokLive } from '@tiktool/live';
const live = new TikTokLive({
uniqueId: 'streamer_name',
apiKey: 'YOUR_API_KEY',
});
live.on('chat', (e) => {
if (e.comment.toLowerCase() === '!hello') {
console.log(`Hello, ${e.user.nickname}!`);
}
});
live.on('gift', (e) => {
if (e.repeatEnd) {
const total = e.diamondCount * e.repeatCount;
console.log(`${e.user.uniqueId} sent ${e.repeatCount}x ${e.giftName} (${total} diamonds)`);
}
});
await live.connect();Real-Time OBS Overlay
import { TikTokLive } from '@tiktool/live';
import { WebSocketServer } from 'ws';
const wss = new WebSocketServer({ port: 8080 });
const live = new TikTokLive({
uniqueId: 'streamer_name',
apiKey: 'YOUR_API_KEY',
});
live.on('event', (event) => {
for (const client of wss.clients) {
client.send(JSON.stringify(event));
}
});
await live.connect();
console.log('Forwarding TikTok LIVE events → ws://localhost:8080');Gift Leaderboard
import { TikTokLive, GiftEvent } from '@tiktool/live';
const live = new TikTokLive({
uniqueId: 'streamer_name',
apiKey: 'YOUR_API_KEY',
});
const leaderboard = new Map<string, number>();
live.on('gift', (e: GiftEvent) => {
if (e.repeatEnd) {
const total = e.diamondCount * e.repeatCount;
const current = leaderboard.get(e.user.uniqueId) || 0;
leaderboard.set(e.user.uniqueId, current + total);
const sorted = [...leaderboard.entries()].sort((a, b) => b[1] - a[1]);
console.clear();
console.log('🏆 Gift Leaderboard');
sorted.slice(0, 10).forEach(([user, diamonds], i) => {
console.log(`${i + 1}. ${user}: ${diamonds} 💎`);
});
}
});
await live.connect();Battle Monitor
import { TikTokLive } from '@tiktool/live';
const live = new TikTokLive({
uniqueId: 'streamer_name',
apiKey: 'YOUR_API_KEY',
});
live.on('battle', (e) => {
console.log(`Battle ${e.battleId} — Status: ${e.status}`);
for (const team of e.teams) {
console.log(` Team ${team.hostUserId}: ${team.score} points (${team.users.length} members)`);
}
});
live.on('battleArmies', (e) => {
for (const team of e.teams) {
const host = team.hostUser?.uniqueId || team.hostUserId;
console.log(` ${host}: ${team.score} points`);
}
});
await live.connect();Frontend JWT Authentication
const data = await fetch('https://tik.tools/api/live/connect?uniqueId=streamer')
.then(r => r.json());
const ws = new WebSocket(`${data.wsUrl}?uniqueId=streamer&jwtKey=${data.token}`);
ws.onmessage = (event) => {
const parsed = JSON.parse(event.data);
console.log(parsed.type, parsed);
};🔧 Configuration
new TikTokLive(options)
| Option | Type | Default | Description |
|---|---|---|---|
uniqueId |
string |
— | TikTok username (without @) |
apiKey |
string |
— | API key from tik.tools |
signServerUrl |
string |
https://api.tik.tools |
Custom sign server URL |
autoReconnect |
boolean |
true |
Auto-reconnect on disconnect |
maxReconnectAttempts |
number |
5 |
Maximum reconnect attempts |
heartbeatInterval |
number |
10000 |
Heartbeat interval in milliseconds |
debug |
boolean |
false |
Enable debug logging |
webSocketImpl |
any |
Auto-detected | Custom WebSocket implementation |
Instance Properties
| Property | Type | Description |
|---|---|---|
connected |
boolean |
Current connection status |
eventCount |
number |
Total events received |
roomId |
string |
Active room ID |
Instance Methods
| Method | Returns | Description |
|---|---|---|
connect() |
Promise<void> |
Connect to a livestream |
disconnect() |
void |
Gracefully disconnect |
on(event, handler) |
this |
Listen for events |
once(event, handler) |
this |
Listen for a single event |
off(event, handler) |
this |
Remove event listener |
TypeScript
Full TypeScript support with generic type inference on all event handlers:
import { TikTokLive, ChatEvent, GiftEvent, BattleEvent } from '@tiktool/live';
const live = new TikTokLive({
uniqueId: 'username',
apiKey: 'YOUR_API_KEY',
});
live.on('chat', (event: ChatEvent) => {
const username: string = event.user.uniqueId;
const message: string = event.comment;
});
live.on('gift', (event: GiftEvent) => {
const diamonds: number = event.diamondCount;
const isCombo: boolean = event.combo;
});
live.on('battle', (event: BattleEvent) => {
const teams: typeof event.teams = event.teams;
const score: number = teams[0].score;
});All event types, user interfaces, and configuration options are fully exported for use in your application's type system.
Runtime Compatibility
| Runtime | Version | WebSocket | Notes |
|---|---|---|---|
| Node.js | ≥ 22 | Native | No additional packages needed |
| Node.js | 18–21 | ws package |
npm i ws |
| Bun | ≥ 1.0 | Native | Full support |
| Deno | ≥ 1.x | Native | Full support |
| Cloudflare Workers | — | Native | Full support |
| Browsers | Modern | Native | Via bundled connect endpoint |
License
MIT © TikTool