Opus 5.5 in Practice: Effort Is the Only Dial Left
Every Claude Opus 5.5 request thinks, and you can no longer switch that off. You still control how much it thinks, through one parameter called effort. That leaves you three jobs: pick an effort level per task type, keep the prompt cache warm, and check what a long run changed. This page gives the requests, config, and prompts for each job.
Anthropic shipped Opus 5.5 on September 22 at $4 per million input tokens and $20 per million output. That is down from $5 and $25 on Opus 5, and cache reads fall from $0.50 to $0.20. The model has a 1M token context window by default and 128K tokens of maximum output. It is the default Opus model in Claude Code v2.1.280. The API release notes carry the line that breaks code: thinking: {"type": "disabled"} now returns a 400. Thinking is billed as output whether or not you see it.
This page follows one request from the wire up. It covers the request shape the API accepts and the blocks that come back. Then it covers effort, the dial that sets how much thinking you buy, and what a long run needs from your harness. Each section ends in code or config, checked against Anthropic's docs.
1. Make the request valid
The migration guide lists what every request to claude-opus-5-5 must satisfy. Each item marked "rejected" returns a 400 before the model generates a single token.
Send no thinking field, or send {"type": "adaptive"}, which does the same thing. Use tool_choice auto or none. The API rejects a forced tool with any or tool, also on the token counting endpoint. Leave temperature, top_p, and top_k at their defaults. Do not end messages with a prefilled assistant turn. Here is the diff for a typical Opus 5 extraction route:
{
- "model": "claude-opus-5",
+ "model": "claude-opus-5-5",
- "max_tokens": 1024,
+ "max_tokens": 8192,
- "thinking": {"type": "disabled"},
+ "output_config": {"effort": "low"},
- "tool_choice": {"type": "tool", "name": "extract_invoice"},
+ "tool_choice": {"type": "auto"},
"tools": [{"name": "extract_invoice", "strict": true, "input_schema": {...}}],
"messages": [{"role": "user", "content": "..."}]
}
The thinking field goes because the guide rejects both disabled and manual budgets. The Opus 5.5 prompting guide says to start at low on routes that ran with thinking off, then measure. The max_tokens value goes up because it caps thinking plus text. A limit sized for a short reply now cuts that reply off. Forced tool use becomes auto plus strict tool use, and the prompt says when the tool applies. The 8192 is my own starting value to tune, and no doc gives that number.
Computer use changed shape on the Claude API and Google Cloud. The old tool returns 'claude-opus-5-5' does not support tool types: computer_20251124. The toolset takes no name, no display size, and no beta header:
- "tools": [{"type": "computer_20251124", "name": "computer",
- "display_width_px": 1024, "display_height_px": 768}],
+ "tools": [{"type": "computer_toolset_20260801"}],
Your agent loop changes with it. The action is now the tool_use block's name instead of input.action. Several such blocks can arrive in one turn, and every result must echo toolset_name. Amazon Bedrock still accepts the old tool, so one codebase on two clouds needs a flag that picks the declaration. Claude Code ships a skill that does the mechanical part of this across a repository. It then gives you a checklist for the rest:
/claude-api migrate this project to claude-opus-5-5
The figure lints a request the way the guide does. Pick the model it came from and the platform it runs on. Then click a red line to apply the fix.
A production request body retargeted to claude-opus-5-5. Each line is checked against the migration guide's rules for the platform you pick. Red lines return a 400; amber lines run but change behavior. Click a flagged line to apply its fix.
Error strings and fixes are quoted from the platform.claude.com migration guide and API release notes, Sep 22, 2026. The max_tokens value in the fix is my starting point, and no doc gives it. The guide says to revisit max_tokens because it now covers thinking plus text. It says to start at 64k for xhigh and max.
2. Handle thinking you pay for and cannot turn off
"Adaptive" means the model decides. It chooses whether a turn needs thinking and how much, and effort biases that choice. The effort docs say lower levels can skip thinking on simple problems. They also say tool-result turns in a loop can skip it at any level. So "always on" describes the mode, and a given turn can still use no thinking at all. Three facts on the response side follow, and each has broken a harness that never touched the thinking parameter.
Responses now begin with thinking blocks, so select blocks by type. The thinking text is empty by default. thinking.display defaults to "omitted", which returns a block with an empty thinking field and a signature. You pay for those tokens anyway. Set "summarized" to read summaries.
Progress notes also moved into thinking blocks. The model writes short text between tool calls, and that text used to arrive as text. Now it arrives as a progress-update thinking block, which is empty at the default display, so an agent UI goes quiet. The "updates" display mode returns only those notes. It needs the beta header thinking-display-updates-2026-08-18.
One more rule turns a UI bug into a hard failure. In a tool loop, thinking blocks must go back to the API complete and unmodified, empty ones included. A harness that rebuilds the assistant turn from its text and tool calls gets a 400 on the second request. The loop that gets all of this right is short:
import anthropic
client = anthropic.Anthropic()
messages = [{"role": "user", "content": "Fix the three failing tests in tests/api/ and run them."}]
def run_tool(block):
output = TOOL_IMPLS[block.name](**block.input)
return {"type": "tool_result", "tool_use_id": block.id, "content": output}
while True:
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=64000,
thinking={"type": "adaptive", "display": "summarized"},
output_config={"effort": "medium"},
tools=TOOLS,
messages=messages,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
break
results = [run_tool(b) for b in response.content if b.type == "tool_use"]
messages.append({"role": "user", "content": results})
print("".join(b.text for b in response.content if b.type == "text"))
The line that matters most is messages.append({"role": "assistant", "content": response.content}). It echoes the turn exactly as received, and that is what the API checks. The fields and values match the effort and migration docs. You define TOOLS and TOOL_IMPLS yourself. The figure below runs one read-then-answer turn through three harness choices and shows which combination fails.
One agent turn on Opus 5.5 that reads a file and then answers: two API responses, rendered as the typed blocks they contain. Choose how your code reads text, which display mode it requests, and how it sends the assistant turn back with the tool result.
Block order and rules come from the Opus 5.5 migration guide. Block contents are an illustrative trace. Token counts are placeholders sized for a short read-then-answer turn.
3. Pick an effort level for each task
Effort is now the only request parameter that controls thinking depth. It also shapes the rest of the reply. The docs say it "affects all tokens in the response," and lower levels make "fewer and terser tool calls" than higher levels. Anthropic's prompting guide for Opus 5.5 reports that medium matched or beat Opus 5 at high on its coding and knowledge-work evaluations. It adds that low came close on several coding evaluations. That is vendor testing, so use these levels as starting points and sweep on your own evals:
| Task class | Start at | max_tokens | Why |
|---|---|---|---|
| Extraction, classification, routing, subagents | low | 8k to 16k | Docs name subagents as the low use case. Routes that ran thinking off start here. |
| Most agentic coding and tool use | medium | 32k to 64k | The default; matched Opus 5 at high in Anthropic's testing |
| Hard debugging, complex reasoning | high | 64k | Spends what the task needs. It is the default on every other model. |
| Runs over 30 minutes with million-token budgets | xhigh | 64k to 128k | The docs describe this level for long agentic work, and 128k "has worked well" for long turns |
| Frontier problems with a measured gain | max | 128k | Reserve it for work where your evals show headroom |
The token ranges are my starting points except where quoted. Two mechanics decide whether switching levels costs you money. A new top-level effort value between requests restarts the prompt cache, because effort shapes the rendered prompt. On Opus 5.5 you can change effort per message instead, and that keeps the cache. Add an effort-only system message with the beta header mid-conversation-output-config-2026-07-01. The new level applies from the next user turn.
{"role": "system", "content": [], "output_config": {"effort": "low"}}
In Claude Code the same dial lives in three places. The top-level effortLevel setting no longer applies to Opus 5.5 and newer models. Those models read a per-model entry in settings.json, per the model config docs:
{
"modelSettings": {
"claude-opus-5-5": { "effortLevel": "high" }
}
}
/effort xhigh # set the level for the active model: low, medium, high, xhigh, max
/effort auto # clear the saved level for this model
export CLAUDE_CODE_EFFORT_LEVEL=low # scripts and CI
If a route ran Opus 5 with thinking disabled for cost, ask whether always-on thinking now costs more than it saves. The figure answers that for one request shape. Set your token counts. Then enter the thinking tokens you measured at low.
One request priced three ways at list prices: Opus 5 with thinking disabled, Opus 5 with thinking on at the same thinking volume, and Opus 5.5, where thinking is always on. The rail marks the thinking volume where Opus 5.5 stops being cheaper than Opus 5 with thinking off. Thinking tokens are yours to measure; the default is a placeholder.
Prices per million tokens come from anthropic.com/news/claude-opus-5-5. Opus 5 is $5 in, $25 out, and $0.50 cache read. Opus 5.5 is $4 in, $20 out, and $0.20 cache read. The figure leaves out cache writes, batch discounts, and fast mode. Thinking tokens are billed as output tokens per the migration guide.
The break-even has a closed form. Let I be uncached input tokens, C cached, V visible output, and T thinking. Opus 5 with thinking off costs 5I + 0.5C + 25V per million, and Opus 5.5 costs 4I + 0.2C + 20(V + T). So Tbreak-even = (I + 0.3C + 5V) / 20. Cheap cache reads push it up. A long cached prefix leaves a lot of room for thinking, and a short uncached classifier call leaves very little.
If a route lands above the line at low, the prompting guide suggests one more lever. Add the system prompt line Answer directly without deliberating. to reduce thinking further. Measure quality when you add it.
4. Move reasoning control out of the prompt
On Opus 5 a prompt was one of the few ways to steer how hard the model worked. On Opus 5.5 that control has a documented home, the effort parameter. The thinking block is its documented output. Prompt lines that tried to do either job now duplicate the mechanism, and one kind collides with a refusal category. Find them with a search before you judge them by eye:
grep -rniE "step by step|think (carefully|hard)|show (your )?(reasoning|work)|explain your reasoning" prompts/ src/**/*.txt
| Line in the prompt | Move the job to | Why, from the docs |
|---|---|---|
| Asks for more deliberation | output_config.effort, one level up | The effort docs make effort the control for thinking depth under adaptive thinking. Wording does not change the budget. |
| Asks the model to print its reasoning before the answer | thinking.display: "summarized" | The migration guide lists reasoning_extraction as a refusal category for prompts that push the model to reproduce its reasoning |
| Asks for a status paragraph every N tool calls | thinking.display: "updates" (beta) | Progress notes are emitted as thinking blocks between tool calls |
| Tells the model not to think | effort: "low" | Thinking cannot be disabled. The lowest effort is the documented way to spend least on it. |
Routing then moves out of the prompt and into a table in code. Every call site reads one map. The map keeps each route on one effort level for its whole conversation, which also keeps its cache prefix stable:
EFFORT_BY_ROUTE = {
"classify_ticket": "low",
"extract_invoice": "low",
"code_agent": "medium",
"incident_debug": "high",
"overnight_migration": "xhigh",
}
def create(route, **kwargs):
return client.messages.create(
model="claude-opus-5-5",
output_config={"effort": EFFORT_BY_ROUTE[route]},
**kwargs,
)
One prompt change is still worth making, for pasted text. The prompting guide recommends wrapping text a user pasted from elsewhere in tags with an ID your app generates. The system prompt then tells the model that instructions inside those tags belong to the pasted source. Generate the ID per paste so a document cannot close the tag itself:
import secrets
def wrap_paste(text):
tag = secrets.token_hex(4)
return f'<pasted_content id="{tag}">\n{text}\n</pasted_content id="{tag}">'
5. Keep long runs moving
The migration guide notes that Opus 5.5 reports progress as it works. Some of those reports end a turn as text with stop_reason: "end_turn" instead of a tool call. A loop that treats end of turn as completion therefore stops partway. The fix belongs in the harness. Only the harness can compare the model's claim with the state of the work.
Give the run a machine-readable state file. A prose plan inside the conversation disappears at compaction, and code cannot check it. A small JSON file in the repository survives compaction, and code can read it. Put the contract in CLAUDE.md:
## Run state
- The run's state lives in .run/state.json with keys "open", "done", and "blocked".
Each item is {"id": ..., "what": ..., "evidence": ...}.
- Move an item to "done" only with evidence: the test command and its result, or the file and line.
- Move an item to "blocked" only with the question that unblocks it.
- The run is finished when "open" is empty and every "done" item has evidence.
Decide continuation in code. After a text-only end of turn, the harness reads the state file. If items are open and none is blocked, the model paused, so the harness sends one short nudge. A blocked item means a human is needed. An empty open list means the run is done. Cap the nudges so a stuck run ends and gets a review:
import json
def next_step(response, nudges, cap=3):
if response.stop_reason == "tool_use":
return "run_tools"
state = json.load(open(".run/state.json"))
if state["blocked"]:
return "ask_human"
if not state["open"]:
return "done"
return "nudge" if nudges < cap else "review"
NUDGE = "Open items remain in .run/state.json. Pick the next one and act on it."
Add any standing system-prompt instruction before the first request. A system prompt change mid-session invalidates earlier thinking blocks and restarts the cache. Opus 5.5 also responds to elapsed-time information. A harness that appends elapsed 340s / 1200s to each message gives it a budget to pace against. Treat that budget as advisory and keep your own timeout.
For interactive sessions in Claude Code, the fast mode docs list Opus 5.5 at $8 and $40 per million tokens. Enable fast mode when a session starts. If you enable it later, the existing context is billed at the fast uncached input price.
6. Check what the run changed
A long run ends with a summary, and that summary is the model's own account of the work. Most of the checks that matter run without a model. Start with scope and tests on a clean checkout:
git diff --stat main...HEAD
git diff --name-only main...HEAD | grep -vE '^(src/routes|test/routes)/' && echo "OUT OF SCOPE"
git stash --include-untracked && npm ci && npm test
Then review for intent, with a reviewer that never saw the author's reasoning. Use a fresh session or a subagent that receives only the task's definition of done and the diff. Ask it to trace each hunk to a requirement. That turns "anything suspicious?" into a question with a checkable answer:
Definition of done: {paste the task's done criteria}
For every hunk in the attached diff, name the requirement above that it serves.
Output a table: file, hunk range, requirement, or NONE.
Every NONE row is a finding. After the table, name each requirement that no hunk serves.
Hunks with no requirement are the extra edits that long runs tend to collect. Requirements with no hunk are work the summary claimed but did not do. Then check the bill. In Claude Code, /context shows where the window went. /cost names the causes of cache misses, including thinking-mode and display changes since v2.1.280.
7. A worked example: migrating 38 route handlers
Take a realistic long run. An Express service has 38 route handlers on a callback-style database client. The task is to move them to the promise API and keep every test green. It runs in Claude Code on API billing, so every token has a list price. The token counts below are assumptions for a run of this size, and I did not measure them. The prices are Anthropic's.
Task: Move every handler in src/routes/ from db.query(sql, cb) to await db.query(sql).
Done means: npm test passes, no handler still passes a callback, and the diff touches
nothing outside src/routes/ and test/routes/. Keep run state in .run/state.json.
Set /effort medium, because this is standard agentic coding. Use xhigh if a first pass misses edge cases. Assume 150 model requests. Each request reads about 104,000 tokens of cached prefix and writes about 6,000 new tokens to the cache. It produces 900 visible tokens, plus 1,600 thinking tokens at medium or 4,000 at xhigh. Cache writes cost 1.25 times the input price, which is $5 per million on Opus 5.5 and $6.25 on Opus 5.
| Setup | Per request | 150 requests |
|---|---|---|
| Opus 5.5, medium | $0.0208 read + $0.0300 write + $0.0500 out = $0.1008 | $15.12 |
| Opus 5.5, xhigh | $0.0208 + $0.0300 + $0.0980 out = $0.1488 | $22.32 |
| Opus 5, same tokens | $0.0520 + $0.0375 + $0.0625 out = $0.1520 | $22.80 |
| Opus 5.5, medium, cache rebuilt every request | $0.5500 write + $0.0500 out = $0.6000 | $90.00 |
At these assumptions, the step from medium to xhigh costs about $7 on the whole run. That is cheap if it saves one round of fixes. The cache matters more than the effort level. Each time the cached prefix is thrown away, rewriting 104,000 tokens costs about $0.52, against $0.02 to read them. Ten top-level effort switches mid-run add about $5. Editing the system prompt or the tool list every turn gives the $90 row.
The Opus 5 row assumes identical token counts. That is conservative for Opus 5.5, since the prompting guide says it tends to finish the same task with fewer tokens.
At the end, npm test must pass on a clean checkout as well as in the session. A search for calls that still pass a callback must return nothing: grep -rnE "db\.query\(.*, *(function|\(err)" src/routes/. The scope check must print nothing. .run/state.json must have an empty open list, with evidence on every done item. The traceability review must have no NONE rows.
When it goes wrong
| Symptom | Cause | Fix |
|---|---|---|
400: "thinking.type.disabled" is not supported for this model. | Thinking can no longer be disabled or budgeted | Remove the field and set output_config.effort |
400: tool_choice: type "tool" and "any" are not supported | Forced tool use was removed | auto plus strict tool use, and a prompt line that says when the tool applies |
| 400 on the second request of every tool loop | The harness rebuilt the assistant turn and dropped thinking blocks | Append response.content as received |
| Replies cut off mid-sentence | max_tokens sized for replies without thinking | Raise it to 64k at xhigh and max, and up to 128k for long agentic turns |
| Agent UI silent between tool calls | Progress notes are empty thinking blocks at the default display | display: "updates" (beta) and render those blocks |
| Unattended agent stops after a progress summary | A text-only end_turn read as completion | A state file the harness reads, plus a capped nudge |
stop_reason: "refusal", category reasoning_extraction | The prompt asks the model to write out its internal reasoning | Remove that instruction; read summarized thinking instead |
| A cheap route got more expensive | It ran Opus 5 with thinking off, and now it thinks | Measure at low and place it on Fig. 3. If it lands above the line, keep it on Opus 5 |
| Cache hit rate drops mid-conversation | Top-level effort changed, or the system prompt or tools were edited | Change effort per message and keep the conversation append-only |
| A fallback model answers without context | On the Claude API, only Fable 5.1 and Mythos 5.1 read Opus 5.5 thinking blocks | Route fallbacks to those, or accept the lost reasoning |
A migration order that follows the request
Each step depends on the one before it, so do them in this order. A route that still sends a disabled thinking field gets a 400, so its prompt and effort level never come into play.
- Make every request valid. Remove
thinkingfields that disable or budget thinking. Replace forcedtool_choicewithautoplus strict tools. Raisemax_tokens. Declarecomputer_toolset_20260801on the Claude API and Google Cloud. Fig. 1 shows what still returns a 400. - Make every response readable. Select blocks by
type. Append assistant turns exactly as received. Choose thethinking.displaymode your interface can render. Fig. 2 shows which combination survives a tool loop. - Give every route an effort level. Keep one map in code and one level per conversation. Change effort mid-conversation only through the effort-only system message. Measure thinking on routes that used to run with it off. Place those routes on Fig. 3.
- Move reasoning control out of prompts. Run the grep. Move each hit to effort or display. Wrap pasted text in tags.
- Put long runs on a state file. Add the CLAUDE.md contract,
next_step()in the harness, and a cap on nudges. - Check the change and the bill. Run scope and tests on a clean checkout. Get a traceability review from a fresh reviewer. After the first long run, read
/costfor cache-miss causes.
Addy Osmani's Getting the most out of Opus 5.5 covers the product side of the same launch, for chat users and interactive coding sessions.
Keep reading