@tiktool/live 2.4.4 → 2.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +119 -363
- package/dist/index.d.mts +288 -135
- package/dist/index.d.ts +288 -135
- package/dist/index.js +455 -403
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +453 -396
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -28
package/README.md
CHANGED
|
@@ -2,52 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
# @tiktool/live
|
|
4
4
|
|
|
5
|
-
###
|
|
5
|
+
### Connect to any TikTok LIVE stream in 4 lines of code.
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/@tiktool/live)
|
|
8
8
|
[](https://opensource.org/licenses/MIT)
|
|
9
9
|
[](https://nodejs.org)
|
|
10
|
-
[](https://www.npmjs.com/package/@tiktool/live)
|
|
10
|
+
[](https://www.typescriptlang.org)
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
Real-time chat, gifts, viewers, battles, follows & 18+ event types from any TikTok livestream.
|
|
14
13
|
|
|
15
|
-
[Quick Start](#-quick-start) · [Events](#-events) · [API
|
|
14
|
+
[Quick Start](#-quick-start) · [Events](#-events) · [API](#-api-reference) · [Rate Limits](#-rate-limits) · [Get API Key](https://tik.tools)
|
|
16
15
|
|
|
17
16
|
</div>
|
|
18
17
|
|
|
19
18
|
---
|
|
20
19
|
|
|
21
|
-
## Why @tiktool/live?
|
|
22
|
-
|
|
23
|
-
- **Direct WebSocket connection** to TikTok from your own IP — events are never proxied through third-party servers
|
|
24
|
-
- **Zero dependencies** — built-in protobuf parser, universal WebSocket resolution, gzip decompression
|
|
25
|
-
- **Universal runtime support** — Node.js 18+, Bun, Deno, Cloudflare Workers, Durable Objects, and browsers
|
|
26
|
-
- **Full TypeScript** — complete type definitions for all 18+ event types with IDE autocompletion
|
|
27
|
-
- **Battle-tested** — production-ready with auto-reconnect, heartbeat management, and error recovery
|
|
28
|
-
- **Generous free tier** — 2,500 requests/day, 1 WebSocket connection, no credit card required
|
|
29
|
-
|
|
30
|
-
---
|
|
31
|
-
|
|
32
20
|
## ⚡ Quick Start
|
|
33
21
|
|
|
34
22
|
```bash
|
|
35
23
|
npm install @tiktool/live
|
|
36
24
|
```
|
|
37
25
|
|
|
38
|
-
Get your free API key at
|
|
26
|
+
Get your free API key at [tik.tools](https://tik.tools)
|
|
39
27
|
|
|
40
28
|
```typescript
|
|
41
29
|
import { TikTokLive } from '@tiktool/live';
|
|
42
30
|
|
|
43
31
|
const live = new TikTokLive({
|
|
44
|
-
uniqueId: '
|
|
32
|
+
uniqueId: 'tv_asahi_news',
|
|
45
33
|
apiKey: 'YOUR_API_KEY',
|
|
46
34
|
});
|
|
47
35
|
|
|
48
36
|
live.on('chat', e => console.log(`${e.user.uniqueId}: ${e.comment}`));
|
|
49
|
-
live.on('gift', e => console.log(`${e.user.uniqueId} sent ${e.giftName} (${e.diamondCount}
|
|
50
|
-
live.on('member', e => console.log(`${e.user.uniqueId} joined
|
|
37
|
+
live.on('gift', e => console.log(`${e.user.uniqueId} sent ${e.giftName} (${e.diamondCount} diamonds)`));
|
|
38
|
+
live.on('member', e => console.log(`${e.user.uniqueId} joined`));
|
|
51
39
|
live.on('roomUserSeq', e => console.log(`Viewers: ${e.viewerCount}`));
|
|
52
40
|
|
|
53
41
|
await live.connect();
|
|
@@ -55,32 +43,36 @@ await live.connect();
|
|
|
55
43
|
|
|
56
44
|
---
|
|
57
45
|
|
|
58
|
-
##
|
|
46
|
+
## How It Works
|
|
59
47
|
|
|
60
48
|
```
|
|
61
|
-
Your App
|
|
49
|
+
Your App tik.tools TikTok
|
|
62
50
|
+-----------+ +--------------+ +--------------+
|
|
63
|
-
|
|
|
64
|
-
|
|
|
65
|
-
|
|
|
66
|
-
|
|
|
67
|
-
|
|
|
51
|
+
| -+-- sign_url --> Signs URL | | |
|
|
52
|
+
| Your <-+-- X-Bogus --| with params | | TikTok |
|
|
53
|
+
| Code | | | | WebSocket |
|
|
54
|
+
| -+------------ Connect directly ---------->| Server |
|
|
55
|
+
| <-+------------ Live events (protobuf) <---| |
|
|
68
56
|
+-----------+ +--------------+ +--------------+
|
|
69
|
-
|
|
70
|
-
|
|
57
|
+
^ Only interaction ^ Direct from
|
|
58
|
+
with our server YOUR IP
|
|
71
59
|
```
|
|
72
60
|
|
|
73
|
-
|
|
61
|
+
- Your app connects directly to TikTok from your IP address
|
|
62
|
+
- The sign server only generates cryptographic signatures (requires API key)
|
|
63
|
+
- TikTok never sees the sign server
|
|
64
|
+
- Built-in protobuf parser, no external dependencies
|
|
74
65
|
|
|
75
66
|
---
|
|
76
67
|
|
|
77
|
-
##
|
|
68
|
+
## Events
|
|
69
|
+
|
|
70
|
+
### Listening
|
|
78
71
|
|
|
79
72
|
```typescript
|
|
80
73
|
live.on('chat', (event) => {
|
|
81
|
-
event.user.uniqueId
|
|
82
|
-
event.
|
|
83
|
-
event.comment; // string
|
|
74
|
+
event.user.uniqueId // string
|
|
75
|
+
event.comment // string
|
|
84
76
|
});
|
|
85
77
|
|
|
86
78
|
live.on('event', (event) => {
|
|
@@ -88,238 +80,129 @@ live.on('event', (event) => {
|
|
|
88
80
|
});
|
|
89
81
|
```
|
|
90
82
|
|
|
91
|
-
###
|
|
92
|
-
|
|
93
|
-
| Event | Type | Description |
|
|
94
|
-
|
|
95
|
-
| `chat` | `ChatEvent` | Chat message
|
|
96
|
-
| `member` | `MemberEvent` | User joined
|
|
97
|
-
| `like` | `LikeEvent` | User liked
|
|
98
|
-
| `gift` | `GiftEvent` | Gift sent
|
|
99
|
-
| `social` | `SocialEvent` | Follow
|
|
100
|
-
| `roomUserSeq` | `RoomUserSeqEvent` | Viewer count
|
|
101
|
-
| `battle` | `BattleEvent` |
|
|
102
|
-
| `battleArmies` | `BattleArmiesEvent` | Battle
|
|
83
|
+
### Reference
|
|
84
|
+
|
|
85
|
+
| Event | Type | Description | Fields |
|
|
86
|
+
|-------|------|-------------|--------|
|
|
87
|
+
| `chat` | `ChatEvent` | Chat message | `user`, `comment` |
|
|
88
|
+
| `member` | `MemberEvent` | User joined | `user`, `action` |
|
|
89
|
+
| `like` | `LikeEvent` | User liked | `user`, `likeCount`, `totalLikes` |
|
|
90
|
+
| `gift` | `GiftEvent` | Gift sent | `user`, `giftName`, `diamondCount`, `repeatCount`, `combo` |
|
|
91
|
+
| `social` | `SocialEvent` | Follow / Share | `user`, `action` |
|
|
92
|
+
| `roomUserSeq` | `RoomUserSeqEvent` | Viewer count | `viewerCount`, `totalViewers` |
|
|
93
|
+
| `battle` | `BattleEvent` | Link Mic battle | `status` |
|
|
94
|
+
| `battleArmies` | `BattleArmiesEvent` | Battle teams | — |
|
|
103
95
|
| `subscribe` | `SubscribeEvent` | New subscriber | `user`, `subMonth` |
|
|
104
|
-
| `emoteChat` | `EmoteChatEvent` | Emote
|
|
105
|
-
| `envelope` | `EnvelopeEvent` | Treasure chest | `
|
|
96
|
+
| `emoteChat` | `EmoteChatEvent` | Emote in chat | `user`, `emoteId` |
|
|
97
|
+
| `envelope` | `EnvelopeEvent` | Treasure chest | `diamondCount` |
|
|
106
98
|
| `question` | `QuestionEvent` | Q&A question | `user`, `questionText` |
|
|
107
|
-
| `control` | `ControlEvent` | Stream control
|
|
108
|
-
| `room` | `RoomEvent` | Room status
|
|
109
|
-
| `liveIntro` | `LiveIntroEvent` | Stream intro
|
|
110
|
-
| `rankUpdate` | `RankUpdateEvent` |
|
|
111
|
-
| `linkMic` | `LinkMicEvent` | Link Mic
|
|
112
|
-
| `unknown` | `UnknownEvent` | Unrecognized
|
|
99
|
+
| `control` | `ControlEvent` | Stream control | `action` (3 = ended) |
|
|
100
|
+
| `room` | `RoomEvent` | Room status | `status` |
|
|
101
|
+
| `liveIntro` | `LiveIntroEvent` | Stream intro | `title` |
|
|
102
|
+
| `rankUpdate` | `RankUpdateEvent` | Rank update | `rankType` |
|
|
103
|
+
| `linkMic` | `LinkMicEvent` | Link Mic | `action` |
|
|
104
|
+
| `unknown` | `UnknownEvent` | Unrecognized | `method` |
|
|
113
105
|
|
|
114
106
|
### Connection Events
|
|
115
107
|
|
|
116
108
|
| Event | Callback | Description |
|
|
117
109
|
|-------|----------|-------------|
|
|
118
|
-
| `connected` | `() => void` |
|
|
119
|
-
| `disconnected` | `(code, reason) => void` | Disconnected
|
|
120
|
-
| `roomInfo` | `(info: RoomInfo) => void` | Room
|
|
121
|
-
| `error` | `(error: Error) => void` |
|
|
110
|
+
| `connected` | `() => void` | Connected to stream |
|
|
111
|
+
| `disconnected` | `(code, reason) => void` | Disconnected |
|
|
112
|
+
| `roomInfo` | `(info: RoomInfo) => void` | Room info |
|
|
113
|
+
| `error` | `(error: Error) => void` | Error |
|
|
122
114
|
|
|
123
115
|
---
|
|
124
116
|
|
|
125
|
-
##
|
|
126
|
-
|
|
127
|
-
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.
|
|
128
|
-
|
|
129
|
-
### `callApi(options)` — High-Level Helper
|
|
117
|
+
## API Reference
|
|
130
118
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
```typescript
|
|
134
|
-
import { callApi } from '@tiktool/live';
|
|
135
|
-
|
|
136
|
-
// Get stream video URLs for a user
|
|
137
|
-
const streamData = await callApi({
|
|
138
|
-
apiKey: 'YOUR_API_KEY',
|
|
139
|
-
endpoint: '/webcast/room_video',
|
|
140
|
-
uniqueId: 'streamer_name',
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// Get room info
|
|
144
|
-
const roomInfo = await callApi({
|
|
145
|
-
apiKey: 'YOUR_API_KEY',
|
|
146
|
-
endpoint: '/webcast/room_info',
|
|
147
|
-
uniqueId: 'streamer_name',
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
// Use a custom server URL
|
|
151
|
-
const data = await callApi({
|
|
152
|
-
serverUrl: 'https://your-server.com',
|
|
153
|
-
apiKey: 'YOUR_API_KEY',
|
|
154
|
-
endpoint: '/webcast/rankings',
|
|
155
|
-
uniqueId: 'streamer_name',
|
|
156
|
-
method: 'GET',
|
|
157
|
-
});
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
#### `CallApiOptions`
|
|
119
|
+
### `new TikTokLive(options)`
|
|
161
120
|
|
|
162
121
|
| Option | Type | Default | Description |
|
|
163
122
|
|--------|------|---------|-------------|
|
|
164
|
-
| `
|
|
165
|
-
| `
|
|
166
|
-
| `
|
|
167
|
-
| `
|
|
168
|
-
| `
|
|
169
|
-
| `
|
|
170
|
-
|
|
171
|
-
### `resolveRoomId(uniqueId)` — Username to Room ID
|
|
172
|
-
|
|
173
|
-
Scrapes a TikTok live page to extract the `room_id`. Results are cached for 5 minutes. Returns `null` if the user is not live.
|
|
174
|
-
|
|
175
|
-
```typescript
|
|
176
|
-
import { resolveRoomId } from '@tiktool/live';
|
|
177
|
-
|
|
178
|
-
const roomId = await resolveRoomId('streamer_name');
|
|
179
|
-
if (roomId) {
|
|
180
|
-
console.log(`Room ID: ${roomId}`);
|
|
181
|
-
}
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
### `resolveLivePage(uniqueId)` — Full Live Page Resolution
|
|
185
|
-
|
|
186
|
-
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.
|
|
187
|
-
|
|
188
|
-
```typescript
|
|
189
|
-
import { resolveLivePage } from '@tiktool/live';
|
|
123
|
+
| `uniqueId` | `string` | — | TikTok username (without @) |
|
|
124
|
+
| `apiKey` | `string` | — | **Required.** API key from [tik.tools](https://tik.tools) |
|
|
125
|
+
| `signServerUrl` | `string` | `https://api.tik.tools` | Sign server URL |
|
|
126
|
+
| `autoReconnect` | `boolean` | `true` | Auto-reconnect on disconnect |
|
|
127
|
+
| `maxReconnectAttempts` | `number` | `5` | Max reconnect attempts |
|
|
128
|
+
| `heartbeatInterval` | `number` | `10000` | Heartbeat interval (ms) |
|
|
129
|
+
| `debug` | `boolean` | `false` | Debug logging |
|
|
190
130
|
|
|
191
|
-
|
|
192
|
-
if (info) {
|
|
193
|
-
console.log(`Room: ${info.roomId}, Region: ${info.clusterRegion}`);
|
|
194
|
-
}
|
|
195
|
-
```
|
|
131
|
+
### Methods
|
|
196
132
|
|
|
197
|
-
|
|
133
|
+
| Method | Returns | Description |
|
|
134
|
+
|--------|---------|-------------|
|
|
135
|
+
| `connect()` | `Promise<void>` | Connect to livestream |
|
|
136
|
+
| `disconnect()` | `void` | Disconnect |
|
|
137
|
+
| `connected` | `boolean` | Connection status |
|
|
138
|
+
| `eventCount` | `number` | Total events received |
|
|
139
|
+
| `roomId` | `string` | Current room ID |
|
|
198
140
|
|
|
199
|
-
|
|
200
|
-
|-------|------|-------------|
|
|
201
|
-
| `roomId` | `string` | Active room ID for the livestream |
|
|
202
|
-
| `ttwid` | `string` | Session cookie for WebSocket auth |
|
|
203
|
-
| `clusterRegion` | `string` | Server cluster region (e.g. `'us'`, `'eu'`) |
|
|
141
|
+
---
|
|
204
142
|
|
|
205
|
-
|
|
143
|
+
## Rate Limits
|
|
206
144
|
|
|
207
|
-
|
|
145
|
+
All API requests require an API key. Get yours at [tik.tools](https://tik.tools).
|
|
208
146
|
|
|
209
|
-
|
|
210
|
-
|
|
147
|
+
| Tier | Rate Limit | Endpoints | Price |
|
|
148
|
+
|------|-----------|-----------|-------|
|
|
149
|
+
| **Free** | 5/min | `sign_url`, `check_alive` | Free |
|
|
150
|
+
| **Basic** | 30/min | + `fetch`, `room_info`, `bulk_live_check`, WS relay | Free (with key) |
|
|
151
|
+
| **Pro** | 120/min | + `room_video`, `ranklist/regional`, CAPTCHA solver, all endpoints | Coming soon |
|
|
211
152
|
|
|
212
|
-
|
|
213
|
-
const tikTokData = await fetchSignedUrl(apiResponse);
|
|
214
|
-
```
|
|
153
|
+
The SDK calls the sign server **once per connection**, then stays connected via WebSocket. A free key is sufficient for most use cases.
|
|
215
154
|
|
|
216
155
|
---
|
|
217
156
|
|
|
218
|
-
##
|
|
157
|
+
## 🏆 Regional Leaderboard
|
|
219
158
|
|
|
220
|
-
|
|
159
|
+
Get daily, hourly, popular, or league LIVE rankings for any streamer. **Requires Pro or Ultra tier.**
|
|
221
160
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
```typescript
|
|
225
|
-
import { solvePuzzle } from '@tiktool/live';
|
|
226
|
-
|
|
227
|
-
const result = await solvePuzzle('YOUR_API_KEY', backgroundBase64, pieceBase64);
|
|
228
|
-
console.log(`Slide to X=${result.slideX}px (confidence: ${result.confidence})`);
|
|
229
|
-
```
|
|
161
|
+
This endpoint uses a **two-step sign-and-return pattern** because TikTok sessions are IP-bound:
|
|
230
162
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
| `slideX` | `number` | Pixel X position for the slider |
|
|
234
|
-
| `confidence` | `number` | Confidence score (0.0–1.0) |
|
|
235
|
-
| `solveTimeMs` | `number` | Time taken to solve in ms |
|
|
236
|
-
|
|
237
|
-
### `solveRotate(apiKey, outerB64, innerB64, serverUrl?)` — Rotate Whirl
|
|
163
|
+
1. Call `getRegionalRanklist()` to get a signed URL from the server
|
|
164
|
+
2. POST the signed URL from **your own IP** with your TikTok session cookie
|
|
238
165
|
|
|
239
166
|
```typescript
|
|
240
|
-
import {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
| `confidence` | `number` | Confidence score (0.0–1.0) |
|
|
250
|
-
| `solveTimeMs` | `number` | Time taken to solve in ms |
|
|
251
|
-
|
|
252
|
-
### `solveShapes(apiKey, imageB64, serverUrl?)` — 3D Shape Matching
|
|
167
|
+
import { getRegionalRanklist } from '@tiktool/live';
|
|
168
|
+
|
|
169
|
+
// Step 1: Get signed URL from the API
|
|
170
|
+
const signed = await getRegionalRanklist({
|
|
171
|
+
apiKey: 'YOUR_PRO_KEY',
|
|
172
|
+
roomId: '7607695933891218198',
|
|
173
|
+
anchorId: '7444599004337652758',
|
|
174
|
+
rankType: '8', // Daily
|
|
175
|
+
});
|
|
253
176
|
|
|
254
|
-
|
|
255
|
-
|
|
177
|
+
// Step 2: Fetch from YOUR IP with YOUR session cookie
|
|
178
|
+
const resp = await fetch(signed.signed_url, {
|
|
179
|
+
method: signed.method,
|
|
180
|
+
headers: { ...signed.headers, Cookie: `sessionid=YOUR_SID; ${signed.cookies}` },
|
|
181
|
+
body: signed.body,
|
|
182
|
+
});
|
|
256
183
|
|
|
257
|
-
const
|
|
258
|
-
|
|
184
|
+
const { data } = await resp.json();
|
|
185
|
+
data.rank_view.ranks.forEach((r: any, i: number) =>
|
|
186
|
+
console.log(`${i+1}. ${r.user.nickname} — ${r.score} pts`)
|
|
187
|
+
);
|
|
259
188
|
```
|
|
260
189
|
|
|
261
|
-
|
|
262
|
-
|-------|------|-------------|
|
|
263
|
-
| `point1` | `{x, y}` | First matching shape center |
|
|
264
|
-
| `point2` | `{x, y}` | Second matching shape center |
|
|
265
|
-
| `confidence` | `number` | Confidence score (0.0–1.0) |
|
|
266
|
-
| `solveTimeMs` | `number` | Time taken to solve in ms |
|
|
190
|
+
### Rank Types
|
|
267
191
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
| Endpoint | Description |
|
|
275
|
-
|----------|-------------|
|
|
276
|
-
| `POST /webcast/sign_url` | Generate X-Bogus signed URL |
|
|
277
|
-
| `GET /webcast/check_alive` | Check if a user is currently live |
|
|
278
|
-
| `GET /webcast/room_info` | Detailed room metadata (title, viewers, streamer info) |
|
|
279
|
-
| `GET /webcast/room_id` | Resolve username to room ID |
|
|
280
|
-
| `GET /webcast/room_cover` | Get stream cover image URL |
|
|
281
|
-
| `GET /webcast/room_video` | Get HLS/FLV stream URLs |
|
|
282
|
-
| `GET /webcast/rankings` | In-stream contribution rankings |
|
|
283
|
-
| `GET /webcast/gift_info` | Available gifts and diamond values |
|
|
284
|
-
| `GET /webcast/hashtag_list` | Trending live hashtags |
|
|
285
|
-
| `GET /webcast/bulk_live_check` | Check multiple users' live status in one request |
|
|
286
|
-
| `POST /webcast/fetch` | Fetch protobuf messages via HTTP polling |
|
|
287
|
-
| `GET /webcast/ws_credentials` | Get WebSocket connection credentials |
|
|
288
|
-
| `POST /webcast/sign_websocket` | Sign a WebSocket URL |
|
|
289
|
-
| `GET /webcast/resolve_user_ids` | Resolve display names to user IDs |
|
|
290
|
-
| `POST /webcast/chat` | Send chat messages (requires session cookies) |
|
|
291
|
-
| `GET /webcast/user_earnings` | Streamer earnings data (requires session cookies) |
|
|
292
|
-
| `GET /webcast/feed` | Live feed discovery |
|
|
293
|
-
| `GET /webcast/live_analytics/*` | Video list, video detail, user interactions |
|
|
294
|
-
| `GET /webcast/moderation/*` | Mute and ban management |
|
|
295
|
-
| `POST /authentication/jwt` | Generate scoped JWT for frontend WebSocket access |
|
|
296
|
-
| `POST /captcha/solve/puzzle` | Solve puzzle slider CAPTCHA (**Pro+**) |
|
|
297
|
-
| `POST /captcha/solve/rotate` | Solve rotate whirl CAPTCHA (**Pro+**) |
|
|
298
|
-
| `POST /captcha/solve/shapes` | Solve 3D shape matching CAPTCHA (**Pro+**) |
|
|
299
|
-
|
|
300
|
-
Full API documentation: **[tik.tools/docs](https://tik.tools/docs)**
|
|
301
|
-
|
|
302
|
-
---
|
|
192
|
+
| Value | Period |
|
|
193
|
+
|-------|--------|
|
|
194
|
+
| `"1"` | Hourly |
|
|
195
|
+
| `"8"` | Daily (default) |
|
|
196
|
+
| `"15"` | Popular LIVE |
|
|
197
|
+
| `"16"` | League |
|
|
303
198
|
|
|
304
|
-
|
|
199
|
+
### Response Shape
|
|
305
200
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
| **Monthly** | Free | $48/mo | $169/mo |
|
|
312
|
-
| **Yearly** | Free | $39/mo | $149/mo |
|
|
313
|
-
| **Daily Requests** | 2,500 | 50,000 | 250,000 |
|
|
314
|
-
| **WebSocket Connections** | 3 | 50 | 1,000 |
|
|
315
|
-
| **Endpoints** | All | All | All |
|
|
316
|
-
| **CAPTCHA Solver** | — | 50/day | 500/day |
|
|
317
|
-
| **Uptime SLA** | — | — | 99.5% |
|
|
318
|
-
| **Priority Routing** | — | ✓ | ✓ |
|
|
319
|
-
| **Overage** | — | $0.001/req | $0.0005/req |
|
|
320
|
-
| **Support** | Community | Email | Dedicated |
|
|
321
|
-
|
|
322
|
-
Get your Free API key at **[tik.tools](https://tik.tools)**.
|
|
201
|
+
The TikTok response contains `data.rank_view` with:
|
|
202
|
+
- **`ranks`** — Array of ranked users with `user.nickname`, `user.display_id`, `score`
|
|
203
|
+
- **`owner_rank`** — The streamer's own position and gap info
|
|
204
|
+
- **`countdown`** — Seconds until the ranking period ends
|
|
205
|
+
- **`cut_off_line`** — Minimum score to appear on the leaderboard
|
|
323
206
|
|
|
324
207
|
---
|
|
325
208
|
|
|
@@ -343,15 +226,14 @@ live.on('chat', (e) => {
|
|
|
343
226
|
|
|
344
227
|
live.on('gift', (e) => {
|
|
345
228
|
if (e.repeatEnd) {
|
|
346
|
-
|
|
347
|
-
console.log(`${e.user.uniqueId} sent ${e.repeatCount}x ${e.giftName} (${total} diamonds)`);
|
|
229
|
+
console.log(`${e.user.uniqueId} sent ${e.repeatCount}x ${e.giftName} (${e.diamondCount * e.repeatCount} diamonds)`);
|
|
348
230
|
}
|
|
349
231
|
});
|
|
350
232
|
|
|
351
233
|
await live.connect();
|
|
352
234
|
```
|
|
353
235
|
|
|
354
|
-
###
|
|
236
|
+
### OBS Overlay
|
|
355
237
|
|
|
356
238
|
```typescript
|
|
357
239
|
import { TikTokLive } from '@tiktool/live';
|
|
@@ -370,123 +252,17 @@ live.on('event', (event) => {
|
|
|
370
252
|
});
|
|
371
253
|
|
|
372
254
|
await live.connect();
|
|
373
|
-
console.log('Forwarding
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
### Gift Leaderboard
|
|
377
|
-
|
|
378
|
-
```typescript
|
|
379
|
-
import { TikTokLive, GiftEvent } from '@tiktool/live';
|
|
380
|
-
|
|
381
|
-
const live = new TikTokLive({
|
|
382
|
-
uniqueId: 'streamer_name',
|
|
383
|
-
apiKey: 'YOUR_API_KEY',
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
const leaderboard = new Map<string, number>();
|
|
387
|
-
|
|
388
|
-
live.on('gift', (e: GiftEvent) => {
|
|
389
|
-
if (e.repeatEnd) {
|
|
390
|
-
const total = e.diamondCount * e.repeatCount;
|
|
391
|
-
const current = leaderboard.get(e.user.uniqueId) || 0;
|
|
392
|
-
leaderboard.set(e.user.uniqueId, current + total);
|
|
393
|
-
|
|
394
|
-
const sorted = [...leaderboard.entries()].sort((a, b) => b[1] - a[1]);
|
|
395
|
-
console.clear();
|
|
396
|
-
console.log('🏆 Gift Leaderboard');
|
|
397
|
-
sorted.slice(0, 10).forEach(([user, diamonds], i) => {
|
|
398
|
-
console.log(`${i + 1}. ${user}: ${diamonds} 💎`);
|
|
399
|
-
});
|
|
400
|
-
}
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
await live.connect();
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
### Battle Monitor
|
|
407
|
-
|
|
408
|
-
```typescript
|
|
409
|
-
import { TikTokLive } from '@tiktool/live';
|
|
410
|
-
|
|
411
|
-
const live = new TikTokLive({
|
|
412
|
-
uniqueId: 'streamer_name',
|
|
413
|
-
apiKey: 'YOUR_API_KEY',
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
live.on('battle', (e) => {
|
|
417
|
-
console.log(`Battle ${e.battleId} — Status: ${e.status}`);
|
|
418
|
-
for (const team of e.teams) {
|
|
419
|
-
console.log(` Team ${team.hostUserId}: ${team.score} points (${team.users.length} members)`);
|
|
420
|
-
}
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
live.on('battleArmies', (e) => {
|
|
424
|
-
for (const team of e.teams) {
|
|
425
|
-
const host = team.hostUser?.uniqueId || team.hostUserId;
|
|
426
|
-
console.log(` ${host}: ${team.score} points`);
|
|
427
|
-
}
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
await live.connect();
|
|
431
|
-
```
|
|
432
|
-
|
|
433
|
-
### Frontend JWT Authentication
|
|
434
|
-
|
|
435
|
-
```typescript
|
|
436
|
-
const data = await fetch('https://tik.tools/api/live/connect?uniqueId=streamer')
|
|
437
|
-
.then(r => r.json());
|
|
438
|
-
|
|
439
|
-
const ws = new WebSocket(`${data.wsUrl}?uniqueId=streamer&jwtKey=${data.token}`);
|
|
440
|
-
|
|
441
|
-
ws.onmessage = (event) => {
|
|
442
|
-
const parsed = JSON.parse(event.data);
|
|
443
|
-
console.log(parsed.type, parsed);
|
|
444
|
-
};
|
|
255
|
+
console.log('Forwarding events to ws://localhost:8080');
|
|
445
256
|
```
|
|
446
257
|
|
|
447
258
|
---
|
|
448
259
|
|
|
449
|
-
## 🔧 Configuration
|
|
450
|
-
|
|
451
|
-
### `new TikTokLive(options)`
|
|
452
|
-
|
|
453
|
-
| Option | Type | Default | Description |
|
|
454
|
-
|--------|------|---------|-------------|
|
|
455
|
-
| `uniqueId` | `string` | — | TikTok username (without `@`) |
|
|
456
|
-
| `apiKey` | `string` | — | API key from [tik.tools](https://tik.tools) |
|
|
457
|
-
| `signServerUrl` | `string` | `https://api.tik.tools` | Custom sign server URL |
|
|
458
|
-
| `autoReconnect` | `boolean` | `true` | Auto-reconnect on disconnect |
|
|
459
|
-
| `maxReconnectAttempts` | `number` | `5` | Maximum reconnect attempts |
|
|
460
|
-
| `heartbeatInterval` | `number` | `10000` | Heartbeat interval in milliseconds |
|
|
461
|
-
| `debug` | `boolean` | `false` | Enable debug logging |
|
|
462
|
-
| `webSocketImpl` | `any` | Auto-detected | Custom WebSocket implementation |
|
|
463
|
-
|
|
464
|
-
### Instance Properties
|
|
465
|
-
|
|
466
|
-
| Property | Type | Description |
|
|
467
|
-
|----------|------|-------------|
|
|
468
|
-
| `connected` | `boolean` | Current connection status |
|
|
469
|
-
| `eventCount` | `number` | Total events received |
|
|
470
|
-
| `roomId` | `string` | Active room ID |
|
|
471
|
-
|
|
472
|
-
### Instance Methods
|
|
473
|
-
|
|
474
|
-
| Method | Returns | Description |
|
|
475
|
-
|--------|---------|-------------|
|
|
476
|
-
| `connect()` | `Promise<void>` | Connect to a livestream |
|
|
477
|
-
| `disconnect()` | `void` | Gracefully disconnect |
|
|
478
|
-
| `on(event, handler)` | `this` | Listen for events |
|
|
479
|
-
| `once(event, handler)` | `this` | Listen for a single event |
|
|
480
|
-
| `off(event, handler)` | `this` | Remove event listener |
|
|
481
|
-
|
|
482
|
-
---
|
|
483
|
-
|
|
484
260
|
## TypeScript
|
|
485
261
|
|
|
486
|
-
Full TypeScript support with
|
|
262
|
+
Full TypeScript support with type inference:
|
|
487
263
|
|
|
488
264
|
```typescript
|
|
489
|
-
import { TikTokLive, ChatEvent, GiftEvent
|
|
265
|
+
import { TikTokLive, ChatEvent, GiftEvent } from '@tiktool/live';
|
|
490
266
|
|
|
491
267
|
const live = new TikTokLive({
|
|
492
268
|
uniqueId: 'username',
|
|
@@ -502,30 +278,10 @@ live.on('gift', (event: GiftEvent) => {
|
|
|
502
278
|
const diamonds: number = event.diamondCount;
|
|
503
279
|
const isCombo: boolean = event.combo;
|
|
504
280
|
});
|
|
505
|
-
|
|
506
|
-
live.on('battle', (event: BattleEvent) => {
|
|
507
|
-
const teams: typeof event.teams = event.teams;
|
|
508
|
-
const score: number = teams[0].score;
|
|
509
|
-
});
|
|
510
281
|
```
|
|
511
282
|
|
|
512
|
-
All event types, user interfaces, and configuration options are fully exported for use in your application's type system.
|
|
513
|
-
|
|
514
|
-
---
|
|
515
|
-
|
|
516
|
-
## Runtime Compatibility
|
|
517
|
-
|
|
518
|
-
| Runtime | Version | WebSocket | Notes |
|
|
519
|
-
|---------|---------|-----------|-------|
|
|
520
|
-
| **Node.js** | ≥ 22 | Native | No additional packages needed |
|
|
521
|
-
| **Node.js** | 18–21 | `ws` package | `npm i ws` |
|
|
522
|
-
| **Bun** | ≥ 1.0 | Native | Full support |
|
|
523
|
-
| **Deno** | ≥ 1.x | Native | Full support |
|
|
524
|
-
| **Cloudflare Workers** | — | Native | Full support |
|
|
525
|
-
| **Browsers** | Modern | Native | Via bundled connect endpoint |
|
|
526
|
-
|
|
527
283
|
---
|
|
528
284
|
|
|
529
285
|
## License
|
|
530
286
|
|
|
531
|
-
MIT © [
|
|
287
|
+
MIT © [tiktool](https://tik.tools)
|