Requests and responses

Every endpoint is a POST to a named path under /v1/{platform}, with a JSON body. There is no pass-through route: if a path is not in the reference, it is not served.

The request

Method

POST, always, even where the call only reads. The arguments belong in a body rather than a query string, and a GET against a named route is a 405.

Body

One JSON object, capped at 64 KiB. Unknown fields are rejected rather than ignored, so a typo in a field name is a 400 and not a silently different query.

Headers

authorization and content-type: application/json. Nothing else is read, and nothing else is required.

curl -X POST https://api.sorina.sh/v1/tiktok/comments \
  -H "authorization: Bearer $SORINA_KEY" \
  -H 'content-type: application/json' \
  -d '{"aweme_id":"7639719512924835103","cursor":0,"count":20}'

Typos are refused, not guessed

An unknown field is almost always a misspelled one. Ignoring it would turn a typo into a successful call that answered a question you did not ask — and you would pay for it, and then debug the result rather than the request.

# awemeId is not aweme_id, and this is a 400 rather than a
# search for a video whose id you never sent.
{"error":"unknown field \"awemeId\"","code":"bad_request"}

The response

A 200 carries the platform's own body and its own content type, unchanged. We do not rename fields, drop them, or wrap the whole thing in an envelope of ours. What the endpoint pages describe is what the platform sends, not a shape we invented on top of it.

x-sorina-shape: upstream marks a body as theirs rather than ours. Our own errors never carry it, so it is a reliable way to tell a refusal from an answer without reading the status twice.

x-tt-logid is passed through when the platform sends one. Keep it: it is what a paginated search needs back as search_id, and it is the handle their own logs are keyed by.

200 OK
content-type: application/json; charset=utf-8
x-sorina-shape: upstream
x-tt-logid: 20260818…

Three things that catch people

A 200 can still be a failure

The platform reports some of its own errors inside an HTTP 200. Read the body’s own status fields — status_code is 0 on success — as well as ours.

Ids do not fit in a double

Video, user, comment and sound ids are 19 digits. JavaScript’s JSON.parse and Go’s any both route them through a float64 and round the last few digits. Decode them as strings or 64-bit integers.

Media URLs expire

Media and cover URLs in a response carry an x-expires stamp and stop working once it passes. Fetch what you need at the time; do not store the URL and come back to it tomorrow.

The one endpoint that is different

/search — the all-results tab — arrives in frames rather than as one JSON document. We decode the framing and hand you a JSON array of the frames, in arrival order, unmerged and otherwise untouched. Read every frame that has a data array; the ones without are layout hints and carry no results.

Everything else answers with one document. All paths are relative to https://api.sorina.sh/v1/tiktok.