npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

tiktok-live-events

v1.2.3

Published

TikTok LIVE event stream for Node.js + TypeScript. Read chat, gifts, viewers, follows, PK battles, AI captions, moderation deletes and 50+ real-time event types from any TikTok LIVE stream over a single WebSocket. 2026 edition.

Downloads

0

Readme

tiktok-live-events

The 2026 TikTok LIVE event stream for Node.js + TypeScript.

Read chat, gifts, viewers, follows, PK battles, AI captions, polls, karaoke, pictionary, live shopping, moderation deletes and 80+ real-time event types from any TikTok LIVE stream in 4 lines of code.

npm downloads types license


Three ways to use it

1. One-click (Windows)

Download start.bat, double-click. It installs the package + prompts for a username. Streams every chat, gift, like, follow in real-time to the console.

2. One-click (macOS / Linux)

curl -fsSL https://raw.githubusercontent.com/tiktool/tiktok-live-events/main/start.sh | bash

3. CLI

npm i -g tiktok-live-events
tiktok-live-events streamer_username
[ready]   connected to @streamer (room 7648...)
[chat]    fan123: love this!
[gift]    bigtipper -> Rose x99
[like]    casual_viewer (15)
[follow]  new_follower

4. Programmatic

import { TikTokLive } from 'tiktok-live-events';

const live = new TikTokLive('streamer_username');
live.on('chat', e => console.log(`${e.user.uniqueId}: ${e.comment}`));
live.on('gift', e => console.log(`${e.user.uniqueId} sent ${e.giftName} x${e.repeatCount}`));
await live.connect();

No key. No config. Just run it.


What you get

  • Real-time chat, gifts, likes, follows, viewer counts, PK battles, AI captions, gift catalog updates, moderation deletes, viewer entry-source analytics and 50+ other live event types.
  • One WebSocket. Zero protocol code. No protobuf libraries. No proxy setup. No headless browser.
  • Full TypeScript types for every event. Your IDE autocompletes every field.
  • Auto-reconnect, structured error handling, typed emitter.
  • Tiny. Under 5 KB compiled, one runtime dependency (ws).

The protocol decode happens on the TikTools edge. Your code only ever sees clean JSON.


How it works (free / sandbox / paid)

The SDK has two connect strategies and picks one automatically based on your tier.

Direct mode (default for free + sandbox)

Your machine opens the WebSocket to TikTok from your own IP. Our edge only:

  1. Signs the URL + mints you a fresh session cookie.
  2. Receives raw frames over a side channel and returns parsed JSON.

You never run a protobuf library, you never set up a proxy, you never run a headless browser. But TikTok sees your real residential IP - geo is correct, your fingerprint is organic, and you don't share our session pool. Captcha + cluster-ban risk lands on your IP.

Managed mode (default for paid tiers)

You open ONE WebSocket to wss://api.tik.tools. Our edge runs the upstream TikTok session via our residential proxy pool. Your IP never touches TikTok. Fan-out economics: many customers watching the same creator share one upstream connection.

Picking mode

| Tier | Default mode | Override | |---|---|---| | Anonymous (no key) | direct | { mode: 'managed' } | | Sandbox (free signup) | direct | { mode: 'managed' } | | Basic+ (paid) | managed | { mode: 'direct' } |

The default is mode: 'auto' - the SDK asks our edge which mode fits your tier and picks the right one. Force a mode explicitly when you want to override.

const live = new TikTokLive('streamer', { apiKey: '...', mode: 'direct' }); // force direct
const live = new TikTokLive('streamer', { apiKey: '...', mode: 'managed' }); // force managed
const live = new TikTokLive('streamer');                                     // auto (default)

Pricing tiers + current per-mode caps live on the pricing page.


CLI reference

tiktok-live-events <username> [options]

Options:
  -f, --filter <list>     Comma-separated event types (default: all)
                          e.g. chat,gift,follow,viewer,like
      --json              Emit each event as one JSON line (machine-readable)
  -h, --help              Show this help

