ES · Leer en español Explore for free

Comparison · Intermediate · 10 min

Opus 5.5 vs Sonnet 5.5 vs Haiku 5.5 in Claude Code: which to use

Which model and effort to pick in Claude Code 2.1.316, official prices, opusplan and a reproducible test with hidden tests (results pending).

Cover: Opus 5.5 vs Sonnet 5.5 vs Haiku 5.5 in Claude Code: which to use

Tested with Claude Code 2.1.316 · reviewed

See the files on GitHub Free · MIT license

Summary and verdict

In six weeks four new models have reached Claude Code (Fable 5.1, Opus 5.5, Sonnet 5.5 and Haiku 5.5) and, on top of that, the 5.5 models now start with medium effort. "Which one should I use?" is a daily question and the usual answer is an opinion. Here you get two things: a decision table based on the official documentation and a reproducible test (same task, same repository, hidden tests, three repetitions) to measure time, cost and quality.

Test status: methodology ready; results pending. The test costs money and has not been run yet, so this page contains no result figures at all. Once it runs, the results table will be filled in with the date and version. Until then, the verdict is based only on the official documentation:

All the test files are in the free repository ctrlia-opus-sonnet-haiku.

The four models you have in Claude Code today

On the Anthropic API the aliases point to: opus → Opus 5.5 (since 2.1.280), sonnet → Sonnet 5.5 (2.1.284), haiku → Haiku 5.5 (2.1.293) and fable → Fable 5.1 (2.1.257). All four have a 1 million token window. On Bedrock, Agent Platform and Foundry the Sonnet and Haiku aliases still point to older versions, so there you have to use the full ID.

With the default value, Pro, Max, Ultra, Team, Enterprise and the API start on Opus 5.5. Fable is never the default model and, depending on your plan, may be billed with usage credits (the picker shows "Requires usage credits"). To pin an exact version, use the ID:

claude --model claude-opus-5-5

Effort: the setting almost nobody touches

All four models accept low, medium, high, xhigh and max. Opus 5.5, Sonnet 5.5 and Haiku 5.5 start at medium (Opus 5 started at high); according to Anthropic, Opus 5.5 at medium matches or beats Opus 5 at high in its coding evaluations. The official guide:

Change it with /effort (with s for this session only), with --effort at startup, per model in modelSettings, or with CLAUDE_CODE_EFFORT_LEVEL, which overrides everything. For a single turn, write ultrathink in the prompt: since 2.1.311 it raises that turn to xhigh. Watch out: an old effortLevel in your ~/.claude/settings.json is not applied to Opus 5.5.

How we tested it (methodology)

1. A single realistic, verifiable task

A 2-file Python repository where the model has to: (1) fix a test that fails because round() uses banker's rounding and the business wants ROUND_HALF_UP, (2) add apply_coupon() with three rules and (3) write its tests. The prompt is identical for every model (en/TASK.md; the Spanish original, TAREA.md, is in the repo root):

You are in a small Python project (src/ and tests/ folders).
1. Run `python3 -m pytest -q` and fix the failure in `tests/test_prices.py` without touching the tests.
2. Add to `src/prices.py` the function `apply_coupon(total, code)` with these rules:
   - "WELCOME" takes 10 % off; "SHIPPING" takes 4.99 off if the total is at least 30; any other code raises ValueError.
   - The result never goes below 0 and is rounded to 2 decimals with ROUND_HALF_UP from the decimal module (not with round()).
3. Write tests for `apply_coupon` in `tests/test_coupon.py`.
4. Finish when `python3 -m pytest -q` passes completely. Don't install packages or use the network.

The starting code has the bug on purpose. en/template/src/prices.py:

from decimal import Decimal


def total_with_vat(net, vat=0.21):
    return round(net * (1 + vat), 2)

And the visible test that fails, en/template/tests/test_prices.py:

from src.prices import total_with_vat

def test_vat_rounds_half_up():
    # 10.125 € net at 0 % VAT must end up as 10.13 (ROUND_HALF_UP), not 10.12
    assert total_with_vat(10.125, 0) == 10.13

def test_normal_vat():
    assert total_with_vat(100, 0.21) == 121.0

