Skip to content

AI CODING ASSISTANTS

What Model Does Claude Code Use? Measured, Not Documented

What model does Claude Code use? On this machine, two per turn — and nothing in the repository decides which. Here is the resolution chain, measured end to end.

What model does Claude Code use? On the machine this was written on — CLI 2.1.232, no model set in any configuration file — a single five-word prompt was billed to two different models, and nothing in the repository decided which. The session model was claude-opus-5[1m]; claude-haiku-4-5 ran alongside it. This article shows the resolution chain that produces that answer, the committed script that reads it, and the one-line change that moves the decision into git.

Key takeaways

  • One turn used two models. modelUsage on a single headless run named claude-haiku-4-5-20251001 and claude-opus-5[1m] — the question has a plural answer.
  • There is no shipped default to look up. Part of the model list is fetched from the account and cached in ~/.claude.json, which is why two developers on the same CLI version see different menus.
  • Model ids carry a context suffix. claude-opus-5[1m] reports a 1,000,000-token window with canonicalModel: claude-opus-5. The bracketed form appears in no public model list.
  • On this repository the resolution chain falls all the way through to the account, so the model can change without a commit. That is a one-line fix.
  • The prompt was 2 tokens of the 32,005 that reached the model — 0.006%. Which model you pick matters less than most comparisons assume.

The short answer

Claude Code uses whichever model your account resolves to, unless a settings file or an environment variable pins one — and it uses a cheaper model alongside it on the same turn regardless. If you want a stable answer, put model in .claude/settings.json and commit it; if you want a cheaper bill, the effort setting is the larger lever.

For everything around the model — the permission system, subagents, the instruction file — the complete Claude Code guide is the pillar this sits under.

Two models ran, not one

The measurement is one headless invocation with JSON output, which reports per-model usage for the turn. That is the whole method: no instrumentation, no proxy, one documented flag.

Terminal
claude -p "Reply with exactly: ok" --output-format json --effort low
modelUsage, captured 2026-08-15 on CLI 2.1.232
  MODEL ID                      CANONICAL           CONTEXT     MAX OUT   IN       OUT   COST
  claude-haiku-4-5-20251001     claude-haiku-4-5    200,000     32,000    521      12    $0.000581
  claude-opus-5[1m]             claude-opus-5       1,000,000   64,000    2        4     $0.092567

Four things in that table are worth more than the model names.

Haiku is not incidental. It read 521 input tokens and wrote 12 — real work, on a prompt that asked for one word. Whatever it is doing, it does it on turns you did not ask it to.

The session model read 2 input tokens. Not two thousand. The prompt was almost the entire uncached input, because the other 32,003 tokens arrived as cache reads and cache writes. That is the harness, and it is the reason the model choice moves the bill less than people expect.

The context windows differ by 5x between the two models on the same turn, and both are reported, so you can see which ceiling applies to what. How much of either ceiling a real repository consumes is a separate measurement — one lockfile came to 139,640 tokens, which is most of the smaller window on its own.

The output ceiling is not the published one. Anthropic's model overview lists 128k max output for Claude Opus 5 on the Messages API; the CLI reported maxOutputTokens: 64000 for the same model on this turn, and 32,000 for Haiku 4.5 against a published 64k. Claude Code is setting its own cap, roughly half the API maximum in both cases. Worth knowing before you plan a task around a single long generation.

The cheap model cost 0.6% of the turn. $0.000581 against $0.092567. Worth knowing about, not worth optimising.

What model does Claude Code use here: the resolution chain

The reason there is no single documented answer is that the model is the output of a precedence chain, and the last step is not a file.

Precedence, highest first
--model flag
  → .claude/settings.local.json      ← repo, not committed
  → .claude/settings.json            ← repo, committed
  → ~/.claude/settings.json          ← machine
  → ANTHROPIC_MODEL                  ← environment
  → whatever the account resolves to ← not visible from the repository

Every step but the last is something you can read, diff and hand to a teammate. The last one is not. The committed check reports where a given repository lands:

Terminal
npm run check:model
Output, 2026-08-15
  CLI                  2.1.232 (Claude Code)

Where a model could be pinned, highest precedence first

  SOURCE            FILE                                  PRESENT   MODEL       EFFORT
  project (local)   .claude/settings.local.json           yes       —           —
  project (shared)  .claude/settings.json                 no        —           —
  user              ~/.claude/settings.json               yes       —           —

Account state cached on this machine  (~/.claude.json)

  Org default          none cached
  Model access         empty — no per-model grants cached
  Additional options   1 fetched from the account, not shipped in the CLI
    claude-fable-5[1m]      Fable

FAIL: nothing in this repository pins a model.

That Additional options line is the finding. ~/.claude.json carries additionalModelOptionsCache, orgModelDefaultCache and modelAccessCache — the set of models the CLI will offer you is partly server state cached on disk. It is not a list compiled into the binary, which is why "what model does Claude Code use" cannot be answered by reading the release notes, and why two developers on identical CLI versions can genuinely see different pickers.

The suffix on the model id

claude-opus-5[1m] is not a typo and not a version. The usage payload reports it as the model id while giving canonicalModel separately as claude-opus-5, with contextWindow: 1000000. The same bracketed form shows up in the cached account options, where claude-fable-5[1m] is listed with the label Fable.