Examples:
  tiktok-live-events streamer
  tiktok-live-events streamer --filter chat,gift
  tiktok-live-events streamer --json > events.ndjson

Install

npm install tiktok-live-events
# yarn / pnpm / bun
yarn  add tiktok-live-events
pnpm add tiktok-live-events
bun   add tiktok-live-events

Quick start (SDK)

import { TikTokLive } from 'tiktok-live-events';

const live = new TikTokLive('streamer_username');

live.on('connected', () => console.log('Connected.'));
live.on('roomInfo', (info) => console.log(`Watching ${info.roomId}`));

live.on('chat', (e) => console.log(`${e.user.uniqueId}: ${e.comment}`));
live.on('gift', (e) => console.log(`${e.user.uniqueId} sent ${e.giftName} x${e.repeatCount} (${e.diamondCount} diamonds)`));
live.on('like', (e) => console.log(`${e.likeCount} likes (total: ${e.totalLikes})`));
live.on('member', (e) => console.log(`${e.user.uniqueId} joined`));

await live.connect();

Events

Every event is dispatched by name via the typed emitter. live.on('chat', e => ...) is fully typed end-to-end. Each event payload extends BaseEvent (type, timestamp, msgId, optional protoVersion).

Core live events

| Event | What it carries | |---|---| | connected | Socket open. | | disconnected | Socket close. Includes the close code + reason. | | roomInfo | One-shot post-connect: { roomId, wsHost, clusterRegion, connectedAt }. | | chat | user, comment, emotes, optional starred. v3 adds language (auto-detected), messageUuid, replyToUser (~8% of chats are replies). | | gift | giftId, giftName, diamondCount, repeatCount, repeatEnd, giftType. v3 adds transactionId, senderUserId, relationship (joinDayNumber). | | like | likeCount (this batch), totalLikes (room cumulative). | | member | Viewer joined. v3 adds entrySource ("homepage_hot-live_cell", "follow-tab", ...), entryAction ("draw"/"click"), entryType ("rec"). | | social | Follow / share. | | roomUserSeq | Periodic viewer count tick. | | subscribe | A viewer subscribed. |

PK / battle events

| Event | What it carries | |---|---| | battle | PK lifecycle. status (1=ACTIVE, 2=STARTING, 3=ENDED, 4=PREPARING), battleDuration, teams. v3 adds extraHostUserIds, layoutSubtype. | | battleArmies | Per-host MVP breakdown ticking through the PK. hosts[].contributors[] sorted MVP first. v3 adds transactionId. | | battleItemCard | Booster card: x2 / x3 multipliers, gloves (crit), mist, thunder, extra-time, match-guide. Carries TikTok CDN overlay assets. | | battlePunishFinish | Loser-side punishment screen ended. | | battleNotice, battleGameplay | PK notice + mini-game state. | | linkLayer, linkMicOpponentGift, linkScreenChange, cohostLayoutUpdate | Link-mic negotiation, opponent-side gifts, layout flips. | | competition, competitionContributor, guestShowdown | Cross-stream competitions + guest showdowns. |

Native captions (v3)

| Event | What it carries | |---|---| | caption | NEW in v3. TikTok native auto-captions ride on the LIVE WebSocket. text, language (auto-detected), isFinal, startedAtMs, endsAtMs. |

Creator + room

| Event | What it carries | |---|---| | goalUpdate | Stream goal progress (subscriber goal, gift goal, watch-time goal). | | commentTray, roomPin, roomSticker, inRoomBanner, bottomMessage | Room UI events. | | hostBoard, rankText, rankUpdate, hourlyRank | Leaderboard / rank events. | | privilegeAdvance, accessRecall, roomVerify | Viewer privilege + content-classification events. | | anchorToolModification, streamStatus, shareRevenueNotice | Creator-side metadata flips. | | capsule, hotRoom, linkMicAnchorGuide | TikTok host nudges. |