2. Quality is decided by hidden tests, not by our opinion

When the run ends, the script copies a test file the model has never seen (7 cases, including edges such as 29.99 € or 0.045) and counts how many pass. en/hidden/test_hidden.py:

import pytest
from src.prices import apply_coupon, total_with_vat

def test_welcome():               assert apply_coupon(50, "WELCOME") == 45.0
def test_shipping_minimum():      assert apply_coupon(30, "SHIPPING") == 25.01
def test_shipping_below():        assert apply_coupon(29.99, "SHIPPING") == 29.99
def test_never_negative():        assert apply_coupon(0, "WELCOME") == 0
def test_round_half_up():         assert apply_coupon(0.05, "WELCOME") == 0.05  # 0.045 -> 0.05
def test_invalid_code():
    with pytest.raises(ValueError): apply_coupon(10, "FAKE")
def test_vat_still_right():       assert total_with_vat(2.675, 0) == 2.68

Before publishing we checked the tests themselves: a reference solution passes all 9 tests (2 visible + 7 hidden) and the starting code fails the visible test, as intended. Look at total_with_vat(2.675, 0): the float 2.675 is really 2.67499…, so only a solution that converts with Decimal(str(x)) passes.

3. What we measure and under which conditions

From the JSON output of claude -p we take total_cost_usd, duration_ms and num_turns, and with git we count the lines added in src/. Matrix: 3 models × 2 efforts (medium, high) × 3 repetitions = 18 runs; Fable 5.1 is added only if approved separately (6 more).

Each run starts in a fresh temporary folder, with --bare so no hooks, skills or CLAUDE.md from the machine are loaded (comparable results), --permission-mode dontAsk with a closed list of tools, no network, and caps of --max-budget-usd 2 and --max-turns 40. Versions are pinned: Claude Code 2.1.316 and IDs claude-opus-5-5, claude-sonnet-5-5 and claude-haiku-5-5.

4. How much it costs before running it

Assumption per run: about 200 K input tokens processed in total (adding up turns) and 8 K output tokens.

ModelUpper bound per run (no cache)6 runs
Opus 5.5≈ 0.96 USD5.76 USD
Sonnet 5.5≈ 0.48 USD2.88 USD
Haiku 5.5≈ 0.02 USD0.14 USD
Fable 5.1 (optional)≈ 2.40 USD14.40 USD

Upper-bound total: 8.8 USD without Fable and 23.2 USD with Fable. Realistic estimate, with ~85 % of the input read from cache at the official read prices: about 3 USD without Fable and 7.5 USD with Fable (it does not include the cache write surcharge, which we have not verified). The --max-budget-usd 2 cap limits the worst case to 36 USD (18 runs) or 48 USD (24).

The plan: phase 1 smoke test with 3 runs (≈ 0.5-1.5 USD) to check the script works; if it goes well, full phase 2 without Fable; Fable is a separate decision. With a subscription instead of an API key, --bare does not work (it does not read the login), the cost figures are estimates and what you spend is plan limit.

5. Run it yourself

Requirements: Claude Code 2.1.316, git, python3 with pytest, jq and an ANTHROPIC_API_KEY (needed for --bare and for an exact cost). en/measure.sh does one run and appends a line to en/results.jsonl:

