API documentation
The Bearer token from the cabinet uses the same account data: library, watch, AI and keywords, plus metadata and comment collection.
Create a tokenAuthorisation
Send the token in the Authorization: Bearer sk_live_… header. Do not put it in the URL — it would end up in server logs and browser history.
Tokens are created in the workspace after the email is confirmed, up to 5 active per account. The full value is shown once — keep it on your server only and revoke it if it leaks. The same token as the cabinet covers the scopes below: metadata:read, analyze:write, library:write, ai:write, keywords:write. Send the token only in the Authorization header. An analysis request spends comments from the balance (otherwise 402 quota_exceeded). CORS: Bearer works from any origin without cookies; a cookie session is limited to our domains and needs CSRF.
| Scope | Access |
|---|---|
metadata:read | Free post metadata |
analyze:write | Comment collection jobs |
library:write | Saved posts, watch, refresh |
ai:write | Quote, run, watch phrases |
keywords:write | Keywords, channels, alerts |
Sandbox — no token needed
POST /api/v1/sandbox/metadata and POST /api/v1/sandbox/analyze return fixtures shaped exactly like the live endpoints: no TikTok, no quota, no token. The IP limit is 5 requests per minute and 30 per hour.
# metadata fixture (no token)
curl -X POST https://api.example.com/api/v1/sandbox/metadata \
-H "Content-Type: application/json" \
-d '{"url": "https://www.tiktok.com/@user/video/7300000000000000000"}'
# full analysis fixture (status: complete right away)
curl -X POST https://api.example.com/api/v1/sandbox/analyze \
-H "Content-Type: application/json" \
-d '{"url": "https://www.tiktok.com/@user/video/7300000000000000000"}'POST /api/v1/metadata — free
A synchronous response: author, metrics, hashtags, sound, cover and description keywords. No comments here. With a token or session the limit is 10 requests per 2 hours per account. From the website without signing in: 10 requests per day (browser only). A failed lookup does not spend the limit. Responses are cached for 5 minutes by URL, and a cache hit ("cached": true) does not spend the limit. Curl and your own clients need a Bearer token.
curl -X POST https://api.example.com/api/v1/metadata \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{"url": "https://www.tiktok.com/@user/video/7300000000000000000"}'POST /api/v1/analyze — paid from the comment balance
Asynchronous: returns a job_id with status queued, and the result is fetched with GET /api/v1/analyze/{job_id}. Poll once every 1–2 seconds; statuses are queued, in_progress, complete and failed. Someone else's job returns 404. In comments, replies are already nested inside their parent as replies. A saved breakdown and comment media files last 14 days from the last full comment scrape; opening a bookmark does not extend that.
# create a job
curl -X POST https://api.example.com/api/v1/analyze \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{"url": "https://www.tiktok.com/@user/video/7300000000000000000"}'
# response: {"job_id": "b1e2...", "status": "queued"}
# fetch the result
curl https://api.example.com/api/v1/analyze/b1e2... \
-H "Authorization: Bearer sk_live_..."Repeating the same URL
The same URL is never parsed twice. If a job is still running you get its job_id back ("deduplicated": "in_flight"). If a result arrived less than 5 minutes ago, the finished job is returned ("deduplicated": "recent_result"). Neither case spends quota.
Idempotency-Key
Optional. If you send an Idempotency-Key header with a unique value, a repeat with the same key within 24 hours returns the same job_id instead of a new job. Ordinary requests do not need this header.
Library and watch
POST /api/v1/analyses/{ref}/save bookmarks a post. List with GET /api/v1/analyses/saved, load GET /api/v1/analyses/{ref}, remove with DELETE /api/v1/analyses/{ref}. Turn on auto-refresh with PUT /api/v1/analyses/{ref}/watch and run a full scrape with POST /api/v1/analyses/{ref}/refresh. The latest browser session is GET /api/v1/analyses/session.
A token analyze auto-saves with bookmark_if_absent: the post is already in the library when the job completes. Polling GET /api/v1/analyze/{job_id} does not extend the 14-day TTL.
From analyze to the library
With a token the finished job is already bookmarked. Then you can watch the post or run AI — the same analysis_id is the ref.
# create a job
curl -X POST https://api.example.com/api/v1/analyze \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{"url": "https://www.tiktok.com/@user/video/7300000000000000000"}'
# fetch the result
curl https://api.example.com/api/v1/analyze/b1e2... \
-H "Authorization: Bearer sk_live_..."
# complete job — analysis_id is already in the library: {"status":"complete","result":{"analysis_id":"tiktok:7300..."}}
# optional: auto-refresh
curl -X PUT https://api.example.com/api/v1/analyses/tiktok:7300.../watch \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{"enabled": true, "interval_min": 60}'
# optional: AI quote (then POST /ai/run)
curl -X POST https://api.example.com/api/v1/ai/quote \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{"ref": "tiktok:7300..."}'AI
POST /api/v1/ai/quote returns the charge, POST /api/v1/ai/run starts a job, poll GET /api/v1/ai/jobs/{id}. Store watch phrases with PUT /api/v1/ai/{ref}/queries (up to three). Calling these with a token is consent to send comment texts to the model.
Keywords and alerts
GET and PUT /api/v1/keywords manage the account word list. Configure Telegram and HTTPS callbacks with GET / PUT /api/v1/notification-channels. Poll matches with GET /api/v1/keyword-alerts.
Job webhook
If job_events is on for the webhook channel (off by default), we POST when a job finishes: type is job.complete or job.failed, kind is analyze or ai, plus job_id, ref, url and error. Sign the body with HMAC in X-Signature as for keyword alerts.
Endpoints
| Method | Path | What |
|---|---|---|
POST | /api/v1/metadata | Free post metadata |
POST | /api/v1/analyze | Start comment collection |
GET | /api/v1/analyze/{job_id} | Poll the analyze job |
POST | /api/v1/sandbox/metadata | Metadata fixture |
POST | /api/v1/sandbox/analyze | Analyze fixture |
GET | /api/v1/usage | Limits and comment balance |
GET | /api/v1/media/file | Proxied cover and images |
GET | /api/v1/media/download | Download media as a file |
POST | /api/v1/analyses/{ref}/save | Bookmark in the library |
GET | /api/v1/analyses/saved | List saved posts |
GET | /api/v1/analyses/{ref} | Load a saved analysis |
DELETE | /api/v1/analyses/{ref} | Remove from the library |
PUT | /api/v1/analyses/{ref}/watch | Auto-refresh |
POST | /api/v1/analyses/{ref}/refresh | Full re-scrape now |
GET | /api/v1/analyses/session | Latest session analysis |
POST | /api/v1/analyses/save-metadata | Save metadata only |
POST | /api/v1/ai/quote | AI charge quote |
POST | /api/v1/ai/run | Start AI job |
GET | /api/v1/ai/jobs/{id} | Poll the AI job |
PUT | /api/v1/ai/{ref}/queries | Watch phrases |
GET, PUT | /api/v1/keywords | Keyword list |
GET, PUT | /api/v1/notification-channels | Telegram / webhook |
GET | /api/v1/keyword-alerts | Keyword matches |
Limits and quotas
| Limit | Value |
|---|---|
| Metadata | site: 10/day; token: 10 / 2 hours |
| Comments | by purchased packs (balance for the calendar month) |
| Comments per post | up to 5,000 |
| Concurrent analyses | 3 |
Responses carry X-RateLimit-Limit, X-RateLimit-Remaining, X-Quota-Limit and X-Quota-Remaining, plus Retry-After in seconds on a 429. The current state is also available from GET /api/v1/usage.
When the comment balance runs out, analysis answers 402 quota_exceeded: buy a pack on the comments page and it is added to the current month. The free comment limit after signup works only in the workspace: with a token, analysis always comes from a pack.
Errors
Error body: {"detail": "...", "code": "machine_code"}
| HTTP | code | When |
|---|---|---|
| 401 | unauthorized | The token is missing, revoked or invalid |
| 403 | forbidden_scope | The token lacks the required scope |
| 402 | quota_exceeded | No comments available — buy a pack |
| 402 | payment_required | A paid feature (watch, AI, keywords) needs a comment pack, or comments were not collected for this post yet |
| 429 | rate_limited | Request limit exceeded, see the Retry-After header |
| 429 | concurrency_limit | Three analyses are already running at once |
| 400 | validation_error | Invalid URL or unsupported platform |
| 422 | audience_restricted | The author restricted the post audience |
| 502 | parse_error | Could not fetch the post data |
| 404 | not_found | The job or analysis does not exist or belongs to another account |