Moderation / safety

| Event | What it carries | |---|---| | imDelete | Chat moderation delete. Correlate via chat.messageUuid (v3). | | unauthorizedMember | Non-logged-in viewer hit a gated feature. | | barrage | Raw barrage feed. | | superFan, superFanJoin, superFanBox | Super-fan lifecycle. | | emoteChat | Inline emote message. |

Gift catalog + ecommerce

| Event | What it carries | |---|---| | giftPanelUpdate | Real-time gift catalog change. | | giftCollectionUpdate | Host curated gift set changed. v3 | | giftDynamicRestriction, giftGallery, giftUnlock, viewerPicksUpdate | Gift availability flips, host gift wall, gated-gift reveals, viewer-pick highlights. | | oecLiveShopping, oecLiveManager, oecLiveBillboard, ecShortItemRefresh | OEC live-shopping events. |

Engagement + AI

| Event | What it carries | |---|---| | aiSummary | TikTok AI summary of the room (entry-time recap, multi-language). | | poll, shortTouch | In-stream poll lifecycle. | | question, questionSelected, questionSlideDown | Q&A round events. | | pictionaryStart, pictionaryUpdate, pictionaryEnd, pictionaryExit | Drawing-game rounds. v3 | | karaokeReq | Viewer queued / requested a track on the host's karaoke widget. v3 | | subPin | Comment pinned by a paid subscriber via the sub-only pin slot. v3 | | toast, gapHighlightPushGuide | Generic toast popups and first-render UX hints. v3 | | gameAutoPostNotice | Notice posted automatically by an in-room mini-game. v3 | | cohostSettingsUpdate | Cohost settings updated (slot count, layout, permissions). v3 | | fansEvent, fanTicket | Fan-club events. | | envelope, envelopePortal | Red-envelope drops + multi-room portal chain. | | gameMoment, gameServerFeature | TikTok Gaming live integration. | | groupLiveMemberNotify | Group-live member join / leave. | | perception | Perception event (mute cancel, hint signal). | | control, room, liveIntro | Stream control + room metadata. |

Universal field: extras

Every event carries an optional extras: Record<string, ...> map containing any payload field TikTok ships that doesn't yet have a typed name. New fields appear automatically the day TikTok introduces them - no SDK upgrade required. Use it as a forward-compat hook:

live.on('chat', e => {
  if (e.extras?.['18']) console.log('chat flag 18:', e.extras['18']);
});

Catch-all

  • event - Fires once for every decoded event (dump-to-queue pattern).
  • unknown - Fires when TikTok ships a method not yet modelled (forward-compat hook).

Recipes

Chat logger

import { TikTokLive } from 'tiktok-live-events';

const live = new TikTokLive('creator');
live.on('chat', e => console.log(`[${new Date().toISOString()}] ${e.user.uniqueId}: ${e.comment}`));
await live.connect();

Gift leaderboard

import { TikTokLive } from 'tiktok-live-events';

const board = new Map<string, number>();
const live = new TikTokLive('creator');

live.on('gift', e => {
    if (!e.repeatEnd) return; // only count final combo
    const total = (board.get(e.user.uniqueId) || 0) + e.diamondCount * e.repeatCount;
    board.set(e.user.uniqueId, total);
});

setInterval(() => {
    const top = [...board.entries()].sort((a, b) => b[1] - a[1]).slice(0, 10);
    console.clear();
    top.forEach(([user, diamonds], i) => console.log(`${i + 1}. ${user} - ${diamonds} diamonds`));
}, 5_000);

await live.connect();

PK MVP tracker

import { TikTokLive } from 'tiktok-live-events';

const live = new TikTokLive('creator');

live.on('battleArmies', (e) => {
    for (const host of e.hosts ?? []) {
        const mvp = host.contributors[0];
        if (mvp) console.log(`Host ${host.hostUserId} MVP: ${mvp.nickname} (${mvp.score})`);
    }
});

