Library / SDK
zerodytrash/TikTok-Live-Connector avatar
zerodytrash/TikTok-Live-Connector

TikTok-Live-Connector: reading a TikTok LIVE chat with a username

TikTok LIVE API for Node.js: An unofficial library to receive live stream events (comments, gifts, etc.) in realtime from TikTok LIVE.

2,220 stars478 forksTypeScriptAGPL-3.0

At a glance

What is it?
TikTok-Live-Connector turns a broadcaster's username into a signed WebSocket event stream of comments, gifts and viewer counts for Node.js code. The parts worth understanding before you depend on it are the third-party signing hop and the session cookies you can hand it.
Who is it for?
Adopt this library when your own tooling has to react to a live room in real time and a reverse engineered path is acceptable: install tiktok-live-connector, pass a username, supply a signApiKey. Keep it out of anything user facing or uptime critical, since the README calls it a reverse engineering project while the npm description promises a complete library with no login step.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A username is the only required constructor argument

`TikTokLiveConnection(uniqueId, [options])` is the whole surface. The broadcaster's handle is the only mandatory value, and it can arrive as a bare name, in the form with a leading at sign, or as a full live address such as `https://www.tiktok.com/@officialgeilegisela/live`, whose example resolves to `officialgeilegisela`. Everything else sits in the optional object, where each key carries a default you override only for a concrete reason.

`connect()` returns a promise resolving to a state object whose `roomId` identifies the room, and it rejects with `UserOfflineError` when the account is not live at that moment. After that, nothing is a raw socket you parse yourself. Each message type arrives on its own `WebcastEvent` key, so `WebcastEvent.CHAT` carries comments while `WebcastEvent.GIFT` carries gifts with a `giftId`.

The tracked set reaches well past chat. Joins, room user counts, follows, shares, questions, likes, super fans and linkmic battles are all covered, anchored in the table of contents as `#chat`, `#gift`, `#like`, `#roomuser`, `#social`, `#member`, `#questionnew`, `#superfan` and `#linkmicbattle`.

From npm install to the first chat line in one script

The package is published on npm as `tiktok-live-connector`, and installation is one command with no build step on your side. The quick start adds no configuration file and reads no environment variables, because everything travels through the constructor.

bash
npm i tiktok-live-connector

A first connection needs an import, a username and one call to `connect()`. The call returns a promise, so `await` is optional, and the resolved value is where the room id lives.

ts
// Username of someone who is currently live
import { TikTokLiveConnection, WebcastEvent } from 'tiktok-live-connector';

const tiktokUsername = 'officialgeilegisela';

// Create a new wrapper object and pass the username
const connection = new TikTokLiveConnection(tiktokUsername);

// Connect to the chat (await can be used as well)
connection.connect().then(state => {
    console.info(`Connected to roomId ${state.roomId}`);
}).catch(err => {
    console.error('Failed to connect', err);
});

You should see one line naming the room id you are attached to. If the account is offline, the `.catch` branch prints instead. A runnable version with its full source sits at `tiktok-chat-reader.zerody.one`, in the `TikTok-Chat-Reader` repository.

Signing is delegated to Euler Stream rather than your own code

Messages cannot be opened with a websocket client alone, so the library stops at the edge and asks someone else to sign each request. Two options control that hop. `signApiKey` takes an Euler Stream API key and writes it into the global `SignConfig.apiKey` before the underlying Euler client is created. `eulerApiInstance` accepts an already built `EulerStreamApiClient` and takes precedence over `signApiKey`, which lets a long lived process keep one client instead of creating another per room.

That arrangement is the architectural fact that matters most. Your process holds no signing logic and no TikTok app credentials, yet every comment, gift and like depends on a third party answering first. When that service is slow or unreachable, `connect()` has nothing to resolve against, and the failure reaches you as a promise rejection that reads like an offline streamer.

The sign response also carries data. With `processInitialData` at its default of `true`, the batch attached to that first response is decoded and emitted as ordinary events, which is how recent chat history arrives next to live traffic.

