budget_tokens is not supported on Opus 4.7 and later
The symptom
A request that worked against an older Opus model starts returning a 400 error the moment you
point the same code at Opus 4.7, Opus 4.8, or Opus 5. The request body includes a thinking config
using the budget_tokens field — the mechanism older Opus models (Opus 4.6, Opus 4.5, and earlier)
use to cap how many tokens the model spends thinking before it answers.
The cause
budget_tokens was the thinking-control mechanism before the effort parameter existed. Opus 4.7
introduced effort as its replacement, and from Opus 4.7 onward the newer models do not accept
budget_tokens at all — sending it is rejected outright rather than silently ignored. This is a
clean break, not a deprecation warning period: code written against an older Opus model's thinking
config does not degrade gracefully on the newer ones, it errors.
It's easy to hit this by accident during a routine model upgrade. A team moving from Opus 4.6 to
Opus 5 for the pricing and quality improvements often carries the request-building code over
unchanged, since most of the request shape is identical across the two — the thinking block is
the one place it isn't.
The fix
Replace budget_tokens with the effort parameter. Opus 4.7 and later accept low, medium,
high, xhigh, and max — xhigh did not exist before Opus 4.7 at all, so it's new capability,
not just a renamed field. There is no direct numeric mapping from a budget_tokens value to an
effort level; treat the migration as a re-tuning exercise rather than a mechanical find-and-replace,
starting from high (the recommended default and minimum for anything intelligence-sensitive) and
adjusting from there based on your own quality and cost testing.
One more thing worth checking during this migration: on Opus 5, thinking defaults to on and cannot
be disabled above high effort — the opposite of Opus 4.8, where thinking is off unless you
explicitly request it. On Opus 5.5 it cannot be disabled at all, and omitting effort gives you
medium rather than high. If your code relied on thinking being off by default, moving to either
changes that assumption too, not just the field name — and on Opus 5.5, set effort explicitly
rather than inheriting a default one level lower than the one you tuned against.
Related
See what actually changed from Opus 4.6 to Opus 5 for the fuller migration picture, and migrating off budget_tokens to the effort parameter for the mechanics of the swap itself.
Verified 2026-09-30 against ClaudeHow facts module (src/data/facts/) — see /about/#accuracy.