Pagination
Three rules, and the third is the one that costs people an afternoon. Always page with the value the last response returned — never a number you derived from counting results, because count is a request and not a promise.
Two currencies
Some routes take offset and some take cursor. This is the platform's inconsistency, not ours, and we carry it out to you rather than smoothing it over: normalising to one spelling would mean the API said one thing while asking the platform another, and the first person to debug a paging problem would find the two disagreeing with no note saying why. Sending the wrong one is a 400 naming the field.
A search needs its search id
A search is a session, not a stateless query. Page two and later must carry the first page's x-tt-logid back in the body as search_id. Without it the request opens a brand new search and comes back with nothing — which reads exactly like a search that found nothing.
So we refuse it first, with 400 search_id_required, before it costs you anything. The same value appears in the body as log_pb.impr_id and extra.logid if the header is inconvenient to reach.
curl -X POST https://api.sorina.sh/v1/tiktok/search/videos \
-H "authorization: Bearer $SORINA_KEY" \
-H 'content-type: application/json' \
-d '{"keyword":"nasa","offset":0,"count":10}'
# < x-tt-logid: 20260818113045ABCDEF… keep this
# { "cursor": 10, "has_more": 1, … } and this curl -X POST https://api.sorina.sh/v1/tiktok/search/videos \
-H "authorization: Bearer $SORINA_KEY" \
-H 'content-type: application/json' \
-d '{"keyword":"nasa","offset":10,"count":10,
"search_id":"20260818113045ABCDEF…"}'{"error":"paging past the first page needs search_id: send back the previous
response's X-TT-Logid header (same value as log_pb.impr_id in the body).
Without it the request starts a new search and returns nothing.",
"code":"search_id_required"}The all-results tab does not page
/search accepts offset: 0 and nothing else; anything higher is 400 offset_unsupported. It starts a search rather than continuing one, and its cursor tracks the count you asked for rather than the number of cards it returned, so it is not a page pointer either. Narrow to a vertical to go deeper.
Routes that page on nothing
/feed and /feed/from have no page-size knob and no cursor to follow: count is accepted and ignored, and paging them means calling again. What comes back is a recommendation, not a list you can walk to the end of.
Every other route’s exact paging field, default page size and quirks are on its own page — the endpoint index lists them all.