How to Migrate to the Claude Opus 5 API: Breaking Changes, 400 Errors, and a Production Checklist
A Claude Opus 5 migration guide from a team that did it on launch day. Every breaking change in the claude-opus-5 API, the exact 400 errors you'll hit coming from Opus 4.8, and the fixes with working Python code.
Anthropic released Claude Opus 5 yesterday. The pricing stayed at Opus 4.8 levels ($5 input / $25 output per million tokens), the context window stayed at 1M tokens with 128K max output, the knowledge cutoff moved to May 2026, and the benchmarks in the announcement beat Fable 5 in several places at half its price. Anthropic is positioning it as the default model for agentic coding and enterprise work.
We run several client agent stacks on claude-opus-4-8, so I spent launch day migrating them. If you searched "how to migrate to Claude Opus 5" and landed here, this is the complete list of what breaks, why, and what we changed. The model ID swap takes one line. The other four changes are where the real work is.
Breaking change 1: thinking is now on by default
On Opus 4.8, a request without a thinking field ran without thinking. On Opus 5, the same request runs with adaptive thinking enabled. Nothing errors and your code appears to work. The problem is that max_tokens caps total output, thinking plus response text, so a request that fit comfortably in max_tokens: 4096 yesterday can spend most of that budget thinking and come back truncated with stop_reason: "max_tokens".
We hit this on a summarization route that had max_tokens: 2000 and no thinking field. It started returning summaries that stopped mid-sentence. You have two options:
# Option A: keep thinking, give it room
response = client.messages.create(
model="claude-opus-5",
max_tokens=16000, # was 2000; thinking counts against this now
messages=[{"role": "user", "content": doc}],
)
# Option B: restore the old no-thinking behavior explicitly
response = client.messages.create(
model="claude-opus-5",
max_tokens=2000,
thinking={"type": "disabled"},
output_config={"effort": "high"},
messages=[{"role": "user", "content": doc}],
)
One catch on Option B cost me twenty minutes: disabling thinking only works at effort high or below. Combine thinking: {"type": "disabled"} with effort: "xhigh" or "max" and the API returns a 400. Anthropic coupled these on purpose. The extra capability at the top effort levels comes from reasoning, so you can't have one without the other.
Breaking change 2: temperature and top_p return 400 errors
temperature, top_p, and top_k are gone. Any non-default value is a hard 400, the same as on Opus 4.7 and 4.8. We still had one legacy classification route pinned to temperature: 0.2 from the Opus 4.6 days, and it died immediately after the model swap.
The replacement is prompting. If you were using low temperature for consistency, you probably need nothing at all, because Opus 5 follows instructions more literally than any Claude before it. If you were using high temperature for variety, ask for variety in the system prompt ("vary your phrasing and structure across responses"). This felt wrong to me for about a day. Then I noticed the outputs were more controllable than they ever were with sampling knobs.
Breaking change 3: assistant prefills return 400 errors
If you're coming from anything older than the 4.6 family, the prefill trick of ending messages with a partial assistant turn like {"role": "assistant", "content": "{\"category\": \""} is a 400 on Opus 5. Structured outputs replace it, and the schema is enforced rather than hinted:
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
output_config={
"format": {
"type": "json_schema",
"schema": {
"type": "object",
"properties": {
"category": {"type": "string", "enum": ["billing", "technical", "sales"]},
"urgency": {"type": "integer", "enum": [1, 2, 3]},
},
"required": ["category", "urgency"],
"additionalProperties": False,
},
}
},
messages=[{"role": "user", "content": ticket_text}],
)
Breaking change 4, the silent one: Claude Opus 5 token counts
Opus 5 uses the tokenizer introduced with Opus 4.7. Migrating from 4.7 or 4.8? Counts are roughly unchanged. Jumping from Opus 4.6 or older? The same text produces roughly 1x to 1.35x more tokens, which quietly shifts every token-denominated assumption in your stack: max_tokens values, compaction triggers, cost dashboards, rate-limit math. Skip the blanket multiplier and run client.messages.count_tokens() against claude-opus-5 on a representative sample of your real prompts.
What the Claude Opus 5 API adds
The migration isn't all defensive. We turned on three new capabilities the same day.
Mid-conversation system messages. You can now append {"role": "system", "content": "..."} to the messages array after a user turn. This is the right way to inject operator instructions mid-session. It preserves your cached prefix instead of invalidating it the way editing the top-level system prompt does, and the model treats it with system authority rather than as user text.
Refusal fallbacks. Opus 5 ships with real-time cybersecurity classifiers, and a declined request comes back as HTTP 200 with stop_reason: "refusal". The new beta fallbacks: "default" parameter re-runs a refused request automatically. We wrote a separate post on building refusal-resilient pipelines, because for security-adjacent work it deserves one.
A lower prompt caching minimum. The floor for cacheable prompts dropped from 1,024 tokens to 512. Several of our smaller tool-heavy prompts that silently never cached now do. No code change needed. The cache_control markers we already had just started working.
One prompt change worth making
Opus 5 self-verifies. All the "double-check your output against the schema before responding" lines we accumulated over two years of prompt engineering now cause over-verification, where the model checks its work twice and bills you for both passes. We deleted them route by route and A/B tested against the old prompts. The shorter prompts won on cost and latency with no quality drop. The least intuitive migration advice I can offer: your Opus 5 prompts should be shorter than your Opus 4.8 prompts.
The Claude Opus 5 migration checklist we used
- Swap the model ID to
claude-opus-5 - Find every request with no
thinkingfield; raisemax_tokensor disable thinking explicitly - Audit for
thinking: disabledcombined with effort abovehigh(400 error) - Strip
temperature,top_p,top_k(400 error) - Replace prefills with
output_config.format(400 error) - Re-baseline token counts with
count_tokensif coming from Opus 4.6 or older - Delete self-verification scaffolding from prompts
- Add
stop_reason == "refusal"handling before readingcontent
Total time for our stack was one afternoon, most of it spent rerunning evals rather than editing code. The 400 errors are the easy part because they announce themselves. The silent thinking-by-default change is the one that will bite teams who swap the model ID, see green tests, and call the migration done.
Related reading
An independent comparison of the three agentic models that launched this month. Real pricing, production failure modes, cost per completed task, and the routing table we actually run — not a benchmark aggregation.
A step-by-step tutorial for bulk AI document extraction with the Claude Opus 5 Batch API: structured outputs with JSON schemas, 50% batch pricing, the 300K output beta, and the real cost of processing 40,000 vendor contracts.
How to use the Claude Opus 5 effort parameter (low, medium, high, xhigh, max) to cut Claude API costs. Real per-request cost numbers, working Python code, and why we retired our Haiku/Sonnet/Opus routing layer.
Let's build something great.
Have a project in mind? We are an elite software and AI development studio ready to bring your ideas to production. Let's talk about your roadmap.