What reasoning_effort actually does on DeepSeek, OpenAI, Anthropic and Gemini: one name, five physics, and a five-line test
Set one reasoning_effort for every provider and you get five different bills. Read against vendor docs, a Zenn effort ladder and a gateway vendor's probe, the same value is passed through on one surface, converted into a token budget by somebody else's table on another, honoured to the token here, accepted and ignored there, and rejected with a 400 elsewhere. DeepSeek's V4.1 Flash report publishes an effort-to-score-to-token curve; the Zenn ladder shows GPT-6 Astra's top gear scoring lower for 1.9× the cost. Check the reasoning-token count in usage with the five-line loop below.

reasoning_effort does one of five things depending on where you send it, and the HTTP
status does not tell you which. On an OpenAI-compatible surface the string is forwarded as
sent. Through a translator it is converted into a thinking-token budget by a table you did
not write, so medium is 8,192 tokens on one path and 2,048 on another. On Qwen, GLM and
DeepSeek a numeric budget is honoured to the token. On Kimi K3 and MiniMax M3 the field is
accepted, ignored and billed. On GPT-5.6, on Claude 4.7 and later, and on Gemini Pro, the
wrong spelling is rejected with a 400. That is why the same value costs different amounts:
it is a name, not a unit. A 200 tells you the JSON was accepted, not that the value reached
the model. The reasoning-token count in usage is a practical check, and the last section
is a five-line loop that reads it.
Scope first. There are no measurements of mine in this post. What there is: DeepSeek’s V4.1 Flash technical report; a Zenn effort ladder and a gateway vendor’s thinking-control probe, with their sample sizes in the sections below; a separate Zenn check of async tool calling; and the four vendors’ documentation read field by field. We build a gateway, so we write both sides of this translation and know where each side is tempted to lie. The previous post listed reasoning state as item 4 of the seven things a translator cannot hide (One API for OpenAI, Anthropic and Google models). This is that item unfolded.
What DeepSeek’s V4.1 Flash report says effort buys, and what it costs
DeepSeek publishes an effort-to-score-to-token curve in §5.3.3 and Figure 9 (DeepSeek V4.1 Flash Technical Report). The dial is an integer from 1 to 100. DeepSeek reports that raising it from 25 to 100 moves the average Pass@1 over eight reasoning benchmarks from 67.1% to 76.3%, DeepSWE v1.1 from 66.0% to 74.2% and Terminal-Bench 2.1 from 82.4% to 90.6%, “at the cost of roughly 2.5× more output tokens.”

