thairouter
API

Streaming

Tokens as they are produced, over plain server-sent events.

Enabling streaming

Set "stream": true in the request. The response is text/event-stream and is proxied chunk-for-chunk from the model server; nothing is buffered to disk on the way.

Wire format

Standard OpenAI SSE: one data: line per event, blank line between events, data: [DONE] at the end.

text/event-stream
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","model":"thairouter/glm-5.3-flash","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}

data: {"id":"chatcmpl-…","object":"chat.completion.chunk","model":"thairouter/glm-5.3-flash","choices":[{"index":0,"delta":{"reasoning":"The user wants"},"finish_reason":null}]}

data: {"id":"chatcmpl-…","object":"chat.completion.chunk","model":"thairouter/glm-5.3-flash","choices":[{"index":0,"delta":{"content":"กาล"},"finish_reason":null}]}

data: {"id":"chatcmpl-…","object":"chat.completion.chunk","model":"thairouter/glm-5.3-flash","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: {"id":"chatcmpl-…","object":"chat.completion.chunk","model":"thairouter/glm-5.3-flash","choices":[],"usage":{"prompt_tokens":14,"completion_tokens":96,"total_tokens":110}}

data: [DONE]
  • delta.content carries answer text; delta.reasoning (older builds: delta.reasoning_content) carries thinking on reasoning models (Reasoning). With reasoning.exclude: true those deltas are removed before the frame reaches you.
  • The chunk with finish_reason ends the choice; one more chunk with usage follows.

Usage chunk

ThaiRouter always sets stream_options.include_usage upstream, so the last data chunk before [DONE] has an empty choices array and a usage object. You don't need to request it. That chunk is what your request is billed from, so handle the empty-choices case in your loop.

Response headers

HTTP/2 200
content-type: text/event-stream; charset=utf-8
cache-control: no-cache
x-thairouter-usage-id: 7d0a2b6c-…
x-thairouter-reasoning-effort: max
access-control-expose-headers: x-thairouter-usage-id, x-thairouter-reasoning-effort

The usage id is in a header because the stream body is passed through untouched. Match it against your logs to see the settled cost. x-thairouter-reasoning-effort is present only on reasoning models and reports the level actually used.

Disconnecting early

If the client closes the connection before the usage chunk arrives, the request is charged the full reservation (estimated prompt + max_tokens), because the real token count never reaches us. Keep max_tokens honest when you expect to cancel streams.

Examples

curl -N https://api.thairouter.ai/v1/chat/completions \
  -H "Authorization: Bearer $THAIROUTER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "thairouter/glm-5.3-flash",
    "messages": [{"role": "user", "content": "เล่านิทานสั้นๆ"}],
    "stream": true
  }'