@toktikhq/sdk-js
v0.3.0
Published
Typed JavaScript client for the TokTik Developer API (REST + realtime LIVE events)
Maintainers
Readme
TokTik JavaScript SDK
Typed client for the TokTik Developer API — REST data-plane + realtime LIVE event stream.
npm i @toktikhq/sdk-jsQuickstart
import { TokTikClient } from "@toktikhq/sdk-js";
const client = new TokTikClient({ apiKey: process.env.TOKTIK_API_KEY! });
// REST — provenance-enveloped responses expose `{ data, provenance }`, unchanged.
const creators = await client.creators.list({ q: "cooking", limit: 10 });
console.log(creators.provenance.freshness, creators.data.results);
const board = await client.rankings.official({ board: "hourly", region: "VN" });
console.log(board.provenance.freshness, board.data.board.entries);
// Realtime — three lines to a live event stream.
const stream = await client.live.stream(["@some.creator"], {
onEvent: (frame) => console.log(frame.event, frame.data),
onStatus: (frame) => console.log(frame.creatorId, "→", frame.status)
});The client stays thin: it sends your bearer key only to the configured API origin and leaves REST retries, caching, and key storage to the calling application. (The realtime stream is the one exception — reconnection there is not optional, see below.)
Provenance is part of the answer
Most data methods return { data, provenance } and the SDK never strips the envelope. Much of this
data is observed, not officially published, so freshness (near_realtime / stale /
historical), source and coverageStatus tell you how much to trust a given answer.
A few shapes deliberately differ, matching the server: exports return the job / job list directly,
account.usage() returns its own { period, balance, items } shape, and exports.download()
returns the CSV as a string, not JSON. Everything else is enveloped.
Realtime
client.live.stream(creatorIds, handlers) handles the parts that are easy to get wrong:
- Token lifetime — mints a short-lived handshake token per connection (via
POST /v1/live/stream/token), so an expiry mid-stream costs a reconnect, not a dead socket. - Credential transport — the token travels in the
Sec-WebSocket-Protocolsubprotocol, never the URL, where it would leak into access logs and browser history. - Reconnect — exponential backoff with full jitter and a bounded ceiling, so a fleet does not stampede a restarted gateway.
- Resume — the gateway keeps no per-connection memory, so the SDK re-subscribes everything after a reconnect. Subscribe/unsubscribe issued while disconnected is replayed on the next open.
const stream = await client.live.stream(["creator.one"], {
onEvent: (f) => console.log(f.event, f.sequence, f.data),
onStatus: (f) => console.log(f.status, f.reason), // queued | active | offline | unavailable
onError: (e) => console.error(e),
onReconnect: () => console.log("resumed")
});
stream.subscribe("creator.two");
stream.unsubscribe("creator.one");
stream.close(); // final — stops reconnectingStatuses are honest: a creator whose room is still being admitted/acquired reports queued, not
active. active means events are actually flowing.
Node
Node 22+ has a global WebSocket, so nothing extra is needed. On older runtimes pass a factory:
import WebSocket from "ws";
client.live.stream(ids, handlers, {
socketFactory: (url, token) => new WebSocket(url, ["bearer", token]) as any
});The two subprotocol values must be the literal bearer followed by the token — the gateway selects
and echoes the marker, and a client that offers subprotocols closes the socket if the server selects
none.
Errors
Failures throw TokTikApiError, which keeps the status distinguishable rather than collapsing it:
try {
await client.creators.get("someone");
} catch (error) {
if (error instanceof TokTikApiError) {
error.isUnauthorized; // 401 — bad or expired key
error.isForbidden; // 403 — key lacks the scope this resource is sold under
error.isPaymentRequired; // 402 — out of credits; retrying will not help
error.isRateLimited; // 429
error.retryable; // 429 + 5xx only
error.requestId; // quote this to support
}
}Coverage
| Namespace | Scope | Endpoints |
|---|---|---|
| client.live | live:read, live:stream | creator performance, stream token, stream() |
| client.rankings | rank:read | official({ board }), movers({ board }), history({ board }), regions(), games({ region }) |
| client.creators | creator:read | list, get, changes, analysis, following, followers |
| client.content | content:read | creator videos (no params), video, video comments |
| client.gifters | gifter:read | list, get, for creator |
| client.trends | trend:read | list |
| client.exports | export | list, create, get, download |
| client.account | keys:manage | entitlements, usage |
Option names mirror the server schemas exactly. Those routes declare additionalProperties: false,
so an invented parameter is a hard 400 — not an ignored extra.
The pre-D3.1 method listCreatorPerformance still works and is marked deprecated; it forwards to
client.live.creatorPerformance.
0.3.0 (breaking): live.listSessions, live.getSession and the deprecated listLiveSessions /
getLiveSession were removed — GET /v1/live/sessions is no longer part of the API. exports.create now
takes the full export request: rankings (board + region), gifters (rangeDays, optional region /
segment), creator_roster, creator_gifters (creatorUniqueId, optional window); live_sessions is gone.
Publishing
The npm artifact is self-contained: @v2/contracts is a build-only workspace dependency and the
public declaration graph is bundled into dist/index.d.ts. npm run test:package imports the built
ESM entry point and type-checks a clean consumer whose only dependency is this SDK.
Releases are tag-driven through .github/workflows/sdk-release.yml:
git tag sdk-js-v0.3.0
git push origin sdk-js-v0.3.0The npm package must have a Trusted Publisher configured for this repository and that workflow. The
package remains ESM-only ("type": "module"); a dual ESM/CJS build is not currently provided.
Known scope limits
- Alert-rule (
webhook:manage) and API-key (keys:manage) management endpoints are sellable but intentionally out of scope here; onlyusage/entitlementsare exposed.