#!/usr/bin/env bash
# measure.sh — one run of the test: ./measure.sh <model> <effort> <repetition>
# Requires: claude 2.1.316, git, python3 with pytest, jq. Recommended mode: --bare with ANTHROPIC_API_KEY.
# Costs money: each run is capped at 2 USD (--max-budget-usd 2).
set -euo pipefail
[ $# -eq 3 ] || { echo "Usage: $0 <model> <effort> <repetition>" >&2; exit 2; }
model="$1"; effort="$2"; rep="$3"
root="$(cd "$(dirname "$0")" && pwd)"
dir="$(mktemp -d /tmp/model-test.XXXXXX)"
cp -r "$root/template/." "$dir/"
cd "$dir"
git init -q && git add -A && git -c user.name=test -c user.email=test@local commit -qm base

start=$(date +%s)
claude --bare -p "$(cat "$root/TASK.md")" \
  --model "$model" --effort "$effort" \
  --permission-mode dontAsk \
  --allowedTools "Read" "Edit" "Bash(python3 -m pytest *)" \
  --max-budget-usd 2 --max-turns 40 \
  --no-session-persistence \
  --output-format json > result.json || true
end=$(date +%s)
[ -s result.json ] || echo '{"is_error": true}' > result.json   # if claude wrote nothing, the row is marked as an error

# Quality: hidden tests the model hasn't seen
cp "$root/hidden/test_hidden.py" tests/
if python3 -m pytest -q tests/test_hidden.py > hidden.txt 2>&1; then hidden=ok; else hidden=fail; fi
passed=$(grep -Eo '[0-9]+ passed' hidden.txt | grep -Eo '[0-9]+' || echo 0)
lines=$(git diff --shortstat HEAD -- src | grep -Eo '[0-9]+ insertion' | grep -Eo '[0-9]+' || echo 0)

jq -c --arg m "$model" --arg e "$effort" --arg r "$rep" --arg o "$hidden" \
      --arg p "$passed" --arg l "$lines" --arg s "$((end-start))" \
  '{model:$m, effort:$e, rep:($r|tonumber), cost_usd:.total_cost_usd,
    ms:.duration_ms, ms_api:.duration_api_ms, turns:.num_turns, error:.is_error,
    hidden:$o, hidden_passed:($p|tonumber), src_lines:($l|tonumber), wall_seconds:($s|tonumber),
    per_model:.modelUsage}' result.json | tee -a "$root/results.jsonl"
echo "Run folder: $dir"

en/batch.sh launches the smoke phase or the full matrix and prints the summary:

#!/usr/bin/env bash
# batch.sh — phase 1 (smoke, 3 runs, ~0.5-1.5 USD): ./batch.sh smoke   · phase 2 (18 runs): ./batch.sh full
# Costs money. Check results.jsonl after the smoke phase before launching the full one.
set -euo pipefail
cd "$(dirname "$0")"
models="claude-haiku-5-5 claude-sonnet-5-5 claude-opus-5-5"   # Fable 5.1 only if approved separately: add "fable"
case "${1:-smoke}" in
  smoke) for m in $models; do ./measure.sh "$m" medium 1; done ;;
  full) for rep in 1 2 3; do for e in medium high; do for m in $models; do ./measure.sh "$m" "$e" "$rep"; done; done; done ;;
  *) echo "Usage: $0 [smoke|full]" >&2; exit 2 ;;
