The Overthinking Tax

OpenAI calls it reasoning_effort. Anthropic calls it effort, five levels from low to max. Google calls it thinking_budget, a raw token count. Three vendors, three parameter names, one identical mechanic: every reasoning model bills its internal thinking as output tokens, whether anyone reads it or not, and left untouched it defaults to the same level on a two-second classification as a multi-file code review. Here's the parameter, the code, and the decision rule for each provider, plus what changes when a router sets the dial per request instead of per deployment.

Published 2026-07-22 by Dor Amir on the Nadir blog.

Filed under Pricing & Models.

Same lever, three names, one invoice

Every frontier reasoning model now ships a dial for how much it thinks before it answers, and every vendor calls it something different. OpenAI's o-series and GPT-5.5 reasoning tier take a reasoning_effort string. Claude Opus 4.8 takes an effort string with five levels. Gemini 2.5 Pro and Flash take a thinking_budget integer, a raw token count instead of a label. Three parameter names, three shapes, one identical mechanic underneath: the model reasons internally before it writes the answer you see, and that internal reasoning is billed as output tokens, at output rates, whether anyone ever reads it or not.

A typical o-series call generates 3x to 10x more reasoning tokens than the visible response contains. Nobody sees those tokens by default. Everybody pays for them.

One dial, three names: OpenAI's reasoning_effort, Anthropic's effort, and Google's thinking_budget, compared side by side
One dial, three names: OpenAI's reasoning_effort, Anthropic's effort, and Google's thinking_budget, compared side by side

Most teams never touch the dial. They call the reasoning model, get an answer, and move on, reasoning at whatever level the SDK defaults to, on every call, for every task, regardless of whether the task needed deep reasoning or a two-second classification. This is the tutorial for turning the dial down, provider by provider, with the code to do it.

The three parameters, side by side

ProviderParameterValuesDefaultDocs
OpenAI (o-series, GPT-5.5 reasoning tier)reasoning_effortminimal · low · medium · highmediumOpenAI, "Reasoning models"
Anthropic (Claude Opus 4.8)effortlow · medium · high · extra · maxhighClaude API Docs, "Effort"
Google (Gemini 2.5 Pro / Flash)thinking_budget0* – model max, or -1 for dynamicdynamic (-1)Google, "Gemini thinking"

*thinking_budget: 0 disables thinking entirely on Flash-tier models. Pro-tier models require a minimum non-zero budget and can't be fully turned off. Exact supported values vary by model snapshot, so check the docs for the specific model you're calling before you ship a hardcoded level.

Note what's consistent across all three: none of them expose a separate line item for reasoning tokens in the response object. The number folds into output_tokens (or the provider's equivalent), so the only way to see what the dial is actually costing you is to instrument it yourself, the same problem this blog has already covered for Claude specifically.

Setting the dial, provider by provider

OpenAI:

import openai

client = openai.OpenAI()

response = client.chat.completions.create(
    model="gpt-5.5",
    reasoning_effort="low",  # minimal / low / medium / high
    messages=[{"role": "user", "content": task_prompt}],
)

Anthropic:

import anthropic

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-opus-4-8",
    max_tokens=4096,
    effort="low",  # low / medium / high / extra / max
    messages=[{"role": "user", "content": task_prompt}],
)

Google:

from google import genai
from google.genai import types

client = genai.Client()

response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents=task_prompt,
    config=types.GenerateContentConfig(
        thinking_config=types.ThinkingConfig(thinking_budget=0)  # off, Flash-tier only
    ),
)

Three SDKs, three call shapes, one line each. The hard part was never the code. It's remembering the line exists, and deciding what value belongs on which call site.

The decision most teams skip

Setting a global default and forgetting it is the common failure mode, and it fails in both directions. Leave every call at the vendor default and a document-triage step, a status check, a one-line classification, reasons as hard as a multi-file code review. Hardcode low everywhere to save money and the one call that actually needs deep reasoning, a contract clause interpretation, a multi-step debugging trace, ships a shallow answer because the dial never moved back up.

The fix is the same discipline this blog keeps coming back to for model selection: treat effort level as a routing decision, not a deployment-wide constant. A rough starting split that works across most agent pipelines:

The same cascade logic that routes a request to a cheap model when a cheap model can answer it applies one level down, inside a single model, to how hard that model is allowed to think. Model selection and effort selection are two separate levers on the same bill, and most teams only ever pull one of them.

What changes when a router picks the dial instead of a developer

Hand-maintaining three branches of provider-specific effort logic across a codebase is exactly the kind of per-provider bookkeeping a routing layer exists to absorb. Nadir's proxy accepts one normalized shape and maps it to whichever parameter the routed provider actually expects:

import openai

client = openai.OpenAI(
    base_url="https://api.getnadir.com/v1",
    api_key="YOUR_NADIR_KEY",
)

