Blog

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.

Leo Kaka21 min readUpdated Oct 12, 2026
Matrix of the five things that can happen to a reasoning_effort value after a 200 response: passed through, translated into a token budget, honoured to the token, accepted and ignored, rejected with 400, with where each is documented and what the reasoning-token meter does

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.”

Three line charts from DeepSeek's V4.1 Flash technical report: Pass@1 (solid) and mean output tokens (dashed) against reasoning effort from 25 to 100, for eight reasoning benchmarks averaged, DeepSWE v1.1 and Terminal-Bench v2.1. Accuracy and token count rise together on all three; the output-token line steepens after 80.
Fig. 1 — DeepSeek's own effort curve: Pass@1 (solid, left axis) and mean output tokens per response (dashed, right axis) as the effort value runs from 25 to 100. The public API exposes three points on it: 50, 75 and 100.Source: DeepSeek V4.1 Flash Technical Report, Figure 9

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.

EffortMean scoreStd. dev.Median timeChild costMedian output tokens
low90.887.086.5 min$0.9907.1k
medium91.765.777.6 min$0.9818.2k
high100.000.0014.6 min$1.59715.1k
xhigh97.540.0021.1 min$2.96929.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.

#PhysicsWhere it is written downWhat the reasoning-token count does
a✓Passed through as an enumOpenAI-compatible surfaces; one gateway: “forwarded to the provider as sent,” neither validated nor guaranteedMoves, if the model has that tier
b⚠Translated into a token budgetGoogle’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,096Moves, in steps the translator chose
c✓Honoured to the tokenA 13-model probe, August 2026: thinking_budget: 16 burned exactly 16 on Qwen 3.8 Max, GLM 5.2 and DeepSeek V4Equals what you sent
d✗Accepted and ignoredSame 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 notesFlat, billed anyway
e✗Rejected with 400GPT-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 0Nothing 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.

SurfaceWhere the count isDocumented in
OpenAI Responsesusage.output_tokens_details.reasoning_tokensReasoning models — OpenAI API
OpenAI-compatible Chat Completions (DeepSeek and others)usage.completion_tokens_details.reasoning_tokensCreate Chat Completion — DeepSeek API Docs
Anthropic Messagesusage.output_tokens_details.thinking_tokensExtended thinking — Claude Platform Docs
Gemini generateContentusageMetadata.thoughtsTokenCountGemini API reference — UsageMetadata
Gemini Interactionsusage.total_thought_tokensGemini 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'
done

Read 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.