Skillforge Field notes on shipping with AI tools

Claude Code Effort Levels: Cost, Caps, and maxEffortLevel

claude-codecosts

Effort is the one Claude Code setting that changes your bill without changing the model. It tells the model how many output tokens to spend on a turn, and output tokens are the expensive ones. Thinking, tool calls, and the visible reply are all metered as output, and effort scales all three at once.

This guide covers the five levels and which models take them, where each way of setting effort actually lands (some picks save, some last one session, one overrides everything), the maxEffortLevel cap that shipped in Claude Code 2.1.267, and the two places effort quietly interacts with the prompt cache. Everything is checked against the Claude Code docs (Model configuration, Settings reference, Environment variables, Prompt caching, Changelog) and the Claude API effort page on September 11, 2026, on Claude Code 2.1.268.

The short answer

  1. Five levels exist: low, medium, high, xhigh, max. high is the default on every model except Opus 4.7, and at the API level high is identical to not sending the parameter at all.
  2. Effort is a behavioral signal, not a token budget. Lower effort means the model thinks less often and makes fewer, terser tool calls. It still thinks on hard problems.
  3. Typing a level after /effort saves it as your default for the current model. Launching with --effort or pressing s in the slider applies to one session. max is always one session. The environment variable CLAUDE_CODE_EFFORT_LEVEL beats all of them.
  4. maxEffortLevel (2.1.267 or later) caps effort in any settings file, on every provider, and the lowest cap across files wins. Put it in project settings and nobody's /effort xhigh can lift it.
  5. Changing effort mid-session throws away the prompt cache on most models. On Fable 5.1 with an API key or a Claude subscription it does not.

Which models support which levels

ModelLevels
Fable 5.1, Fable 5low, medium, high, xhigh, max
Opus 5, Sonnet 5, Opus 4.8, Opus 4.7low, medium, high, xhigh, max
Opus 4.6, Sonnet 4.6low, medium, high, max (no xhigh)

Models not in the table (Haiku 4.5, Sonnet 4.5 and earlier) do not take the parameter, and the effort slider does not appear for them in /model. If you set a level a model lacks, Claude Code runs the highest supported level at or below it, so xhigh on Opus 4.6 runs as high.

Two things the docs say that matter for cost planning. First, the scale is calibrated per model: medium on Sonnet 5 and medium on Opus 5 are not the same amount of work, so an effort setting you tuned on one model needs re-testing on the next. Second, max is described as "prone to overthinking" with diminishing returns, and the platform docs recommend testing it before adopting it broadly. It is the level to reach for on a specific hard task, not a default.

What each level costs

Anthropic publishes no per-level multiplier, and we will not invent one. What the API docs do say is precise enough to reason from: effort applies to all output tokens, including thinking when active, tool call arguments, and text. Lower effort also produces fewer tool calls, which cuts input tokens on the following turns as well, because each tool result would otherwise be appended to the conversation you re-send.

The second-order effect is the one people miss. A turn at low that reads three files instead of eight leaves a shorter transcript for every later turn to carry, and over a long session that compounds. It is why the platform docs name "subagents" as the typical low use case: a scoped search has nothing to gain from deep reasoning and everything to gain from a short transcript.

Thinking tokens are billed as output whether or not you see them (Ctrl+O shows them). On Fable models thinking cannot be turned off, so there effort is the only control over how much of it you pay for.

Where each way of setting effort lands

This is the part that trips people up, because the same word "set" means four different durations.

How you set itLastsNotes
/effort medium typed, or Enter in the /effort or /model sliderSaved as your default for the current modelWritten under modelSettings in ~/.claude/settings.json (2.1.251 or later; before that it wrote effortLevel)
s in the /effort or /model sliderThis session only2.1.257 or later
claude --effort lowThis session onlyDoes not touch saved settings
/effort maxThis session onlymax is never saved, from any picker
CLAUDE_CODE_EFFORT_LEVEL=lowEvery session that inherits the variableOverrides --effort, /effort, and both settings keys. Only a cap still applies
effortLevel in a settings fileDefault for models with no saved levelAccepts low to xhigh, not max
modelSettings.<model>.effortLevelDefault for that one modelWhat /effort writes for you
effort: frontmatter on a skill, command, or subagentWhile that skill or subagent is activeOverrides the session level, not the environment variable
Effort control on a phone or browser via Remote ControlThis session only2.1.234 or later
/effort autoClears the saved level for the current modelOther models' entries stay

