API reference

Thinking and max_tokens

A thinking (reasoning) model spends tokens on reasoning before it answers. This page lists which models think, the fields that switch or steer it, how max_tokens covers reasoning and answer together, what is held before the call and what is billed, and how reasoning streams.

Which models think

A model thinks always, optionally or never. The tables below were measured on staging on 2026-10-25: one short prompt at default settings, then each switch. The response is the authority for any single call: usage.completion_tokens_details.reasoning_tokens is the part of completion_tokens spent on reasoning, and message.reasoning_content carries the reasoning text where the model returns it.

Some models report reasoning only as text or only as a token count. minimax/minimax-m2.7 and qwen/qwen3.5-flash return reasoning text with no reasoning_tokens; their reasoning is inside completion_tokens. Some models report reasoning_tokens and no text.

Thinks by default, enable_thinking switches it

Send "enable_thinking": false to end the reasoning. thinking_budget (an integer) caps the reasoning tokens on the models that list it in supported_parameters.

ModelsNotes
qwen/qwen3.5-plus, qwen/qwen3.5-flash, qwen/qwen3.6-plus, qwen/qwen3.6-flash, qwen/qwen3.7-plus, qwen/qwen3.7-flash, qwen/qwen3.7-maxenable_thinking: false gave no reasoning tokens
qwen/qwen3.8-max, qwen/qwen3.8-max-0902, qwen/qwen3.8-flash, qwen/qwen3.8-27b, qwen/qwen3.8-omni-flashAs above. qwen/qwen3.8-omni-flash also takes reasoning_effort: "none"
deepseek/deepseek-v4-pro, deepseek/deepseek-v4-pro-0813, deepseek/deepseek-v4-flash-0731, deepseek/deepseek-v4.1-flashAs above
z-ai/glm-5.2, z-ai/glm-5.2-fast-previewAs above
baidu/ernie-5.0, baidu/ernie-5.1As above

Thinks by default, thinking or reasoning_effort: "none" ends it

Send "thinking": {"type": "disabled"} or "reasoning_effort": "none". enable_thinking: false had no effect on these models.

Modelsthinking disabledreasoning_effort: "none"
minimax/minimax-m3YesNo effect
tencent/hy3, tencent/hy4-previewYesYes
xiaomi/mimo-v2.6-flash, xiaomi/mimo-v2.6-pro, xiaomi/mimo-v2.6-pro-ultraspeedYesYes
z-ai/glm-5.1, z-ai/glm-5.2-fastYesYes
moonshotai/kimi-k2.8-preview, moonshotai/kimi-k3No effectYes
deepseek/deepseek-v4.1-flash-1017No effectYes

Thinks by default, the switch is off or fixed

These models reason on every call. A switch is dropped (the call succeeds and the model still thinks) or refused with 400, so use the effort or budget field to steer them.

ModelsSteer with
z-ai/glm-5.3, z-ai/glm-5.3-flash, z-ai/glm-5.3-flashxreasoning_effort: low, high or max. thinking disabled had no effect. low is the way to shorten the reasoning
qwen/qwen3.7-max-preview, qwen/qwen3.8-2.4t-a95bthinking_budget. enable_thinking: false is accepted and has no effect
moonshotai/kimi-k2.7-code, moonshotai/kimi-k2.7-code-highspeedNo switch. Disabling is 400 unsupported_parameter on the high-speed model
minimax/minimax-m2.7, x-ai/grok-4.6No switch tested ended the reasoning

Off by default, you switch it on

ModelsTurn on with
qwen/qwen-flash, qwen/qwen3-vl-plus"enable_thinking": true
moonshotai/kimi-k2.6"thinking": {"type": "enabled"}

Amazon Nova

ModelsBehaviour
amazon/nova-2-lite-v1No reasoning by default. reasoning_effort: low or medium; high is 400

Never think

