Model, thinking and effort
effort is the option that says how much reasoning Claude puts into a turn, and model, fallback_model, thinking and betas choose and tune the model doing it.
Last updated: 20 Oct, 2026 · Claude Agent SDK
A schema decides the shape of the answer. These options decide who writes it and how hard they work on it. All of them reach Claude Code as flags, so the command line is the place to check what you asked for.
Choosing a model and a backup
from claude_agent_sdk import ClaudeAgentOptions
from flags import flags
# the backup takes over while the first is overloaded, and is dropped when it recovers
print(flags(ClaudeAgentOptions(model="claude-sonnet-5", fallback_model="claude-opus-5")))
print(flags(ClaudeAgentOptions(effort="low")))model takes an alias or a full model name, and with neither it, your settings nor your environment choosing one, the session starts on Claude Code's default. fallback_model also accepts a comma separated list, and the first model is retried at the start of each user turn: configuration.
total_cost_usd is a literal zero and its token counts are word counts, so a model name in these options changes the command line and no more than that.Turning extended thinking on and off
thinking takes one of three shapes. adaptive lets Claude decide when to think, enabled sets a token budget, and disabled turns it off. The optional display field asks for the thinking text to come back summarised rather than left out.
print(flags(ClaudeAgentOptions(thinking={"type": "adaptive", "display": "summarized"})))
print(flags(ClaudeAgentOptions(thinking={"type": "enabled", "budget_tokens": 8000})))
print(flags(ClaudeAgentOptions(thinking={"type": "disabled"})))--output-format stream-json --verbose --system-prompt '' --thinking adaptive --thinking-display summarized --input-format stream-json --output-format stream-json --verbose --system-prompt '' --max-thinking-tokens 8000 --input-format stream-json --output-format stream-json --verbose --system-prompt '' --thinking disabled --input-format stream-json
- adaptive becomes
--thinking adaptive, and the display choice rides beside it. - enabled becomes
--max-thinking-tokenswith the budget. There is no--thinking enabled. - disabled becomes
--thinking disabledand carries no budget.
max_thinking_tokens is the older option and the Python reference marks it deprecated. Passing both is worth doing once, to see which one the SDK keeps.
print(flags(ClaudeAgentOptions(max_thinking_tokens=4000)))
print(flags(ClaudeAgentOptions(thinking={"type": "adaptive"}, max_thinking_tokens=4000)))--output-format stream-json --verbose --system-prompt '' --max-thinking-tokens 4000 --input-format stream-json --output-format stream-json --verbose --system-prompt '' --thinking adaptive --input-format stream-json
On its own the old option still works. Beside thinking it is dropped without a warning, which is the behaviour to know about before you set both in the same place.
Asking for more reasoning with effort
| Level | What it does | Suits |
|---|---|---|
"low" | Least reasoning, fastest answers | Lookups, listing a directory |
"medium" | Balanced reasoning | Routine edits |
"high" | Thorough analysis | Refactors, debugging |
"xhigh" | Deeper again, where the model supports it | Agentic coding |
"max" | The most reasoning | Multi-step problems |
Leaving it unset lets Claude Code resolve the level itself. Not every model supports the parameter, and xhigh falls back to high on a model that does not take it: agent-loop.
shop_desk.py replaces ShopModel.reply with a version that reads the level, and shop_tools.py adds a second order, B22, to ORDERS. The desk turns effort into how many of the orders named in a question it bothers to check, which stands in for a model reasoning further before it answers.
def reply(self, said, results, tools=(), rules="", skills=(), agents=(), effort=None):
...
# low effort reads the newest id only; from high up it checks every id it was given
orders = ORDER.findall(said[-1]) or ORDER.findall(" ".join(said))[-1:]
if effort in (None, "low", "medium"):
orders = orders[:1] def think(self, results):
"""One step from the stand-in model, with what this session gives it."""
return self.model.reply(self.said, results, self.offered(), self.rules(),
self.permitted(), self.setup.get("agents") or {},
self.options.effort)ORDERS = {"A17": "Order A17 shipped on 3 March.",
"B22": "Order B22 is being packed today."}Two runs, one level apart
from claude_agent_sdk import ClaudeAgentOptions
from shop_tools import shop
from watch import watch
def desk(effort):
# one option apart, and two orders named in the question
return ClaudeAgentOptions(mcp_servers={"shop": shop}, setting_sources=["project"],
allowed_tools=["mcp__shop__lookup_order"], effort=effort)await watch("Where are A17 and B22?", desk("low"))system init
assistant calls mcp__shop__lookup_order {'order_id': 'A17'}
user result: Order A17 shipped on 3 March.
assistant Order A17 shipped on 3 March.
result success: Order A17 shipped on 3 March.await watch("Where are A17 and B22?", desk("high"))system init
assistant calls mcp__shop__lookup_order {'order_id': 'A17'}
user result: Order A17 shipped on 3 March.
assistant calls mcp__shop__lookup_order {'order_id': 'B22'}
user result: Order B22 is being packed today.
assistant Order A17 shipped on 3 March. Order B22 is being packed today.
result success: Order A17 shipped on 3 March. Order B22 is being packed today.- At low one tool call goes out and the second order is never looked at.
- At high both are looked up and both appear in the answer.
- The question was the same. The level decided how much of it was answered.
Turning on a beta
betas takes the beta features the SDK knows about, and the type lists one: context-1m-2025-08-07, the million token context window.
print(flags(ClaudeAgentOptions(betas=["context-1m-2025-08-07"])))--output-format stream-json --verbose --system-prompt '' --betas context-1m-2025-08-07 --input-format stream-json
The Python reference warns that this beta is retired for Claude Sonnet 4.5 and Sonnet 4 on the Claude API, where passing it now fails any request over the standard window. A model that runs with the larger window by default is the route to take instead: the Python reference.
Where these options change a bill
- An agent that only reads files: a smaller model and
effort="low". - A debugging run on a hard failure:
effort="high"for that run alone, set per subagent when only one part needs it. - A service that must answer during an outage: a
fallback_model, so an overloaded first choice does not end the session.
Related
- Previous: Structured output
- Next: Cost and usage
- Reference: Configure your agent
- Set
effort="max"and compare the run with"high". - Pass
thinking={"type": "enabled", "budget_tokens": 1}and read the flag. - Add a third order to the question and run both levels again.
You understood something today that you didn't yesterday.