Cập nhật lần cuối
Errors
Every non-2xx response is a JSON envelope with the same four fields. Branch on code, show message to humans, log requestId, and retry only when retryable is true.
Tài liệu kỹ thuật hiện chỉ có bằng tiếng Anh.
The envelope
429 Too Many Requests
{ "code": "rate_limit_exceeded", "message": "API key rate limit exceeded.", "requestId": "3f2b6c1e-8a4d-4f0e-9b7a-2d5c1e6f8a90", "retryable": true}| Field | Type | Meaning |
|---|---|---|
code | string | Stable, machine-readable identifier in snake_case. Branch on this, never on message. |
message | string | Human-readable explanation in English. Safe to show to an operator; wording may change. |
requestId | string | UUID of the request, generated by the server. Include it when you contact support; it is the only way we can find the request in our logs. |
retryable | boolean | true when the same request may succeed later without changes (rate limits, transient failures). Everything else needs a different request. |
Status codes still matter
The HTTP status is the coarse class (4xx yours, 5xx ours);
code is the fine reason. A client that only looks at the status loses the distinction between a missing scope and a plan that lacks the module (both 403).Handling errors
The pattern below is what the RoomScope dashboard itself uses: parse the envelope, fall back to http_error when the body is not JSON, and retry with exponential backoff only when the server marks the error retryable.
class ApiError extends Error { constructor(status, { code, message, requestId, retryable }) { super(message); Object.assign(this, { status, code, requestId, retryable: Boolean(retryable) }); }} async function call(path, init) { const res = await fetch(`https://api.toktokdata.live${path}`, init); if (res.ok) return res.status === 204 ? undefined : res.json(); let envelope = {}; try { envelope = await res.json(); } catch {} throw new ApiError(res.status, { code: "http_error", message: `Request failed (${res.status})`, ...envelope });} // Retry only when the server says so.async function withRetry(fn, attempts = 3) { for (let i = 0; ; i++) { try { return await fn(); } catch (e) { if (!(e instanceof ApiError) || !e.retryable || i === attempts - 1) throw e; await new Promise((r) => setTimeout(r, 500 * 2 ** i)); } }}Codes by status
Codes are stable; new ones may be added, so treat an unknown code by its status. Each endpoint in the reference lists the codes it can return.
| Status | Code | When | Retryable |
|---|---|---|---|
| 400 | validation_error | The body or a parameter is malformed. The message names the field. | no |
| 400 | invalid_rankings_input | Rankings: board missing, a value over 128 characters, or a bad limit, leagueTier, period or liveOnly. | no |
| 400 | invalid_cursor · invalid_region · invalid_trend_type | A cursor, region (two letters on insights) or trend type the endpoint does not accept. | no |
| 400 | invalid_live_history_input · invalid_alert_input · invalid_dataset · invalid_format | Endpoint-specific input errors; the message says what to fix. | no |
| 401 | unauthorized | Missing, malformed, unknown, revoked or expired key, or an inactive workspace. Sent with WWW-Authenticate: Bearer. | no |
| 402 | credit_cap_exceeded | The request would take the period’s spending past the credit cap. | no |
| 403 | insufficient_scope | The key lacks the scope, or the route is dashboard-only. | no |
| 403 | module_not_entitled | The workspace’s plan does not include the Live or Lookup module. | no |
| 403 | export_format_not_entitled | The plan does not include that export format. | no |
| 404 | not_found | No route matches the method and path. | no |
| 404 | creator_not_found · video_not_found · live_session_not_found · alert_not_found · export_not_found | The resource does not exist or belongs to another workspace. | no |
| 409 | export_not_ready · api_key_limit_reached | The export is still building, or the plan’s key limit is reached. | no |
| 429 | rate_limit_exceeded | The key went over its requests per second. Honour Retry-After. | yes |
| 429 | daily_quota_exceeded | The key used its daily quota. Retry-After counts to midnight UTC. | yes |
| 503 | rate_limit_unavailable · metering_unavailable · webhook_unavailable | A dependency is down. Retry with backoff. | yes |
| 500 | internal_error | Something failed on our side. Retry with backoff; include the requestId when you report it. | yes |
Not errors
- An unknown or uncovered
regionon rankings is not an error: the answer is200withpublishable: false, an emptyentrieslist and aprovenancethat says why. - Following, followers and comments that are being collected answer
202withstatus: "collecting", ajobIdand aRetry-Afterheader. Call again after that many seconds.
Rate limits have their own page: Rate limits.
Trang này có hữu ích không?