baidu/ernie-4.5-turbo-128k, baidu/ernie-4.5-turbo-32k, baidu/ernie-4.5-turbo-vl, baidu/ernie-4.5-turbo-vl-32k, qwen/qwen-max, qwen/qwen3-coder-flash, qwen/qwen-mt-flash, qwen/qwen-mt-lite, qwen/qwen-mt-plus, qwen/qwen-vl-ocr-2025-11-20, qwen/qwen3.5-omni-flash, qwen/qwen3.5-omni-plus.

Switch and effort fields

FieldValuesWhat it does
enable_thinkingtrue, falseTurns reasoning on or off on the models in the first and the "off by default" tables
thinking_budgetIntegerCaps the reasoning tokens. thinking_budget: 20 on qwen/qwen3.8-max ended after 11 reasoning tokens
thinking{"type": "enabled"} or {"type": "disabled"}Turns reasoning on or off on the models in the second table and on moonshotai/kimi-k2.6
reasoning_effortnone, low, medium, high, max, as the model's row listsSets how much the model reasons. A value a model does not take is 400
reasoning{"effort": …}, {"max_tokens": …}The same controls as an object, on Nova models

A field the model's route does not take is dropped or refused, never silently reinterpreted:

  • A switch on a model that always thinks is dropped. The call succeeds and the model still thinks.
  • A value the provider does not accept is 400 invalid_request with the message upstream rejected the request, for example reasoning_effort: "high" on amazon/nova-2-lite-v1.
  • A field the gateway knows the model cannot take is 400 unsupported_parameter with the field named, for example disabling thinking on moonshotai/kimi-k2.7-code-highspeed.

Whether a model lists a field is in its supported_parameters in GET /v1/models. On /v1/messages the Anthropic thinking field is ignored and named in X-TokenHub-Ignored-Fields; on /v1/responses reasoning.effort is passed on.

How max_tokens covers reasoning

max_tokens and max_completion_tokens both cap the reasoning and the answer together. AI Hub sends the field the route reads, so you can use either. usage.completion_tokens includes the reasoning, and a call that reaches the cap stops with finish_reason: "length".

Measured on qwen/qwen3.8-max on 2026-10-25, each with the same prompt:

RequestResult
No cap60 reasoning tokens, then the answer, finish_reason: "stop"
max_tokens: 5050 reasoning tokens, content empty, finish_reason: "length"
max_completion_tokens: 50The same as max_tokens: 50
thinking_budget: 2011 reasoning tokens, then the answer

Empty content

When reasoning uses the whole cap there is no room for the answer. The response is 200 with empty content, finish_reason: "length" and billed reasoning tokens. On a call that is not streamed, when the reasoning used the whole cap, the response also carries the header X-TokenHub-Warning: reasoning_exhausted_output_cap. On 2026-10-25 a cap of 16 tokens gave this result on deepseek/deepseek-v4-pro, z-ai/glm-5.3, minimax/minimax-m3, moonshotai/kimi-k3, tencent/hy3, and a cap of 50 did on qwen/qwen3.8-max.

To avoid it:

  • Raise the cap above what the model reasons. Reasoning tokens on the same coding prompt (merge overlapping intervals, then explain the cost) ranged from 40 to 2,565.
  • Omit the cap for open-ended work. A call with no cap is held at 32,768 output tokens (see below).
  • Lower the reasoning with the model's switch or effort field from the tables above.
  • Stream, and read finish_reason on the last chunk. A stream has no warning header.
ModelReasoning tokensTotal completion_tokens
z-ai/glm-5.32,5653,014
qwen/qwen3.6-plus1,7961,984
tencent/hy31,5321,724
qwen/qwen3.7-max-preview767995
z-ai/glm-5.2562785
moonshotai/kimi-k3424663
qwen/qwen3.8-max418549
minimax/minimax-m3313473
baidu/ernie-5.1290513
deepseek/deepseek-v4-pro159405
xiaomi/mimo-v2.6-pro129425

Recommended minimum caps for a model that thinks by default:

Taskmax_tokens
A short answer or a classification4,000
Code, analysis or a long answer16,000, or no cap
A call where you switched reasoning offThe length of the answer you expect

A cap of 4,000 covered every model in the table for this prompt. Harder prompts reason longer, so raise the cap with the task.

Admission hold and billing