This matters in exactly one place, and it is a place people hit: string matching. Cost dashboards, log parsers, allowlists and analytics that compare a model id against a published list will miss the bracketed variant, because the bracketed variant is not on the published list. Match on the canonical field where one is available, or match on a prefix.

Sonnet vs Opus, and the setting that matters more

The Sonnet-versus-Opus question is real, and it is smaller than the question next to it. We since measured it properly — two probes, one per tier — and the premium turns out to be exactly 2.5x on identical work, invariant across every token shape, while the figure this CLI reports for the pair is 1.3x. Claude Code exposes both:

Terminal
claude --model sonnet --effort medium
# --model  accepts an alias ('opus', 'sonnet') or a full id ('claude-opus-5')
# --effort accepts low, medium, high, xhigh, max

Aliases track the latest model in a family, which is convenient and is also a moving target — an alias pinned in a committed settings file will silently start meaning something else when the family advances. A full id does not move. Neither is wrong; they are different promises, and the choice deserves a sentence in your project's setup notes rather than a shrug.

On effort: the environment on this machine reported CLAUDE_EFFORT=high for a running session, and the flag documents five levels. Effort changes how much thinking a turn does, which on agentic work moves the bill and the wall-clock more than swapping model families does. If a session feels expensive, measure before you downgrade — why Claude Code gets slow covers the other half of that, and what AI coding assistants actually cost covers the pricing side.

Pin the model in the repository

If the answer to "which model built this" should live in git rather than in a menu, it is one file:

.claude/settings.json
{
  "model": "claude-opus-5"
}

Commit it. Two consequences, both worth accepting deliberately:

  • A pinned full id stops tracking the family. You will get the model you asked for after the next release, which is the point, and you now own the upgrade.
  • A pinned model overrides the account default for everyone on the repository, including people whose account would have offered something else. That is usually what a team wants and occasionally is not.

The check exits non-zero until one of those files names a model, so it works as a build step or a pre-commit hook rather than as something you remember to run.

What we are not claiming

This article does not rank the models against each other, and the omission is deliberate.

Everything above is a measurement of resolution and accounting — which model was selected, what it reported about itself, what it cost. None of it is evidence about output quality, which would need a controlled task set and a grader, and which the harness dominates anyway: 32,003 of the 32,005 input tokens on the measured turn were not the prompt.

Two further limits, stated plainly. The probe is one account on one day; the resolution chain means yours can differ legitimately. And the cached account state was read from ~/.claude.json, which also holds identifiers — the committed script touches four keys by name for that reason, and prints none of the rest.

Common mistakes reading the model picker

  • Assuming the picker is the whole list. Part of it is fetched and cached per account. An option a colleague has and you do not is not necessarily a version difference.
  • Reading one model per turn. Two were billed on the smallest prompt we could write.
  • Matching model ids as exact strings. claude-opus-5[1m] will not equal claude-opus-5, and the bracketed form is the one in the usage payload.
  • Pinning an alias and calling it pinned. opus tracks the family; the family moves.
  • Optimising the model before the harness. On the measured turn, the prompt was 0.006% of what reached the model, and emptying the tool list is enough to make the same model answer a repository question wrong.

Conclusion

Put model in .claude/settings.json and commit it, so the answer stops depending on an account setting nobody can see from the repository. Then run npm run check:model on your own project and read the modelUsage block from one headless turn before you plan anything around cost — on this machine that took one command and returned two models, a bracketed id that is on no public list, and a prompt that was two tokens of thirty-two thousand.

Frequently asked questions

What model does Claude Code use by default?
Whatever your account resolves to at the time, unless something pins it. On the machine measured here, CLI 2.1.232 with no model in any settings file, one turn billed claude-opus-5 as the session model and claude-haiku-4-5 alongside it. There is no single shipped default in the binary — part of the model list is fetched from the account and cached in ~/.claude.json.
Does Claude Code use more than one model in a single request?
Yes. A single headless run of a five-word prompt reported two entries in modelUsage: claude-haiku-4-5-20251001 and claude-opus-5[1m]. Haiku handled 521 input tokens of auxiliary work for 0.06 cents while the session model handled the turn itself. Any cost model that assumes one model per turn will be wrong, though not by much.
What does the 1m in claude-opus-5[1m] mean?
It is a context-window variant, not a version. The probe reported claude-opus-5[1m] with a contextWindow of 1,000,000 and canonicalModel of claude-opus-5. The bracketed form appears in usage output and cached account options but in no public model list, so anything matching on model id by exact string needs to expect the suffix.
How do I pin Claude Code to a specific model?
Set model in .claude/settings.json and commit it. An alias such as opus or sonnet tracks the latest in that family; a full id such as claude-opus-5 pins harder. The precedence runs from the --model flag down through settings.local.json, settings.json, the user settings file and ANTHROPIC_MODEL before falling through to the account default.
Should I use Sonnet or Opus in Claude Code?
Pick the model for the task class and then tune effort, which is the larger lever and the one most people leave alone. Claude Code exposes --effort with five levels from low to max. Reach for a cheaper model on mechanical work and a stronger one on multi-file reasoning, but measure your own turns first — the harness dominates the token bill either way.

Muhammad Kashif

Founder and editor of Devventa, covering AI coding assistants, Next.js and the modern AI development stack.