Tuning the effort Parameter for Cost and Speed
The effort parameter sets how much reasoning depth Claude applies to a request, letting you trade quality for cost and speed on a per-call basis.
Summary
Every request has an implicit cost and latency budget.
The effort parameter, passed under output_config, gives you a direct lever on that budget.
Lower effort favors speed and cost, higher effort favors thoroughness.
This is distinct from the thinking config, which controls whether reasoning is adaptive and visible at all.
The two settings compose, so a well-tuned production system usually sets both deliberately rather than leaving either at a default.
Recipe
Quick-reference recipe card - copy-paste ready.
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
output_config={"effort": "medium"},
messages=[{"role": "user", "content": "Summarize this customer support thread."}],
)
print(response.content[-1].text)When to reach for this:
- High-volume, latency-sensitive endpoints where
loweffort keeps cost predictable. - Code review, planning, or safety-critical tasks where
highormaxeffort is worth the cost. - A/B testing quality versus cost trade-offs across effort levels for the same prompt.
- Routing effort dynamically based on request type instead of a single fixed setting.
- Capping runaway reasoning cost on a workload that previously had no effort ceiling.
Working Example
import anthropic
client = anthropic.Anthropic()
EFFORT_BY_TASK_TYPE = {
"classify": "low",
"summarize": "medium",
"code_review": "high",
"incident_root_cause": "max",
}
def run_task(task_type: str, prompt: str) -> str:
effort = EFFORT_BY_TASK_TYPE.get(task_type, "medium")
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1536,
thinking={"type": "adaptive"},
output_config={"effort": effort},
messages=[{"role": "user", "content": prompt}],
)
for block in response.content:
if block.type == "text":
return block.text
return ""
if __name__ == "__main__":
ticket = "User reports intermittent 502s on checkout, started after last night's deploy."
result = run_task("incident_root_cause", ticket)
print(result)What this demonstrates:
- A lookup table that maps task type to an effort level, avoiding a single hardcoded setting across the whole application.
- Pairing
thinking={"type": "adaptive"}with an expliciteffortceiling, the two settings composing cleanly. - Falling back to
"medium"for unrecognized task types rather than failing. - Extracting just the
textblock, ignoring anythinkingblock for this call site's purposes. - Applying
maxeffort specifically to the highest-stakes task type, incident root cause analysis.
Deep Dive
How It Works
output_config={"effort": "<level>"}sets a ceiling on how much reasoning computation Claude applies before answering.- The levels are ordered
low,medium,high,max, from fastest and cheapest to most thorough and expensive. - Effort applies regardless of whether the
thinkingblock is visible, it governs the underlying reasoning budget, not just its display. - Raising effort generally increases both output token count (when thinking is visible) and latency, since Claude does more internal work per request.
- Effort composes with
thinking: adaptive thinking decides whether reasoning happens at all for a given prompt, effort caps how deep it goes when it does.
Effort Levels at a Glance
| Level | Speed | Cost | Best fit |
|---|---|---|---|
low | Fastest | Lowest | Classification, extraction, short lookups |
medium | Balanced | Moderate | General assistant replies, summarization |
high | Slower | Higher | Code review, multi-step planning |
max | Slowest | Highest | Safety-critical analysis, deep debugging |
Python Notes
# Pattern: fail loudly on an invalid effort value instead of silently defaulting.
VALID_EFFORTS = {"low", "medium", "high", "max"}
def build_output_config(effort: str) -> dict:
if effort not in VALID_EFFORTS:
raise ValueError(f"Unknown effort level: {effort!r}")
return {"effort": effort}Validating the effort string before it reaches the API call catches typos ("med" instead of "medium") at the call site instead of as an opaque request error.
Gotchas
- Setting
maxeffort everywhere "to be safe." This inflates cost and latency across your whole application, most requests do not need it. Fix: map effort to task type deliberately, reservemaxfor genuinely high-stakes calls. - Assuming effort controls whether a thinking block appears. Effort controls depth and cost, not visibility, that is the
thinkingconfig's job. Fix: set boththinkingandoutput_config.effortexplicitly when you need both self-calibration and a cost ceiling. - Not measuring latency impact before shipping a higher effort level.
highandmaxcan meaningfully slow down a user-facing endpoint. Fix: benchmark p50 and p95 latency at each candidate effort level against your real traffic before choosing one. - Hardcoding one effort level for a mixed workload. A single endpoint serving both trivial and complex requests wastes budget on the easy ones or under-serves the hard ones. Fix: route effort per request using a cheap upstream classifier or an explicit caller-provided hint.
- Comparing effort levels on too few test prompts. Effort's quality impact is most visible on genuinely hard tasks, testing only on easy prompts hides the difference. Fix: build an evaluation set that includes your hardest real-world cases before deciding on a default.
- Passing an invalid effort string and only discovering it at request time. A typo like
"med"for"medium"surfaces as an API error deep in a request path. Fix: validate against the known set of levels before constructing the request.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Fixed single effort level for the whole app | The workload is uniform in difficulty | Requests vary widely in complexity or stakes |
| Per-request effort passed by the caller | Different product surfaces have different cost/quality needs | You cannot trust caller input or need centralized cost control |
| Effort routed by an upstream classifier | High request volume with mixed difficulty and a need for automatic tuning | Traffic is low enough that manual mapping is simpler and cheaper to maintain |
FAQs
What values does the effort parameter accept?
low, medium, high, and max, passed as a string under output_config={"effort": "<level>"}.
Does a higher effort level guarantee a better answer?
Not always. It increases the reasoning budget Claude is allowed to use, which tends to help on genuinely hard tasks but adds little value on simple ones while still costing more.
Is effort the same as the thinking config?
No. thinking controls whether and how adaptively reasoning happens and is shown; effort controls how deep that reasoning is allowed to go and its cost ceiling. They are independent and typically used together.
What is a sensible default effort level for a general chat assistant?
medium is a reasonable starting point for general-purpose assistant replies, then adjust up or down per task type based on measured quality and cost.
How do I choose effort per request instead of a single global setting?
effort = EFFORT_BY_TASK_TYPE.get(task_type, "medium")
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
output_config={"effort": effort},
messages=[{"role": "user", "content": prompt}],
)Does effort affect latency?
Yes. Higher effort levels generally take longer to complete since Claude performs more reasoning work before producing the final answer.
Should low-effort requests skip the thinking config entirely?
Not necessarily. You can keep thinking={"type": "adaptive"} at low effort, Claude will still decide per-prompt whether any reasoning is warranted, just capped at a shallower depth.
Can effort level differ across models in the lineup?
Yes conceptually, since each model's baseline reasoning capability differs (Fable 5, Opus 4.8, Sonnet 5, Haiku 4.5), the same effort level does not produce identical depth or cost across models.
What happens if I pass an invalid effort string?
The request fails with an API error. Validate the value against the known set (low, medium, high, max) in your own code before sending it, so failures surface at the call site instead of deep in a request path.
Is max effort worth it for every code review task?
Not necessarily. high effort is often sufficient for routine code review; reserve max for particularly high-stakes or complex diffs where the extra cost is justified by the risk of a missed issue.
Should effort level be exposed to end users or kept server-side?
In most applications, keep effort as a server-side decision tied to task type or internal routing logic, rather than exposing it directly to end users, to keep cost predictable.
Related
- Extended Thinking and the Effort Parameter Explained - the conceptual relationship between thinking and effort.
- Enabling Adaptive Thinking with thinking: {type: 'adaptive'} - the companion setting that effort pairs with.
- Effort Levels and Thinking Display Options Reference - a full reference table for every level.
- Extended Thinking, Effort & Multimodal Basics - minimal examples covering effort alongside thinking and images.
- Extended Thinking, Effort & Multimodal Best Practices - numbered practices for tuning effort in production.
Stack versions: Written against the Claude model lineup current as of ~June 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (the default), and Claude Haiku 4.5 - and the official
anthropicPython SDK (latest 0.x release). Model names, SDK versions, and pricing move quickly - verify current specifics at platform.claude.com/docs before relying on them.