live.on('battleItemCard', (e) => {
    if (e.multiplier > 0) console.log(`x${e.multiplier} booster from @${e.senderUniqueId}`);
});

await live.connect();

TikTok native captions on the stream

import { TikTokLive } from 'tiktok-live-events';
import { writeFileSync } from 'fs';

const live = new TikTokLive('creator');
let transcript = '';

live.on('caption', (e) => {
    if (e.isFinal) {
        transcript += e.text + '\n';
        writeFileSync('transcript.txt', transcript);
    }
});

await live.connect();

Creator viewer-acquisition funnel

import { TikTokLive } from 'tiktok-live-events';

const sources = new Map<string, number>();
const live = new TikTokLive('creator');

live.on('member', (e) => {
    if (!e.entrySource) return;
    sources.set(e.entrySource, (sources.get(e.entrySource) || 0) + 1);
});

setInterval(() => {
    console.log('Top viewer sources:', [...sources.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5));
}, 10_000);

await live.connect();

Moderation delete correlator

import { TikTokLive } from 'tiktok-live-events';

const live = new TikTokLive('creator');
const recent = new Map<string, { user: string; comment: string }>();

live.on('chat', e => { if (e.messageUuid) recent.set(e.messageUuid, { user: e.user.uniqueId, comment: e.comment }); });
live.on('imDelete', e => {
    const orig = recent.get(e.deletedMsgId);
    if (orig) console.log(`DELETED: ${orig.user}: ${orig.comment}`);
});

await live.connect();

More ready-to-run recipes in examples/.


TypeScript

Every event is fully typed. Use the exported interfaces directly:

import { TikTokLive, ChatEvent, GiftEvent, BattleArmiesEvent } from 'tiktok-live-events';

const live = new TikTokLive('creator');

live.on('chat', (e: ChatEvent) => {
    e.user.uniqueId;  // string
    e.comment;        // string
    e.language;       // string | undefined  (v3)
    e.messageUuid;    // string | undefined  (v3)
});

live.on('gift', (e: GiftEvent) => {
    e.giftName;       // string
    e.diamondCount;   // number
    e.transactionId;  // string | undefined  (v3 dedup key)
});

live.on('battleArmies', (e: BattleArmiesEvent) => {
    e.hosts?.forEach(h => {
        h.contributors[0]; // MVP, sorted highest first
    });
});

API reference

new TikTokLive(uniqueId, options?)

Construct a client. uniqueId is the TikTok @username (with or without @).

new TikTokLive('streamer');
new TikTokLive('streamer', { autoReconnect: true, maxReconnectAttempts: 5, debug: false });

| Option | Type | Default | Description | |---|---|---|---| | autoReconnect | boolean | true | Reconnect with exponential backoff if the socket drops. | | maxReconnectAttempts | number | 5 | Stop reconnecting after N attempts. | | debug | boolean | false | Verbose stdout logging. |

live.connect(): Promise<void>

Open the socket. Resolves on first open, rejects if the initial handshake fails.

live.disconnect(): void

Close the socket. Auto-reconnect will not fire.

live.destroy(): void

Tear down the client (subsequent connect() calls throw).

live.isConnected(): boolean

Whether the socket is currently open.

live.on(event, handler)

Strongly-typed event subscription. See the Events table for every emitted name.


Compatibility

  • Node.js >= 18 (uses native fetch + WebSocket upgrade dependency ws).
  • Works in Bun, Deno (via npm:), serverless (Vercel, Netlify, Cloudflare Workers via the WebSocket polyfill).
  • ESM + CommonJS dual published.

Powered by

This package connects to the TikTools edge. Schema, decoding, proxy rotation, signing, and protocol patches are handled server-side - your npm install never needs to bump when TikTok ships a wire change.


License

MIT

This is an independent third-party project. Not affiliated with, endorsed by, or in any way officially connected to TikTok or ByteDance Ltd. "TikTok" is a trademark of ByteDance Ltd; the name appears here for search discoverability.