Skip to main content

toktik — Python SDK for the TokTik Developer API

Typed Python client for the TokTik Developer API: the full REST data-plane plus a realtime (asyncio) LIVE-event client. Feature parity with @toktikhq/sdk-js; the contract source of truth is @v2/contracts / the served GET /openapi.json.

from toktik import TokTikClient

client = TokTikClient(api_key="ttk_live_...")
board = client.rankings.official(board="hourly", region="VN")
print(board["provenance"])          # the envelope is never stripped
import asyncio
from toktik import TokTikClient, EventFrame

async def main():
    client = TokTikClient(api_key="ttk_live_...")
    async for frame in await client.live.stream(["@creator"]):
        if isinstance(frame, EventFrame):
            print(frame.event, frame.data)          # chat / gift / like / ...

asyncio.run(main())

Install

pip install toktik                # REST only — zero dependencies
pip install "toktik[realtime]"    # adds `websockets` for the realtime client

The REST client speaks HTTP through the standard library, so the data-plane surface pulls in nothing. The realtime client needs a WebSocket implementation; installing the realtime extra provides it, or you can pass your own transport (RealtimeStream(connect=...)).

Design

  • Provenance is never stripped. Data methods return the parsed JSON envelope unchanged — {"data": ..., "provenance": {...}}. For observed (rather than officially published) data, that context is part of the answer. The two documented exceptions match the JS SDK: exports.download returns CSV text, and account.usage is returned directly (no envelope).
  • Errors are surfaced, not swallowed. A non-2xx raises TokTikApiError carrying status, code, request_id and helpers (is_payment_required for 402, is_forbidden for 403, is_rate_limited for 429, retryable). Distinguishing them is the whole point: 402 means buy credits, 403 means the key lacks the scope, 429 means back off.
  • Argument names are snake_case, mapped to the server's wire names. Every route's Fastify schema sets additionalProperties: false, so a wrong query key is a hard 400. The mapping is transcribed from the routes, and a CI parity test asserts every documented data-plane path is covered.
  • The realtime client owns token lifetime, reconnection and resume. A fresh handshake token is minted per connection (expiry becomes a reconnect, not a dead socket); reconnects use exponential backoff with full jitter; and because the gateway keeps no per-connection memory, subscriptions are re-sent on every open. queued / active / offline / unavailable are reported honestly.

Pending (202) answers

A creator we hold nothing fresh for is resolved from TikTok on demand, so creators.get, content.creator_stories, creators.following / followers, content.video_comments and live.playback can answer 202 "not ready yet": a plain dict {"status": "resolving" | "collecting", "retryAfterSeconds": N, ...} with no data key. It is a success, not an error (no exception), and it is never billed. The SAME call returns the real envelope once the read lands. While TikTok's read is failing the server holds it for up to 120 s per call.

from toktik import TokTikPendingTimeout, is_pending, wait_until_ready

result = client.creators.get("someone")
if is_pending(result):
    print("check again in", result["retryAfterSeconds"], "s")

# Or let the SDK poll: honours retryAfterSeconds (clamped to 1–120 s), gives up after 3 minutes.
try:
    profile = wait_until_ready(lambda: client.creators.get("someone"))
    print(profile["data"]["creator"]["followerCount"])
except TokTikPendingTimeout as err:
    print("still not ready; the server suggested", err.retry_after_seconds, "s")

A non-2xx answer inside wait_until_ready still raises TokTikApiError unchanged. After a TokTikPendingTimeout, calling the method again later is safe — but a manual retry that lands the read may be billed like any delivered read.

REST surface

Namespace Methods Scope
client.rankings official(board, region, limit), movers(board, region, limit), history(board, region, limit, cursor, league_tier), regions(), games(region) rank:read
client.live_pro directory(region, live_only, limit), regions() — the LIVE Pro creator directory live-pro:read
client.recruit list(region, exclude_live_pro, new_only, min_followers, max_coins_30d, min_score_today, sort, limit, cursor), mark(creator_uid, status, note), unmark(creator_uid), marks(status, limit, cursor) rank:read (pool), watchlist:manage (marks)
client.live creator_performance, stream_token, stream live:read / live:stream
client.creators list, get, changes, analysis, following, followers creator:read
client.content creator_videos, video, video_comments content:read
client.gifters list, get, for_creator gifter:read
client.agencies list(region, q, service_type, public_profile_only, sort, limit, offset), get(agency_id), regions() — contact details only on the Agency plan agencies:read
client.trends list(region, type) trend:read
client.exports list, create, get, download export
client.account entitlements, usage keys:manage