Session cookies cross the network when authenticateWs is enabled

The default connection is anonymous. Credentials enter through the `session` bundle, which takes `session.cookie` to seed a cookie jar with `sessionid` and `tt-target-idc`, and `session.oAuthToken` to hand an OAuth token to the sign server. A session is required whenever `authenticateWs` or `useMobile` is on.

`authenticateWs` defaults to `false`, and the stated reason is short: signing already happens at a third-party service, so enabling the flag forwards your session cookies or OAuth token to that service too. Read the two facts together. An anonymous connection leaves your credentials on your machine; an authenticated one does not.

`useMobile` switches to the mobile WebSocket flow and is stricter. It needs both `authenticateWs: true` and `session.cookie`, and connecting with `useMobile` while `authenticateWs` is false throws a `TypeError` before any request goes out. `clientPresets` lives in the same object, defaulting to randomized fingerprints that you can replace with a pre-built object whose keys begin with `device` and `screen`.

One line in the README advertises no login, credentials or app registration, which holds for the default path and fails for this one.

The documented chat handler prints uniqueId twice

Pasting the quick start gives you a handler that labels a chat line with the wrong id. In the `WebcastEvent.CHAT` example the template interpolates `data.user.uniqueId` in both positions, so the parenthetical prints the handle rather than the numeric user id. The gift handler two lines further down gets it right and reads `data.user.userId`.

ts
connection.on(WebcastEvent.CHAT, data => {
    console.log(`${data.user.uniqueId} (userId:${data.user.uniqueId}) writes: ${data.comment}`);
});

connection.on(WebcastEvent.GIFT, data => {
    console.log(`${data.user.uniqueId} (userId:${data.user.userId}) sends ${data.giftId}`);
});

Nothing in the library breaks, and the slip sits in a printed string rather than in the event payload, so it survives into any moderation log or CSV you build from that output. Read the payload shape instead of trusting the sample line. The remaining tracked handlers, anchored as `#member`, `#roomuser`, `#social`, `#questionnew`, `#like`, `#superfan` and `#linkmicbattle`, are described further down the same page.

Three connect-time switches decide what the first events contain

What lands in your first seconds depends on three booleans whose defaults are not neutral.

`fetchRoomInfoOnConnect` defaults to `true`. Room info is fetched during connect, stored on `connection.roomInfo` and readable afterwards through `fetchRoomInfo()`, and that same fetch is what turns an offline broadcaster into a rejected promise carrying `UserOfflineError`. Switch it off and you lose the early check, so a dead username looks like a quiet room instead of an error.

`processInitialData` also defaults to `true` and decides whether the batch riding along with the sign response gets decoded and emitted. Turn it off and you receive live traffic only, which suits a session counter that should start at zero rather than replay what the room said while you were connecting.

`enableExtendedGiftInfo` defaults to `false`. Enabled, the room gift list is fetched during connect so `WebcastGiftMessage` events carry an `extendedGiftInfo` field with a name, a cost and image data, sparing you a lookup table for the bare ids in the gift handler.

Node 20, ESM by default and a legacy subpath export

package.json describes an ES module package: `"type": "module"`, `main` at `./dist/index.js`, types at `./dist/index.d.ts`, and `files` limited to `dist` so nothing from `test/` or `docker/` reaches npm. The `exports` map offers the default entry, a `./legacy` subpath resolved to `./dist/legacy.js`, and `./package.json`. Anything below the `>=20.0.0` engine floor is unsupported.

json
"type": "module",
"engines": {
    "node": ">=20.0.0"
},
"scripts": {
    "build": "tsdown && npm run build:copy-package",
    "test": "vitest run",
    "test:routes": "vitest run test/routes"
}