response = client.chat.completions.create(
    model="auto",
    reasoning={"effort": "low"},  # translated to reasoning_effort, effort,
                                   # or thinking_budget depending on where
                                   # the request routes
    messages=[{"role": "user", "content": task_prompt}],
)

One field on the request, instead of three SDKs and three conditionals in application code. The response's cost header still reflects whatever reasoning tokens actually got spent on that specific call, so switching a task from high to low shows up immediately in the number, not in a monthly invoice three weeks later.

A checklist to run this week

  1. Grep your call sites for reasoning models. Anywhere you call an o-series model, GPT-5.5 in its reasoning tier, Opus 4.8, or Gemini 2.5, check whether an effort or budget parameter is set at all. Most calls have none, which means every one of them is running at the vendor default.
  2. Segment those call sites by task type. Classification and extraction calls almost never need the top effort tier. Multi-step reasoning calls almost always underperform at the bottom one.
  3. Set explicit levels, don't inherit defaults. A default is a decision someone else made for a workload they've never seen. Pin the level per call site instead.
  4. Instrument the delta before and after. Compare the cost header (or your own token accounting) on the same task at two effort levels before rolling the change out broadly. The 3x-to-10x range this post opened with is a range, not a guarantee, and it varies by task.
  5. Move it into routing once the pattern holds. A hardcoded effort level per call site is better than no level at all, but it's still a constant. The same task complexity signal that decides which model to call is usually enough to decide how hard that model should think.

The dial exists on every reasoning model shipping today. The cost of leaving it untouched doesn't show up as an error or a warning, it shows up as a slightly higher number on every single call, compounding silently across however many requests a production pipeline sends in a day. Start free and let routing set the dial per request, or read the complete guide to cutting LLM API costs for the rest of what's on the table.

More on pricing & models

What Nadir is

Nadir is an LLM router. Nadir sends each prompt to the cheapest model that can handle it. A small decision model reads each prompt against your model menu and picks the lowest-cost model it expects to handle it. When the decision model is uncertain or unavailable, the request goes to the strongest model on the menu. The routing step makes no call to your model provider.

Nadir runs two ways. The decision API returns a model, reasoning-effort, cache, context, and policy recommendation without calling a model provider, beside the gateway you already run. That is how a shadow-mode evaluation works, and its projected savings stay advisory. The OpenAI compatible managed proxy executes the route, and migration is a two-line change: point the base URL at api.getnadir.com and set model to auto. On that path an optional verifier can score a complete non-streaming answer and escalate to a stronger model when it misses the configured bar. Streaming bypasses post-generation verification. BYOK is supported on every tier.

For coding agents, Nadir Auto is a terminal launcher for Claude Code or Codex that asks Nadir for the main-session model before each turn, with your configured model as the fallback and cost ceiling. Delegation integrations for Claude Code, Codex, or Cursor instead recommend a model tier for subagent work, and the agent decides; the Claude plugin can also move a main-thread turn to Haiku when Nadir's cache check prices it cheaper, unless its switch_main_model option is off. Either way inference runs on your own provider account, so Nadir holds no provider keys on that path.

What the numbers are, and what they are not

Nadir publishes each evaluation with its scope. On checkable code, run-check-escalate solved 392 of 395 common HumanEval and MBPP problems (99.2%), graded by running the canonical tests; that applies only to tasks with runnable deterministic tests. Nadir-Tumbler posts an arena_score of 72.3 on RouterArena's public scorer, 5th of 23 routers, which measures the routing decision on RouterArena's own model pool. A reference-assisted RouterBench evaluation over 11,420 held-out triples produced a 60% lower projected cost than always-Opus with about 98% retained quality and a 1.7% catastrophic-route rate. That experiment gave the verifier the expensive-model reference answer, which production does not have, so it is a research ceiling and not the deployed path.

None of these is a production guarantee, a universal savings rate, or a forecast for any particular workload. Customer savings are reported from measured execution against a declared baseline, and customer quality only from outcome-labelled traffic. Projected savings and realized savings are separate artifacts and are never blended.

Design-partner program

Three rungs, picked by risk appetite. Rung 0 Shadow runs advisory decision calls alongside live traffic and returns a projected receipt, with nothing in the request path changed. Rung 1 Hosted is the two-line swap on a production slice and returns a realized receipt. Rung 2 On-prem is a supervised six-week proof of concept inside the partner's VPC, where no prompt, response, or usage reaches Nadir. The commitments are the same at every rung. Apply for a rung directly: Rung 0 Shadow, Rung 1 Hosted, or Rung 2 On-prem. Not sure which fits? Start at getnadir.com/contact/?reason=design-partner.

Licensing

NadirClaw is the self-hosted core, source-available under the PolyForm Noncommercial License. Source-available is the correct label; NadirClaw is not open source. Nadir Route's hosted plan has no base fee and charges a variable fee only on measured savings from requests Nadir executed.

Pages on this site

Machine-readable summaries of this site: llms.txt and llms-full.txt.