Two details matter more than the headline. First, the gains are front-loaded: “the 60–80 range already recovers most of the accuracy of the maximum setting at less than half of its token budget, whereas the final step to effort 100 lengthens agent trajectories by 1.6–1.8× for only marginal improvements.” Second, Appendix B.3 gives the token side in absolute numbers. From 25 to 100 the mean response grows 2.0–3.1× on every reasoning benchmark, “from 4.6k to 11.4k tokens per response on AIME 2026 and from 29.1k to 86.1k on MathArena Apex 2025,” and accuracy never degrades as effort rises. MathArena Apex gains 40.3 points, 25.3% to 65.6%. On a dataset where the answer takes 86k tokens, the dial does what the name says.
How the dial works is in §5.1.4, and it decides what “effort” is. The value is text. The
training run prepends Reasoning Effort: {effort} (range 1–100; higher values request more thorough reasoning) to the system prompt, then applies a length penalty whose coefficient
decays exponentially with the requested effort. Appendix C states the consequence: the
scalar is “a deployment-time control over the cost–quality trade-off without imposing a hard
token budget.” DeepSeek’s dial is a learned posture, not a cap, which is why the curve is
smooth and why, further down, a probe on a much smaller task sees almost nothing move.
The API exposes three points on the curve, not the curve: Table 2 maps max, high and
low to 100, 75 and 50, and the 25 setting in the figure is not a public tier. The docs
fold every other spelling onto those three: minimal becomes low, medium
becomes high, xhigh becomes high, ultra becomes max, with thinking on by default at
high (Thinking Mode — DeepSeek API Docs).
If your config says medium because that is what it says for OpenAI, DeepSeek runs it at
75, which is what it would have run without the parameter. The same page notes that in
thinking mode temperature, presence_penalty and frequency_penalty “will not trigger an
error but will also have no effect.” The vendor that documents effort most transparently
ignores three other parameters on the same request, silently: one API, two of the five
physics at once.
Cost: the top tier against the 25 setting is about 2.5× the output tokens, which is 2.5× the output bill. At the Flash off-peak output price of $0.60 per million tokens (Models & Pricing — DeepSeek API Docs), an 86k-token MathArena answer is about five cents and the 29k-token one at effort 25 is under two. Cheap per call; a 2.5× multiplier on every step of an agent loop is not. The report’s own advice is that the maximum tier “is best reserved for the most challenging tasks.”
A Zenn author’s ladder on GPT-6 Astra: the top gear scored lower and cost 1.9× more
On 9 September a Zenn author published the effort ladder for GPT-6 Astra, in a benchmark series that delegates a small Godot game implementation to a child model and grades the result with 57 hidden tests (Zenn — godot-llm-gamebench: how gpt-6-astra changes with effort (ja)). Four effort levels, three runs each, twelve runs in all; the parent agent fixed at claude-opus-5 at max effort, the child running on Codex CLI. Three runs per cell is an indication, not a measurement. Costs are at the list prices the author used, $10 per million input, $1 cached, $50 output.
| Effort | Mean score | Std. dev. | Median time | Child cost | Median output tokens |
|---|---|---|---|---|---|
low | 90.88 | 7.08 | 6.5 min | $0.990 | 7.1k |
medium | 91.76 | 5.77 | 7.6 min | $0.981 | 8.2k |
high | 100.00 | 0.00 | 14.6 min | $1.597 | 15.1k |
xhigh | 97.54 | 0.00 | 21.1 min | $2.969 | 29.9k |
Three readings. high scored 100 three times out of three, the first condition in the
series to do so, and the author calls it the series’ best score. xhigh scored lower, and
not by noise: all three runs land on 97.54 with the same two hidden tests failing, standard
deviation zero, which the author describes as a reproducible regression specific to that
level. And low and medium are one gear with two labels: $0.990 against $0.981, 0.88
points apart, with low passing more hidden tests (55.0 of 57 against 53.0). On this task,
then, the dial has three useful positions, not four, and the ceiling is high. Code
quality, judged separately, went the other way, 4.42 against 4.25; if you want readable
code rather than passing code the top gear is arguable.
The bill tracks the tokens: child cost goes 3.0× from low to xhigh as median output
tokens go 7.1k to 29.9k. And the cross-model line from the same post: astra at high beats
gpt-5.6-luna at high by 1.23 points on the combined score at 35.5× the child cost, $1.597
against $0.045. Astra used fewer input tokens, 434k against 689k; a 50× input price does
not care. The author says this task is probably too easy to show what the expensive model
is for. It still shows what xhigh is for, which on this evidence is nothing.
OpenAI’s guide is consistent with every line of that table. Effort values are
“model-dependent and can include none, minimal, low, medium, high, xhigh, and max”; GPT-6
Astra “does not support none reasoning effort” and returns HTTP 400 if you send it; GPT-5.5
defaults to medium; and the closing advice is to “Treat reasoning.effort as a tuning knob,
not the primary way to recover quality”
(Reasoning models — OpenAI API).
The same page says function calling with Astra is Responses-only, which a second Zenn author
confirmed the next day while measuring Astra’s async tool calling, with reasoning.effort
set to low throughout
(Zenn — what GPT-6 Astra waits for: async tool calling and mid-turn steering, measured (ja)).
So on OpenAI the enum is a real gear, the set of gears is model-specific, and the top gear
is not the best one.
Five physics: what happens to the value after the 200
Put the vendor pages, two gateway docs and one probe on one table and the parameter sorts into five behaviours. ✓ means the meter moves as you asked, ⚠ that it moves by somebody else’s table, ✗ that the value never reached the model, whether the call was billed or refused.
| # | Physics | Where it is written down | What the reasoning-token count does |
|---|---|---|---|
| a | Passed through as an enum | OpenAI-compatible surfaces; one gateway: “forwarded to the provider as sent,” neither validated nor guaranteed | Moves, if the model has that tier |
| b | Translated into a token budget | Google’s OpenAI compatibility: low / medium / high → 1,024 / 8,192 / 24,576 on Gemini 2.5; one gateway on Anthropic: 2,048 / 8,192 / 16,384 / 32,768; LiteLLM on older Claude: 1,024 / 2,048 / 4,096 | Moves, in steps the translator chose |
| c | Honoured to the token | A 13-model probe, August 2026: thinking_budget: 16 burned exactly 16 on Qwen 3.8 Max, GLM 5.2 and DeepSeek V4 | Equals what you sent |
| d | Accepted and ignored | Same probe: Kimi K3 burned 8 to 97 against a cap of 16, MiniMax M3 18 to 44; Anthropic’s OpenAI-compatible layer lists reasoning_effort as Ignored; GLM ignores reasoning_effort per a client’s provider notes | Flat, billed anyway |
| e | Rejected with 400 | GPT-5.6 rejects numeric budgets; Claude 4.7 and later reject budget_tokens; Gemini Pro rejects every off spelling; Astra rejects none; Qwen and DeepSeek reject a budget of 0 | Nothing to meter |
Each row with its source.
(a) Passed through. The plainest statement is in a gateway’s own mapping doc: on
OpenAI-compatible upstreams the value is “Forwarded to the provider as sent - the gateway
neither validates the value nor guarantees the provider honours it”
(reasoning_effort on an AI Gateway: Per-Provider Behaviour).
A second gateway states the same policy from the other end: “Effort tiers are never
downgraded by the gateway: providers that accept an effort parameter receive the value
unchanged (unsupported values result in a provider error)”
(Reasoning — LLM Gateway Docs). Pass-through
is the honest default. It also means xhigh in an OpenAI-shaped config is DeepSeek’s
high, OpenAI’s xhigh, and a provider error anywhere the word does not exist.
(b) Translated. Google publishes its table. Through the OpenAI-compatible endpoint,
minimal and low both become thinking_budget: 1024 on Gemini 2.5, medium becomes
8,192 and high 24,576; on the Gemini 3 line the same words become thinking_level values
(Gemini API — OpenAI compatibility, Thinking).
One gateway’s Anthropic mapping is low / medium / high / xhigh / max → 2,048 /
8,192 / 16,384 / 32,768 tokens, clamped to stay under max_tokens and “skipped entirely
when max_tokens cannot fit Anthropic’s 1,024-token minimum”
(reasoning_effort on an AI Gateway: Per-Provider Behaviour).
LiteLLM’s table for older Claude models, which the previous post read, is 1,024 / 2,048 /
4,096 (LiteLLM — Anthropic, Usage - Thinking).
So medium is 8,192 thinking tokens on Google’s path and on one gateway’s Anthropic path,
and 2,048 on LiteLLM’s: same string, four-fold difference, all documented, none wrong. The second gateway clamps whatever it derives to
1,024–128,000 on Anthropic and lets an explicit reasoning.max_tokens override the word
(Reasoning — LLM Gateway Docs).
A note on a claim you may have read: that LangChain’s Anthropic integration turns
reasoning_effort into a budget as a ratio of max_tokens, 0.1 for minimal up to 0.8 for
high. I read langchain-anthropic at 1.5.0, 1.5.3 and 1.6.0. reasoning_effort is typed as
max | xhigh | high | medium | low and written straight into output_config["effort"]; if
you did not set thinking yourself, the integration adds
{"type": "adaptive", "display": "summarized"} on models whose profile lists xhigh
(langchain-anthropic — chat_models.py).
No ratio, no clamp. It is physics (a), and Anthropic’s enum is not a budget of anything,
which is the next section.
(c) Honoured. In August one gateway vendor’s engineering team probed 13 models from
nine vendors with every thinking-control spelling the OpenAI-compatible surface accepts,
three runs per cell, measured on 11 and 12 August 2026
(LLM Thinking Controls: What 13 Models Accept, Ignore, or Enforce).
Their data, not mine, read through their own gateway, and their DeepSeek builds were V4
(the 0731 Flash and Pro), not V4.1. With thinking_budget: 16, Qwen 3.8 Max, GLM 5.2 and
both DeepSeek V4 builds burned exactly 16 reasoning tokens. Gemini Flash treated caps of 0
through 64 as off and let 1,024 through; Gemini Pro cut its burn under a cap but floored
around 200 against a requested 64. A budget enforced to the token is the only one of the
five physics where “budget” means what it means in accounting.
(d) Accepted and ignored. Same probe, and here it reports four runs rather than its
usual three: Kimi K3 burned 8, 19, 79 and 97 reasoning tokens against the same cap of 16,
never 16; MiniMax M3 burned 18 to 44, “billed as usual, with
nothing in the response hinting the cap had been dropped.” Anthropic’s own
OpenAI-compatibility page lists reasoning_effort under Ignored
(OpenAI SDK compatibility — Claude Platform Docs).
A third-party client’s provider notes say GLM’s endpoint “silently ignores” the field and
drives thinking only through thinking.type
(Reasoning controls by provider — DeepSeek-Reasonix).
The probe’s effort ladders show the same shape from the other side. On a five-step
arithmetic task, DeepSeek V4 read 78 reasoning tokens at low and 54 at max, Kimi 94 at
minimal and 67 at max, MiniMax between 61 and 93 in no order; GLM’s high burned less
than its low. Flat lines and a zigzag. Only the two Geminis climbed, 137 to 390 on Flash
and 180 to 387 on Pro.
Mind the scale before you read that against DeepSeek’s curve. A task that takes 50 to 100 reasoning tokens is not where the report says the dial operates; its curves start at 4.6k tokens per answer, and the mechanism is a soft posture, not a cap. A flat probe on a trivial task and a monotone vendor curve on hard benchmarks are both true. What the probe establishes is narrower: at small task sizes several dials do nothing, and the bill does not tell you.
(e) Rejected. Same probe: GPT-5.6 returned 400 for thinking_budget and for
thinking: {"type": "disabled"}; Claude Sonnet 5 and Opus 5 returned 400 for
budget_tokens; Gemini 3.1 Pro returned 400 for every off spelling; Qwen and DeepSeek
rejected a budget of 0. Google’s page says why for Pro: “Reasoning cannot be turned off for
Gemini 2.5 Pro or 3 models.” Anthropic’s says why for budget_tokens: deprecated on the 4.6
models, and “Claude 4.7 and later models do not support it and reject requests that use it,
returning a 400 error”
(Extended thinking — Claude Platform Docs).
OpenAI’s says why for Astra and none. Rejection is the cheapest of the five: one failed
call, no reasoning bill, and you know.
Anthropic and Gemini: on two of the four, “medium” is a posture, not a quantity
The table treats Anthropic’s and Gemini’s levels as enums, which is what the wire carries. What the words mean differs from OpenAI and DeepSeek, and the difference is billable.
Anthropic’s output_config.effort takes low, medium, high, xhigh and max, with
the caveat that not every model with max has xhigh. The doc: “By default, Claude uses
high effort”; “Setting effort to high produces exactly the same behavior as omitting the
effort parameter entirely”; and the sentence that decides how to read the whole dial,
“Effort is a behavioral signal, not a strict token budget”
(Effort — Claude Platform Docs).
It applies to every output token, tool calls included: “Lower effort also means fewer and
terser tool calls.” So on Claude, medium does not buy a quantity of
thinking. It buys a shorter agent. The steering page adds the cost most configs miss: “The
resolved effort value is rendered into the prompt, so changing it between requests
invalidates cache breakpoints,” and their worked example shows the third request re-creating
3,546 cached tokens because effort went from high to medium
(Steering thinking — Claude Platform Docs).
Flip effort per call on Claude and you pay a cache write per flip.
The legacy dial, thinking.budget_tokens, was a quantity, or nearly one: minimum 1,024, must
be less than max_tokens, and “a target rather than a strict cap”
(Extended thinking — Claude Platform Docs).
It is deprecated on 4.6 and a 400 on 4.7 and later. Every translation table in row (b) that
still converts your word into budget_tokens is, on a current Claude model, converting into
an error. DeepSeek’s Anthropic-format endpoint, incidentally, takes the
opposite line: thinking is “Supported (budget_tokens is ignored)” and output_config is
“Only effort is supported”
(Using the Anthropic API — DeepSeek API Docs),
so the same Anthropic-shaped request carries a budget that one backend rejects and another
discards.
Gemini has two fields and a model table. thinkingLevel is an enum, MINIMAL / LOW /
MEDIUM / HIGH, “Recommended for Gemini 3 or later models. Use with earlier models results
in an error”; thinkingBudget is an integer for the 2.5 line
(Gemini API reference — ThinkingConfig).
Which levels exist depends on the model: gemini-3.8-flash defaults to medium and offers low,
medium and high; gemini-3.1-pro-preview defaults to high with the same three; gemini-3.6-flash
adds minimal; gemini-2.5-flash-lite is off by default
(Gemini thinking — Gemini API). Pro
and the 3 series cannot be switched off. And one line on that page costs real money:
max_output_tokens “acts as a hard cutoff enforced by the infrastructure without changing
how the model allocates its thinking budget,” so a model that hits it while thinking returns
incomplete with truncated or empty output “while still billing for any thinking tokens
generated.” Google’s own advice is to lower thinking_level rather than set a small output
cap. A cap is not a dial.
So on two of the four providers, medium is a posture the model interprets per request. On
OpenAI it is a gear with model-specific teeth. On DeepSeek it is a point on a published
curve. None of those is a token count, and only physics (c) ever was.
The response side: reasoning_content is a DeepSeek convention, not an OpenAI field
The request is half the translation. On the way back, the field where the reasoning lands
is not standard either. An llm-rosetta issue from May lays it out: reasoning_content
“was introduced by DeepSeek and adopted by several Chinese providers, but it is not part of
the OpenAI standard”
(llm-rosetta #185 — Normalize reasoning/thinking fields across OpenAI Chat-compatible providers).
Its table: DeepSeek, Volcengine, Qwen, Zhipu and Moonshot return message.reasoning_content
as a string, Volcengine with an encrypted_content alongside; OpenRouter returns
message.reasoning plus a reasoning_details array carrying format metadata; MiniMax
embeds <think>…</think> inside content unless you opt into reasoning_split. Seven
providers, four shapes, and a converter that had hard-coded the first.
DeepSeek’s own rule makes the shape load-bearing. With tools in the request, “the
reasoning_content must be fully passed back to the API in all subsequent requests — even for
turns where the model did not perform a tool call. If your code does not correctly pass back
reasoning_content, the API will return a 400 error.” Without tools, the same field is
ignored if you send it
(Thinking Mode — DeepSeek API Docs).
OpenAI returns no reasoning text on Chat Completions; on Responses the state is an
encrypted_content blob or lives server-side behind previous_response_id. Anthropic
returns thinking blocks with a signature; Gemini attaches thought signatures to parts.
The previous post covered those three state models. The point here is narrower: a gateway
that translates reasoning_effort on the way in also picks one of four field shapes on the
way out, and on one of the four a wrong pick is a 400 on the next turn.
Check the reasoning-token count, then run the ladder
Every vendor bills reasoning as output and reports it separately. The field names differ. The meaning does not.
| Surface | Where the count is | Documented in |
|---|---|---|
| OpenAI Responses | usage.output_tokens_details.reasoning_tokens | Reasoning models — OpenAI API |
| OpenAI-compatible Chat Completions (DeepSeek and others) | usage.completion_tokens_details.reasoning_tokens | Create Chat Completion — DeepSeek API Docs |
| Anthropic Messages | usage.output_tokens_details.thinking_tokens | Extended thinking — Claude Platform Docs |
| Gemini generateContent | usageMetadata.thoughtsTokenCount | Gemini API reference — UsageMetadata |
| Gemini Interactions | usage.total_thought_tokens | Gemini thinking — Gemini API |
The ladder is the same prompt at every effort spelling, reading that field each time. Over an OpenAI-compatible surface, five lines:
for e in none minimal low medium high xhigh max; do
jq -n --arg m "$MODEL" --arg e "$e" --arg p "$PROMPT" \
'{model:$m, reasoning_effort:$e, messages:[{role:"user", content:$p}]}' |
curl -s "$BASE_URL/chat/completions" -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" -d @- |
jq -r --arg e "$e" '[$e, (.usage.completion_tokens_details.reasoning_tokens // "-"), (.error.message // "ok")] | @tsv'
doneRead the three columns as clues, not a verdict. If counts rise across repeated runs, compare the pattern with the translation tables in row (b); the counts alone cannot distinguish pass-through (a) from translation (b). A flat ladder may suggest (d), but first check whether the provider supports the parameter, whether the gateway forwarded it, and whether the task was sensitive enough to effort to show a difference. A dash with an error message points to (e). To test for (c), send a numeric budget instead of the word and check across repeated runs whether the count equals it. Use a prompt that needs a few hundred reasoning tokens at the bottom rung, because trivial prompts can look flat even on real dials; run each rung three times, as both measurements above did. Cost: seven rungs, three runs, twenty-one calls; well under a cent at a Flash-class output price, a few dollars at Astra’s. Either way it is cheaper than one day of a wrong global default.
For native Anthropic and Gemini surfaces the same loop works with the field names from the table; the endpoint and the parameter name change, the idea does not.
What a gateway can honestly translate, and what it has to refuse to fake
A gateway can honestly translate four things. Field names and positions: reasoning_effort
to reasoning.effort to output_config.effort to thinking_level is a rename, and a rename
is safe. Response shape: the four response shapes can be normalised into one field as long
as the raw blob is kept alongside, the way reasoning_details keeps the
original format tag. The meter: five field names into one number, no arithmetic. And
rejection: a 400 from upstream should arrive as a 400, with the upstream’s message, because
the client that sent none to Astra needs to learn that from the response, not from the
invoice.
It cannot honestly do four others. A word into a number, because there is no unit; every
table in row (b) is somebody’s opinion, and the honest version of (b) is to publish the
table and let you override it with an explicit budget. An off switch on a model that cannot
be switched off. A parameter confirmed to be ignored upstream presented as one that worked;
if the meter stays flat, investigate before claiming the parameter was ignored. And a budget_tokens on a
Claude model that returns 400 for it.
If you set one effort value for the whole application, three rules, each with its price. Set it per model, not per app; the cost is one line of config per model in your routing table. Hold it constant within a cached conversation on Claude, or use the per-message effort form where the model supports it; the cost of not doing so is one cache write per change, 3,546 tokens in Anthropic’s own example. And read the reasoning-token count on every response; if repeated counts stay flat across effort levels on a sufficiently demanding task, flag possible non-effect and inspect parameter support and pass-through before calling it (d). The cost is one field in your logging; a flat count is a prompt to investigate, not proof of an ignored setting.
The gateway we are building is designed to do the first list and refuse the second: pass the value through unchanged, surface the upstream’s own reasoning-token count on every response, and return the upstream’s 400 rather than invent a mapping it cannot verify. That describes what we are building, not a shipped capability.
Run the ladder tonight on each model in your routing table. Paste the three columns into a comment next to the effort line in your config. The next person to touch that line will see which of the five physics it is, before the bill does.
No single number works across providers — that is the argument. A starting position does: do not open at the top gear. The Zenn ladder’s top gear scored 2.46 points below the one beneath it for $1.37 and 6.5 minutes more per run, and DeepSeek’s curve buys its last eight points at roughly 2.5× the output tokens. Start one rung down, and let a failing task, not a hunch, move the dial up.