The `build` script runs `tsdown`, and `build:copy-package` then puts package.json into the published `dist` folder. Tests run through `vitest`, and `test:routes` targets `test/routes`, which is where endpoint behaviour is checked. At the root you also find `.babelrc` beside `tsconfig.json` and `tsdown.config.ts`, two lockfiles in `package-lock.json` and `pnpm-lock.yaml`, a `pnpm-workspace.yaml`, a `BREAKING.md` and a `docker/` directory. The table of contents carries a `Run with Docker` entry, but no image tag, compose file or port is named in the README text or in package.json, so that setup cannot be rebuilt from what is published.

The default branch is `ts-rewrite` and the last push landed on 2026-09-22, with the recorded versions running v1.2.3 from 2025-01-28, v2.3.0-beta1 from 2026-05-01 and v2.5.0 from 2026-09-16. The package is published under AGPL-3.0, and since `files` ships only `dist`, an application that wraps it consumes compiled output under that licence.

Four language ports and one hosted API sit beside it

The serious alternative is not a rival library but the service this one points at. A note near the top states that the project is not a production-ready API, that it is a reverse engineering project, and that the Euler Stream WebSocket API is the route for production. The difference in approach is structural: the hosted API hands signing, protocol upkeep and uptime to a vendor, while this library leaves all three inside your process and inside the code that must change when TikTok's internals shift.

The npm metadata tells a different story. The `description` field in package.json calls it the number one library for real-time chat, gift, like, follow and viewer events from any TikTok livestream, while a note in the same repository calls it unfit for production. Both travel to npm together.

Four rewrites carry the same event model elsewhere: `TikTokLive` for Python by isaackogan and eulerstream, `TikTokLiveJava` for Java by jwdeveloper and kohlerpop1, `GoTikTokLive` for Go by steampoweredtaco and davincible, and `TikTokLiveSharp` for C# by frankvHoof93. Each is a port, not an escape from the dependency, since none of them removes the sign server from the path. The documented surface is receive only, and no send, reply or follow method appears among the options or the events.

Editorial conclusion

Adopt this library when your own tooling has to react to a live room in real time and a reverse engineered path is acceptable: install tiktok-live-connector, pass a username, supply a signApiKey. Keep it out of anything user facing or uptime critical, since the README calls it a reverse engineering project while the npm description promises a complete library with no login step. Before shipping, read authenticateWs twice, because turning it on forwards your session cookies and OAuth token to the signing service.

Frequently asked questions

Does TikTok-Live-Connector need a TikTok login to read a live chat?

No. The constructor takes the broadcaster's uniqueId and nothing else is mandatory, and the stated default is no login, credentials or app registration. Credentials only enter the picture when you set authenticateWs or useMobile.

Why does connecting TikTok-Live-Connector with useMobile throw a TypeError?

The mobile WebSocket flow requires authenticateWs: true and session.cookie. Connecting with useMobile while authenticateWs is false throws a TypeError before any network call.

What happens in TikTok-Live-Connector when the streamer is not live?

With fetchRoomInfoOnConnect at its default of true, connect rejects with UserOfflineError because room info is fetched during the connection attempt. Setting that option to false removes the early check.

How do I get gift names and costs from TikTok-Live-Connector instead of bare gift ids?

Set enableExtendedGiftInfo to true. The room gift list is then fetched during connect, and WebcastGiftMessage events carry an extendedGiftInfo field holding the name, cost and image data.

Which Node version does tiktok-live-connector support?

package.json declares an engine floor of >=20.0.0. The package is published as an ES module with type module, main at ./dist/index.js and a separate ./legacy subpath.

Can TikTok-Live-Connector be used in production?

A note in the README states that it is not a production-ready API, describes it as a reverse engineering project, and points at the Euler Stream WebSocket API for production use.

Official sources

  1. License: AGPL-3.0
  2. Project website
  3. README
  4. Releases
  5. zerodytrash/TikTok-Live-Connector on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/zerodytrash-tiktok-live-connector.svg)](https://hysenlabs.com/projects/zerodytrash-tiktok-live-connector)

Was this useful?