Before it calls the provider, AI Hub holds the most the request could cost: the prompt at the prompt price plus the output cap at the completion price. The cap is max_tokens or max_completion_tokens; with neither, the hold assumes 32,768 output tokens. Reasoning is not added on top, because it counts inside the cap. A request your workspace cannot cover is refused before the provider is called with 402 insufficient_credits; on 2026-10-25 max_tokens: 100000000 on deepseek/deepseek-v4-pro was refused this way. The hold is released when the request settles.

You are billed for the usage the provider reports, not for the hold. Reasoning tokens are billed at the completion price and are part of completion_tokens; see Cost. A cap that is lower than the model's reasoning therefore costs the capped tokens and returns no answer.

Streaming reasoning

With "stream": true, reasoning arrives first as delta.reasoning_content chunks, then the answer as delta.content. The last chunk before data: [DONE] carries usage with completion_tokens_details.reasoning_tokens, and an empty choices array.

sse
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","model":"qwen/qwen3.8-max","choices":[{"index":0,"delta":{"reasoning_content":"17 times 23 is 17 times 20 plus 17 times 3"}}]}

data: {"id":"chatcmpl-…","object":"chat.completion.chunk","model":"qwen/qwen3.8-max","choices":[{"index":0,"delta":{"content":"391"},"finish_reason":"stop"}]}

data: {"id":"chatcmpl-…","object":"chat.completion.chunk","model":"qwen/qwen3.8-max","choices":[],"usage":{"prompt_tokens":77,"completion_tokens":62,"total_tokens":139,"completion_tokens_details":{"reasoning_tokens":56}}}

data: [DONE]

Models that return no reasoning text stream only delta.content and still report reasoning_tokens in the usage chunk. On a call that is not streamed, the reasoning text is message.reasoning_content. See Streaming for error frames and truncation.

Examples

Switch reasoning off on a model that thinks by default:

import os, requests

r = requests.post(
    "https://tokenhub-api.belive.sg/api/v1/chat/completions",
    headers={"Authorization": f"Bearer {os.environ['TOKENHUB_API_KEY']}"},
    json={
        "model": "qwen/qwen3.8-max",
        "messages": [{"role": "user", "content": "What is 17 times 23?"}],
        "enable_thinking": False,
        "max_tokens": 200,
    },
)
print(r.json()["choices"][0]["message"]["content"])

Keep the reasoning, give it room, and read how much it used:

import os, requests

r = requests.post(
    "https://tokenhub-api.belive.sg/api/v1/chat/completions",
    headers={"Authorization": f"Bearer {os.environ['TOKENHUB_API_KEY']}"},
    json={
        "model": "z-ai/glm-5.3",
        "messages": [{"role": "user", "content": "Write a function that merges overlapping intervals."}],
        "reasoning_effort": "low",
        "max_tokens": 8000,
    },
)
body = r.json()
choice = body["choices"][0]
if choice["finish_reason"] == "length" and not choice["message"]["content"]:
    raise RuntimeError("The cap was spent on reasoning; raise max_tokens")
print(body["usage"]["completion_tokens_details"]["reasoning_tokens"])

The route and model decide which fields count. Read supported_parameters in GET /v1/models, then confirm with reasoning_tokens in the response.

Additional current routes

A production check on 2026-10-26 used reasoning_effort: "low" with moonshotai/kimi-k3.1-pro, which returned reasoning text. Read the reported token breakdown when available; missing reasoning text does not mean no reasoning was used.

For z-ai/glm-5.3, use a documented reasoning_effort (low, high or max) to control effort. An always-thinking model can accept a thinking field and continue reasoning; use its effort control. Provider routes can differ: a response with accepted fields is evidence for that request, not proof that effort labels represent identical compute across providers.

For ordinary text work on optional-thinking Qwen models, explicitly disable thinking when you do not need it. A 512-token allowance on qwen/qwen3.8-flash was consumed entirely by reasoning in a longer-answer case; both the reasoning and empty answer outcome were still metered. Use the task-dependent caps above, or the documented off switch, rather than copying a short-example cap into every task.