Realtime

await client.live.stream(creator_ids) returns a connected RealtimeStream — an async iterator of frames:

  • StatusFrame(creator_id, status, room_id, reason) — subscription lifecycle (queued→active→…, or offline).
  • EventFrame(event, event_id, creator_id, room_id, sequence, room_state, data, provenance) — a LIVE event (chat, gift, like, member, roomUser, social, control, envelope, goodyBag, unknown).
  • ErrorFrame(code, message, retryable) — a server-side subscription error.

subscribe(id) / unsubscribe(id) change the set mid-stream; aclose() (or async with) stops for good and cancels reconnection.

The token is minted from platform-api; its wsUrl points at the API host, which nginx routes to the gateway in production. Driving a local gateway directly, mint the token yourself and pass the gateway URL — see examples/realtime_demo.py.

Development

python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"
python -m unittest discover -s tests    # 63 tests, no network
mypy && ruff check src tests

The OpenAPI parity snapshot is generated from @v2/contracts (regenerated + diffed in CI):

npm run build --workspace @v2/contracts
node packages/sdk-python/scripts/gen_openapi_snapshot.mjs

0.4.0

Adds is_pending(), wait_until_ready() and TokTikPendingTimeout for the 202 "not ready yet" answer (CR.9; see "Pending (202) answers"). Additive, no breaking change: the REST methods still return the raw dict.

0.3.1

recruit.list(country=…) and the recruit export accept country (the creator's RESOLVED country — what a Recruit country card groups by; region stays the board bucket). Additive, no breaking change.

0.3.0

Adds client.live_pro (directory, regions — LP.4/LP.7) and client.recruit (list, mark, unmark, marks — the recruiting pool + your workspace's marks, EC.1/EC.2); exports.create takes the new recruit dataset with its filters (region, exclude_live_pro, new_only, min_followers, max_coins_30d). Additive, no breaking change.

0.2.0 (breaking)

live.list_sessions and live.get_session were removed — GET /v1/live/sessions answers 410 endpoint_removed since 2026-09-24. exports.create now requires dataset and takes every real export: rankings (board, region, optional game / period), gifters (range_days, optional region / segment), creator_roster, and creator_gifters (creator_unique_id, optional window). live_sessions is no longer a dataset. The agency directory is not exportable (dataset="agencies" was removed in EX.7 and answers 400). A job's filename names its main scope and the UTC minute: toktik-<dataset>[-<scope>]-<YYYYMMDD>-<HHmm>.<format>.

Publishing

Releases are tag-driven through .github/workflows/sdk-release.yml:

git tag sdk-python-v0.4.0
git push origin sdk-python-v0.4.0

The release job builds and checks both the sdist and wheel, installs the wheel into a clean virtual environment, and publishes with PyPI Trusted Publishing. The PyPI project must trust this repository, that workflow, and the pypi GitHub environment before the first release.

Known scope limits

  • Response bodies are typed dict (JsonDict), not generated models. The shapes live in @v2/contracts (TypeScript); generating Pydantic models from them was deliberately deferred to avoid silent drift. Method arguments and realtime frames are typed.
  • Sync REST only. An asyncio REST client is not yet provided; the realtime client is async, and it mints its token off the event loop via asyncio.to_thread.

Metadata

Release files for toktik 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for toktik 0.4.0
File Size Uploaded Provenance Actions
toktik-0.4.0.tar.gz 38.6 kB With provenance Details for toktik-0.4.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for toktik 0.4.0
File Interpreter ABI Platform Actions
toktik-0.4.0-py3-none-any.whl Python 3 none any Details for toktik-0.4.0-py3-none-any.whl

Total release size: 65.3 kB

Release files / toktik-0.4.0.tar.gz

Download URL toktik-0.4.0.tar.gz
Size 38.6 kB
Tags Source
SHA-256 checksum
How to use checksums
1e6cde8de93657bf5cdda89f5dcaefeab6264e1cebc4c608891e276e93e304b4
BLAKE2b-256 checksum
How to use checksums
5cfa944455ecad32505a0b689b1ef796d1ff21817d0489fd6377c2029125dcf7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / toktik-0.4.0-py3-none-any.whl

Download URL toktik-0.4.0-py3-none-any.whl
Size 26.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
30bc53603b6b84d18f08054f328fd8ed0e761ba5112d0adac286ad85e596e26a
BLAKE2b-256 checksum
How to use checksums
b72c457eda03a4e9066c0749ea5d8d3a35b4bfa33fe97caf31744f9c01e753b8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Security for toktik 0.4.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for toktik-0.4.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page