Because /effort saves per model, switching from Opus to Sonnet and back keeps each model's own level. A saved file looks like this after picking medium on Opus 5 and leaving Sonnet alone:

{
  "modelSettings": {
    "claude-opus-5": {
      "effortLevel": "medium"
    }
  }
}

Claude Code writes the canonical model name and matches the alias, the date-suffixed ID, and the [1m] variant to the same entry, so you do not need one entry per spelling.

The default-effort hold on Opus 4.7, Opus 4.8, and Fable 5

Three models carry a quirk worth knowing if your settings seem to be ignored. On Opus 4.7, Opus 4.8, and Fable 5, the first time you run the model Claude Code holds its default effort (built-in or organization-set) across sessions, even when your settings resolve a different level. The hold ends when you confirm a level interactively: Enter in a slider, a level typed after /effort, or a pick from a Remote Control device. It does not end for --effort at launch or s in the slider. Opus 5 and Fable 5.1 have no hold.

The practical consequence is in headless runs. A /effort inside a -p session applies to that session only, and while the hold is in effect on one of those three models it reports Not applied. Pass --effort on the command line instead, which the docs name as the fix.

Frontmatter effort on skills and subagents

A skill or subagent can pin its own level:

---
name: repo-search
description: Find where a symbol is defined and used. Read-only.
tools: Read, Grep, Glob
effort: low
---

The level applies while the skill or subagent runs and then the session level returns. Until 2.1.267 this was silently ignored on the three hold models above; the changelog lists the fix in that release. A cap from maxEffortLevel or an organization limit still bounds the frontmatter level. A skill body can also read the live level as ${CLAUDE_EFFORT} and branch on it. The rest of the frontmatter fields are in our skills tutorial.

Capping effort with maxEffortLevel

maxEffortLevel arrived in 2.1.267 and is the first way to cap effort that works outside a Claude Enterprise plan. It sets a ceiling and leaves lower levels available. Anything higher runs at the cap instead: a /effort pick, the /model slider, --effort, the environment variable, a skill's or subagent's frontmatter, and the model's own default. Claude Code applies the cap on the client before each request, so it holds on Amazon Bedrock, Google Cloud, and Microsoft Foundry too, where the server-side Enterprise limits do not reach.

Three rules decide what happens when caps collide:

  • Lowest wins across files. A cap in project settings cannot be raised from user settings, and a cap in user settings cannot be raised with --settings. Managed settings that set a higher cap still lose to a lower one from any other scope.
  • Per-model caps live under modelSettings. A maxEffortLevel inside a model's entry replaces the top-level cap for that model, but only within the file that sets both. Set it to "max" there to exempt one model from that file's cap.
  • Enterprise role limits still apply. When your organization also sets a per-role effort limit for a model, the lower of the two caps wins.

The example from the settings reference caps everything at medium and lets Sonnet 4.6 run free:

{
  "maxEffortLevel": "medium",
  "modelSettings": {
    "claude-sonnet-4-6": {
      "maxEffortLevel": "max"
    }
  }
}

A cap below xhigh also switches off ultracode for the capped models, since ultracode needs xhigh. /effort stops offering it and --effort ultracode starts the session at the cap with workflows planning off.

For a team, the useful pattern is a committed .claude/settings.json with "maxEffortLevel": "high". It stops a stray max session from running up a shared API key while leaving every developer free to drop lower for routine work. It is not a substitute for a spend limit (those live in the Console workspace, the Teams admin settings, or your cloud billing console), but it removes the one knob that can multiply a session's cost without anyone noticing.

Effort and the prompt cache

Model and effort level are both part of the prompt cache key. Our prompt caching guide covers the mechanics; the effort-specific part is short.

On most models, changing effort mid-session means the next request re-reads the whole conversation with zero cache hits, and Claude Code shows a confirmation dialog first while the cache is still warm. If you are on a five-minute API-key cache and decide fifteen minutes into a session that you want xhigh for one hard step, that step costs the full uncached history plus its deeper reasoning. Deciding effort at the start of a session is cheaper than changing it in the middle.

