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.
| Models | Notes |
|---|---|
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-max | enable_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-flash | As 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-flash | As above |
z-ai/glm-5.2, z-ai/glm-5.2-fast-preview | As above |
baidu/ernie-5.0, baidu/ernie-5.1 | As 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.
| Models | thinking disabled | reasoning_effort: "none" |
|---|---|---|
minimax/minimax-m3 | Yes | No effect |
tencent/hy3, tencent/hy4-preview | Yes | Yes |
xiaomi/mimo-v2.6-flash, xiaomi/mimo-v2.6-pro, xiaomi/mimo-v2.6-pro-ultraspeed | Yes | Yes |
z-ai/glm-5.1, z-ai/glm-5.2-fast | Yes | Yes |
moonshotai/kimi-k2.8-preview, moonshotai/kimi-k3 | No effect | Yes |
deepseek/deepseek-v4.1-flash-1017 | No effect | Yes |
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.
| Models | Steer with |
|---|---|
z-ai/glm-5.3, z-ai/glm-5.3-flash, z-ai/glm-5.3-flashx | reasoning_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-a95b | thinking_budget. enable_thinking: false is accepted and has no effect |
moonshotai/kimi-k2.7-code, moonshotai/kimi-k2.7-code-highspeed | No switch. Disabling is 400 unsupported_parameter on the high-speed model |
minimax/minimax-m2.7, x-ai/grok-4.6 | No switch tested ended the reasoning |
Off by default, you switch it on
| Models | Turn on with |
|---|---|
qwen/qwen-flash, qwen/qwen3-vl-plus | "enable_thinking": true |
moonshotai/kimi-k2.6 | "thinking": {"type": "enabled"} |
Amazon Nova
| Models | Behaviour |
|---|---|
amazon/nova-2-lite-v1 | No 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
| Field | Values | What it does |
|---|---|---|
enable_thinking | true, false | Turns reasoning on or off on the models in the first and the "off by default" tables |
thinking_budget | Integer | Caps 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_effort | none, low, medium, high, max, as the model's row lists | Sets 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_requestwith the messageupstream rejected the request, for examplereasoning_effort: "high"onamazon/nova-2-lite-v1. - A field the gateway knows the model cannot take is
400 unsupported_parameterwith the field named, for example disabling thinking onmoonshotai/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:
| Request | Result |
|---|---|
| No cap | 60 reasoning tokens, then the answer, finish_reason: "stop" |
max_tokens: 50 | 50 reasoning tokens, content empty, finish_reason: "length" |
max_completion_tokens: 50 | The same as max_tokens: 50 |
thinking_budget: 20 | 11 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_reasonon the last chunk. A stream has no warning header.
| Model | Reasoning tokens | Total completion_tokens |
|---|---|---|
z-ai/glm-5.3 | 2,565 | 3,014 |
qwen/qwen3.6-plus | 1,796 | 1,984 |
tencent/hy3 | 1,532 | 1,724 |
qwen/qwen3.7-max-preview | 767 | 995 |
z-ai/glm-5.2 | 562 | 785 |
moonshotai/kimi-k3 | 424 | 663 |
qwen/qwen3.8-max | 418 | 549 |
minimax/minimax-m3 | 313 | 473 |
baidu/ernie-5.1 | 290 | 513 |
deepseek/deepseek-v4-pro | 159 | 405 |
xiaomi/mimo-v2.6-pro | 129 | 425 |
Recommended minimum caps for a model that thinks by default:
| Task | max_tokens |
|---|---|
| A short answer or a classification | 4,000 |
| Code, analysis or a long answer | 16,000, or no cap |
| A call where you switched reasoning off | The 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.
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"])
curl https://tokenhub-api.belive.sg/api/v1/chat/completions \ -H "Authorization: Bearer $TOKENHUB_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"qwen/qwen3.8-max","messages":[{"role":"user","content":"What is 17 times 23?"}],"enable_thinking":false,"max_tokens":200}'
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"])
curl -i https://tokenhub-api.belive.sg/api/v1/chat/completions \ -H "Authorization: Bearer $TOKENHUB_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"z-ai/glm-5.3","messages":[{"role":"user","content":"Write a function that merges overlapping intervals."}],"reasoning_effort":"low","max_tokens":8000}'
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.