thairouter
API

Chat completions

The one endpoint that does the work. Same contract as OpenAI.

Endpoint

POST/v1/chat/completionsAPI key

Send a JSON body with Content-Type: application/json. Non-streaming requests return a JSON completion; set stream: true for server-sent events (see Streaming).

Request

Fields ThaiRouter itself reads or rewrites. Everything else is forwarded to the model server unchanged; see Parameters.

FieldTypeDescription
modelrequiredstringA live ThaiRouter model id, e.g. thairouter/glm-5.3-flash. Unknown or coming-soon ids return 404.
messagesrequiredarrayNon-empty list of { role, content }. Roles system, user, assistant.
max_tokensintegerCompletion cap. Clamped to the model's max_output_tokens and to the context room left after the prompt. Omitted = the model's full output budget. Also sizes the credit reservation.
streambooleanStream tokens as SSE. Usage is always included in the final chunk.Default false.
reasoning_effortstringThinking budget on reasoning models: none, minimal, low, medium, high, xhigh, max. Snapped to the nearest level the model supports. See Reasoning.
reasoningobjectOpenRouter-style alternative: { "effort", "enabled", "exclude" }. exclude: true strips thinking from the response.
temperaturenumberForwarded. 0 to 2.

Response

A standard chat completion object. model is the ThaiRouter id you sent.

200 OK · application/json
{
  "id": "chatcmpl-9c3e…",
  "object": "chat.completion",
  "created": 1789000000,
  "model": "thairouter/glm-5.3-flash",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Connection pooling คือ…",
        "reasoning": "The user asks in Thai about…"
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 31,
    "completion_tokens": 188,
    "total_tokens": 219
  },
  "thairouter": {
    "usage_id": "7d0a2b6c-…",
    "cost": 0.002975,
    "reasoning_effort": "max"
  }
}

finish_reason is stop for a natural end and length when max_tokens cut the answer. Reasoning models may include reasoning (older server builds: reasoning_content); see Reasoning.

The thairouter object

FieldTypeDescription
usage_idstring (uuid)Id of this request in your logs and credit ledger. On streams it arrives as the x-thairouter-usage-id response header instead.
costnumber | nullCredits charged for this request (1 credit = ฿1), computed from the returned usage. null if the model server sent no usage.
reasoning_effortstring | nullThe thinking level actually used after snapping to what the model supports. null on non-reasoning models. On streams it is the x-thairouter-reasoning-effort header.
OpenAI SDKs ignore unknown top-level fields, so thairouter is harmless. In Python it is available via r.model_extra["thairouter"]; in TypeScript cast the response or read it from the raw JSON.

Request lifecycle

1. Validate

Body size, JSON, model, messages, reasoning fields. Failures here are 4xx and cost nothing.

2. Reserve

We estimate prompt tokens from the message bytes, add max_tokens (the full output budget if you omitted it), price both at the model's rates and deduct that amount. If the balance can't cover it you get 402 insufficient_quota and nothing is charged.

3. Proxy

The body is forwarded to vLLM with model rewritten to the upstream name and the reasoning request translated into what the model's chat template understands. Bodies are never persisted.

4. Settle

Actual cost is computed from the usage the model server reports and the difference is credited back (or debited if the estimate was low). An upstream error refunds the full reservation. Details in Billing.

Examples

curl 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": "system", "content": "ตอบเป็นภาษาไทย กระชับ"},
      {"role": "user", "content": "อธิบาย connection pooling"}
    ],
    "temperature": 0.7,
    "max_tokens": 512
  }'