The exception is Fable 5.1 on an API key or a Claude subscription. Since 2.1.260, changing effort there keeps the cache and Claude Code applies the new level without asking. Under the hood this is the API's per-message effort change (still a beta at the API level), which moves the level into a system message instead of the request's top-level config, so the cached prefix still matches. The docs list the cases where it does not apply: Bedrock, Google Cloud, a Claude apps gateway, CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, and HIPAA configurations.

A subagent with its own frontmatter level runs in its own context, so its level never touches the main conversation's cache key. That makes "low-effort subagents, steady effort in the main session" the cheapest shape for a long session on any model.

Seeing which level is active

You should never have to guess. Claude Code shows the level in five places:

  • Session header, next to the model name: "with low effort". The footer also flashes the level at startup and on change.
  • Status line JSON carries effort.level, reflecting mid-session /effort changes. Add it to the cache-counter status line from our status line article and a level change that just cost you a cache miss is visible on the same row.
  • Hooks receive effort.level in their JSON input, and every Bash tool command and hook process gets CLAUDE_EFFORT in its environment. We confirmed this from inside a session while writing this article: a node -e "console.log(process.env.CLAUDE_EFFORT)" run through the Bash tool printed high. Ultracode reports as xhigh; models without effort get no variable at all.
  • /tasks names the model and, when frontmatter set one, the effort level on each subagent row (2.1.242 or later).
  • Transcripts and telemetry: each assistant message records its level (2.1.212 or later), and the OpenTelemetry cost.usage and token.usage events carry an effort attribute, so exported metrics can split spend by level.

Two things that are not effort levels

ultrathink anywhere in a prompt asks for deeper reasoning on that one turn. Claude Code recognizes the keyword and adds an in-context instruction; the effort level sent to the API does not change, so neither does the cache key. Other phrases ("think hard", "think more") pass through as plain text.

ultracode appears in the /effort menu but is a Claude Code setting, not a model level. It sends xhigh and additionally has Claude plan a dynamic workflow for each substantive task. It has its own ultracode settings key, and CLAUDE_CODE_EFFORT_LEVEL and the effortLevel key both refuse the value.

FAQ

What is the default effort level in Claude Code?

high, on every model that supports effort, except Opus 4.7, which defaults to xhigh. An organization can set a different default for its default model, and that applies when you run that model. Historically the default was medium: the changelog moved API-key, cloud-provider, Team, and Enterprise users to high in 2.1.94 and Pro and Max subscribers on Opus 4.6 and Sonnet 4.6 in 2.1.117.

Does lower effort make responses shorter?

Not reliably. The platform docs say explicitly for Opus 5 that effort controls thinking volume, not visible response length, and that you should prompt for length instead. Expect lower effort to mean less thinking and fewer tool calls, and treat any change in reply length as a side effect.

Can I use xhigh or max with thinking turned off?

Not on Opus 5: the API returns a 400 for that combination. Since 2.1.251 Claude Code avoids the error by sending high instead when thinking is disabled and the level is higher. On Fable models thinking cannot be disabled, so the question does not arise.

Does /effort in a headless (-p) run save my default?

No. It applies to that session only, and on Opus 4.7, Opus 4.8, and Fable 5 it may report Not applied while the default-effort hold is in effect. Use claude --effort <level> -p "..." or set CLAUDE_CODE_EFFORT_LEVEL in the job's environment. If you run Claude Code on a schedule, our scheduled runs guide covers where that variable belongs.

Does maxEffortLevel work on Bedrock and Vertex?

Yes. Claude Code applies the cap itself before each request, so it does not depend on the provider enforcing anything. That is the difference from the Enterprise per-role limits, which are server-delivered. Requires 2.1.267 or later.

Is there a PreModelSwitch-style hook for effort changes?

No. The hooks reference lists PreModelSwitch for model changes only; changing effort is not a model switch. What you can do is read effort.level in any hook's input and, for example, have a UserPromptSubmit hook warn when a session is running at max. Our model switch hooks guide covers the hook side.