Claude, OpenAI, and Gemini each let you control how much internal reasoning a model does before it answers, but the three knobs work nothing alike: Claude’s older models take a hard token count called budget_tokens, OpenAI’s models take a qualitative label called reasoning.effort, and Gemini takes a similar label called thinking_level. All three are converging toward the same idea — a named effort tier instead of a raw number — but they are not at the same point in that migration, and mixing up which API expects which shape is the fastest way to get a 400 error.
Image credit: PattayaPatrol, licensed under CC BY-SA 4.0, via Wikimedia Commons.
Claude: two generations, two different parameters
Claude’s manual thinking mode — still the only mode on Claude 4.5 and earlier models that support thinking — takes a thinking object with a hard token count:
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=16000,
thinking={"type": "enabled", "budget_tokens": 10000},
messages=[{"role": "user", "content": "Are there an infinite number of prime numbers such that n mod 4 == 3?"}],
)
budget_tokens is a target, not a hard cap on its own — Claude can stop thinking well before it’s used — but it must be below max_tokens, since thinking tokens are billed as part of that same ceiling. The minimum accepted value is 1,024 tokens; anything lower is rejected outright. Anthropic’s own guidance is to start near 1,024 for simple tasks and 16,000 or more for complex ones, and to move anything above roughly 32,000 to batch processing, since large budgets risk hitting networking timeouts on a synchronous request.
Claude 4.6 models (Opus 4.6, Sonnet 4.6) still accept type: "enabled", but it is deprecated on them. Claude 4.7 and later reject it with a 400 error whose message starts with "thinking.type.enabled" is not supported — which is the exact signal that a model has moved to adaptive thinking and no longer takes a token budget at all. The newer shape drops budget_tokens entirely in favor of a qualitative effort level, set through a separate output_config object:
{
"model": "claude-sonnet-4-6",
"max_tokens": 16000,
"thinking": { "type": "adaptive" },
"output_config": { "effort": "high" }
}
Adaptive thinking decides on its own, per request, whether to think and how long — the effort field only sets the ceiling it is allowed to reach. Switching a deployed integration from manual to adaptive thinking invalidates any prompt cache breakpoints tied to the old request shape, which is easy to miss if you migrate gradually across environments.
OpenAI: reasoning.effort, from none to max
OpenAI’s Responses API never used a token budget — it has always taken a named effort string, and the list of accepted values has grown over time. As of the current docs, reasoning.effort accepts none, minimal, low, medium, high, xhigh, and max, though which values a given model accepts varies — some reject none or minimal outright with an HTTP 400.
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-6-astra",
reasoning={"effort": "low"},
input="Write a bash script that transposes a matrix.",
)
print(response.output_text)
Reasoning tokens are never shown through the API, but they still occupy the context window and are billed as output tokens — the exact count is reported in usage.output_tokens_details.reasoning_tokens. If a response hits max_output_tokens mid-reasoning, its status becomes incomplete, which is why OpenAI’s own guidance is to reserve at least 25,000 tokens for reasoning plus output when you’re still tuning a new integration.
Gemini: thinking_level, and it’s model-dependent
Gemini models think dynamically by default — the model itself decides how much effort a request needs — and thinking_level inside generation_config overrides that default:
from google import genai
client = genai.Client()
interaction = client.interactions.create(
model="gemini-3.8-flash",
input="Provide a list of 3 famous physicists and their key contributions",
generation_config={"thinking_level": "low"}
)
print(interaction.output_text)
The catch is that which levels a given Gemini model accepts is not uniform. Some models support only low, medium, and high; others add a minimal tier; and the default “on” level differs by model too — gemini-3-pro-preview defaults to high with no minimal option at all, while gemini-3.5-flash-lite defaults to minimal. There is also no explicit “off” switch for most models — gemini-2.5-flash-lite is the one model in the current lineup where thinking defaults to off, and for every other model the lowest supported level is the closest thing to disabling it. max_output_tokens counts thinking tokens here too, and hitting the cap mid-thought gives you an incomplete or empty response rather than a trimmed one.
Side-by-side comparison
| Provider | Parameter | Shape | Accepted values (varies by model) | Billed as |
|---|---|---|---|---|
| Claude (manual, 4.5 and earlier) | thinking.budget_tokens | Integer token count | 1,024 minimum, below max_tokens | Output tokens |
| Claude (adaptive, 4.6+) | output_config.effort | Named tier | Model-dependent tiers, exact ceiling set by the model | Output tokens |
| OpenAI (Responses API) | reasoning.effort | Named tier | none, minimal, low, medium, high, xhigh, max (model-dependent) | Output tokens (reasoning_tokens) |
| Gemini | generation_config.thinking_level | Named tier | minimal, low, medium, high (model-dependent subset) | Output tokens, inside max_output_tokens |
Before and after: migrating a Claude integration off budget_tokens
Before, a pinned integration on an older Claude model sets a fixed token budget and tunes it by trial and error per use case:
thinking={"type": "enabled", "budget_tokens": 10000}
After, moving to a model where manual mode is deprecated or rejected replaces the token count with an effort label and relocates it to a separate config object:
thinking={"type": "adaptive"},
output_config={"effort": "high"}
The practical effect: you stop tuning a number per prompt and start tuning a tier per task category, which is closer to how OpenAI’s and Gemini’s parameters already worked. If your code branches on model name to decide which shape to send, the detection signal is the 400 error text itself — "thinking.type.enabled" is not supported" — rather than trying to maintain a hardcoded list of which Claude versions take which shape.
Picking a setting instead of guessing
- Latency-critical paths (classification, voice, fast retrieval): OpenAI’s
none, Gemini’s lowest supported level, or skip Claude’s thinking parameter entirely. - Everyday agentic work (tool use, planning, support chat): OpenAI’s
low, Gemini’slowormedium, Claude’s budget in the low thousands or adaptiveeffort: "medium". - Hard debugging or multi-step planning: OpenAI’s
high, Gemini’shigh, Claude’s budget at 16,000+ or adaptiveeffort: "high". - Long-running research or deep agentic tasks: OpenAI’s
xhighormax, Claude batch processing with a large manual budget on models that still accept one.
Why the token-budget approach is fading
A raw token budget asks the developer to know something they usually can’t know in advance: how many tokens a specific prompt actually needs to reason through well. Set it too low and the model cuts itself off mid-chain of thought; set it too high “to be safe” and every request pays for reasoning it didn’t need, since thinking tokens are billed whether or not they improved the answer. A named effort tier moves that judgment call inside the model itself — the provider calibrates what “high” means for that specific model version, and a prompt that needs more or less reasoning within a tier gets it without the developer re-tuning a number. That is also why Claude’s adaptive mode can decide not to think at all on a simple request even when effort is set to high: the tier is a ceiling on how far the model may go, not an instruction to always use it.
The trade-off is predictability. A fixed budget_tokens value gives you a hard upper bound on latency and cost for a given request shape, which matters for anything with a tight response-time budget of its own, like a voice interface or a synchronous API gateway with its own timeout. A named tier gives you a looser guarantee — “similar cost and latency to other `high` requests on this model” — which is fine for a batch job and risky for anything time-sensitive unless you’ve measured the tier’s actual latency distribution for your specific prompts first.
FAQ
Can I see the actual reasoning tokens a request used? Not the content, on any of the three — OpenAI and Gemini never expose raw reasoning tokens through the API, and Claude’s “thinking” blocks return a summarized version, not the full chain. All three do report the token count: Claude as part of usage, OpenAI as usage.output_tokens_details.reasoning_tokens, and Gemini inside the response’s standard usage metadata.
Does a higher effort setting always improve output quality? Not reliably enough to set it to the maximum by default — all three vendors frame higher tiers as a latency-and-cost trade for tasks where evals show a clear benefit, not a free quality upgrade, which is why OpenAI’s own guidance is to compare xhigh against max rather than assume the top tier wins.
This builds on how the three APIs already differ in function-calling schema and in how they report a truncated response — reasoning tokens are one more place a request can hit a ceiling mid-turn, and the error shape differs by provider there too. If you’re comparing providers on cost as well as reasoning depth, it’s worth reading these settings alongside how each one prices and rate-limits its API tiers, since reasoning tokens are billed as regular output tokens on all three and can quietly dominate a bill if a tier is left too high in production.
Sources: Anthropic’s extended thinking documentation, OpenAI’s reasoning guide, and Google’s Gemini thinking documentation.