esac
# Summary per model and effort: mean cost and seconds, median of hidden tests passed (out of 7)
jq -s 'group_by(.model+"/"+.effort) | map({key:(.[0].model+"/"+.[0].effort),
  n:length, mean_cost:(map(.cost_usd // 0)|add/length), mean_seconds:(map(.ms // 0)|add/length/1000),
  hidden_median:(map(.hidden_passed)|sort|.[length/2|floor]),
  hidden_ok:(map(select(.hidden=="ok"))|length), mean_turns:(map(.turns // 0)|add/length)})' results.jsonl

The steps:

git clone https://github.com/carlosp5751258-lgtm/ctrlia-opus-sonnet-haiku
cd ctrlia-opus-sonnet-haiku/en
export ANTHROPIC_API_KEY=...        # your own; do not save it in the repository
./batch.sh smoke                    # 3 runs; check results.jsonl
./batch.sh full                     # only if the smoke phase went well

Comparison table

List prices per million tokens, according to the official CHANGELOG:

ModelAlias (Anthropic API)SinceInput / output (USD)Cache read (USD)Default effort
Fable 5.1fable2.1.25710 / 500.25no data
Opus 5.5opus2.1.2804 / 200.20medium
Sonnet 5.5sonnet2.1.2842 / 100.10 (down from 0.20)medium
Haiku 5.5haiku2.1.2930.10 / 0.50 (0.50 / 2.50 if the prompt exceeds 100 K tokens)no datamedium

Test results: pending. This is the table that will be published, one row per model and effort:

Model / effortAverage cost per runAverage secondsTurnsHidden tests (out of 7, median of 3)Runs fully greenLines added in src/
Haiku 5.5 / mediumpendingpendingpendingpendingpendingpending
Haiku 5.5 / highpendingpendingpendingpendingpendingpending
Sonnet 5.5 / mediumpendingpendingpendingpendingpendingpending
Sonnet 5.5 / highpendingpendingpendingpendingpendingpending
Opus 5.5 / mediumpendingpendingpendingpendingpendingpending
Opus 5.5 / highpendingpendingpendingpendingpendingpending

Rules for reading it without fooling yourself:

  1. It is a small task: do not extrapolate to large refactors, where the documentation gives Fable the edge.
  2. We will show the spread, not just the average.
  3. total_cost_usd is a client-side estimate and may differ from the invoice.
  4. Date and version in the header; we will repeat the test with every new model.

When to use each one

Based on the official documentation (it will be adjusted with the test data):

TaskModelEffort
Renames, explaining a file, high-volume tasksHaiku 5.5low or medium
Search or summary subagentsHaiku 5.5 (model: haiku in the subagent frontmatter)low
Day-to-day work with a clear scopeSonnet 5.5medium
Bugs in existing code, design decisions, when Sonnet gets it wrongOpus 5.5medium (default) or high
Work bigger than one session, root cause, architectureFable 5.1 (describe the outcome, not the steps)depends on the task
Plan first, then implementopusplan (Opus in plan mode, Sonnet when executing)medium

Three more tips:

The commands, verified in the documentation:

claude --model sonnet --effort medium      # this session only
/model haiku                               # inside the session (Enter = save as default; s = this session only)
/effort high                               # or /effort with no arguments for the slider
/model opusplan                            # Opus in plan mode, Sonnet when executing
export CLAUDE_CODE_EFFORT_LEVEL=low        # overrides everything else
export CLAUDE_CODE_SUBAGENT_EFFORT=low     # effort for subagents that do not set one (2.1.316)
/status                                    # see the current model

And a default model with per-model effort in ~/.claude/settings.json:

{
  "model": "sonnet",
  "modelSettings": {
    "claude-opus-5-5": { "effortLevel": "high" }
  }
}

If you want a skill that always runs on Haiku even when your session is on Opus, look at /explica in the 5-command pack. To see what you pay with each plan, read the Claude Code pricing recipe. Sources: model configuration, headless mode, CLI reference, costs, cost tracking, prompt caching and the CHANGELOG.

FAQ

Which model does Claude Code use by default?

With the default value, Opus 5.5 on Pro, Max, Ultra, Team, Enterprise and the Anthropic API (since 2.1.280). On Microsoft Foundry, Sonnet 4.5. Fable is never the default. Check it with /status.

Opus or Sonnet for coding?

The documentation describes Sonnet for daily work and Opus for complex reasoning. Sonnet 5.5 costs half as much (2/10 USD per million versus 4/20). Start with Sonnet at medium and move up to Opus when it fails on bugs in existing code or on design; our test will show how much it changes on a specific task.

How do I plan with Opus and implement with Sonnet?

With /model opusplan: it uses Opus in plan mode (Shift+Tab or /plan) and switches to Sonnet only when executing. With /model opusplan[1m] you ask for 1M context in both phases where it is not the default.

Which effort should I set?

Leave medium (the default for the 5.5 models) for normal work, high for bugs with edge cases and low for small changes you review. max only for hard problems: the documentation warns it can overthink. For a single hard turn, write ultrathink.

Is Haiku 5.5 good for anything serious?

For simple tasks and for search or summary subagents it is the cheap option (0.10/0.50 USD per million, 1M context). Watch out: if the prompt exceeds 100 K tokens the price goes up to 0.50/2.50.

Why doesn't Fable show up, or why does it ask for credits?

Depending on your plan, Fable may be billed with usage credits instead of the included limit; the picker marks it with "Requires usage credits". It requires 2.1.257 or later.

Does switching models mid-session cost more?

Yes: the caching documentation explains that switching models causes a turn without cache. Choose the model at the start, or use opusplan, which is designed for that switch.

When will you publish the results?

When the test is run, which costs money and needs approval. Until then the page only has the methodology and the script so anyone can repeat it; we will never publish figures that do not come from the results file (results.jsonl, or resultados.jsonl in the Spanish version).

The whole library