Public TikTok data over a small, predictable HTTP API. **Every response uses the same envelope**, success or failure: ```json { "success": true, "data": {}, "cursor": null, "hasMore": false } ``` A failure keeps the shape and adds `error` with a stable `code` you can branch on. Codes never change meaning; new ones may be added. ```json { "success": false, "data": null, "cursor": null, "hasMore": false, "error": { "code": "not_found", "message": "…", "details": {} } } ``` **Error codes** | HTTP | `error.code` | Meaning | |---|---|---| | 400 | `invalid_request` | A parameter failed validation; `details.fields` names each one | | 400 | `unsupported_option` | The request asked for something this endpoint cannot serve | | 403 | `private_account` | The account is private | | 404 | `not_found`, `user_not_found`, `video_not_found` | TikTok has nothing by that id or handle | | 405 | `method_not_allowed` | The path exists, the method does not | | 429 | `quota_exceeded` | Your allowance is used up; retry later | | 429 | `rate_limited` | TikTok is throttling; retry later | | 502 | `upstream_unavailable`, `parse_error`, `listing_unavailable` | TikTok answered with something unusable, or refused | | 503 | `upstream_recovering`, `challenge_detected` | TikTok is temporarily unavailable; retry later | | 504 | `deadline_exceeded` | The request timed out | | 500 | `internal_error` | Something on our side; the `message` says what | **About the numbers.** TikTok rounds a video's play and like counts to three significant digits on listings, at any size - 11,381 plays are published as 11,400 - so listings report `statsPrecision: approximate`. A profile's own follower and like counts are exact, and so are the counts the music and effect endpoints report. Comment, share and save counts arrive exact everywhere. Every response says which it carries in `statsPrecision` rather than passing a rounded number off as real. **Pagination.** `cursor` and `hasMore` are present for shape consistency. Where TikTok exposes no pagination, `hasMore` is always `false` and the response says what the ceiling is - it will not invite you to page through nothing.