A common enterprise pattern is model tiering: send easy, high-volume requests to Claude Haiku 4.5 ($1/$5 per million input/output tokens), the bulk of the work to Sonnet 5 ($3/$15 list), and the hardest cases to Opus 4.8 ($5/$25) or Fable 5 ($10/$50). The pattern works — but "the same prompt" is not quite the same request on each tier. Differences show up in three places: how much guidance the model needs, which API parameters it accepts, and hard limits like context size.
Capability differences change how much you spell out
Anthropic's guidance says to treat Claude like a brilliant new employee: if a colleague with minimal context would be confused, so will the model. In practice, the smaller the model, the less you should leave implicit. A prompt that says "extract the key contract terms" may work on Opus 4.8, which infers what "key" means for your document type; on Haiku 4.5, you get more consistent results by enumerating the fields, adding 3–5 examples in <example> tags, and structuring the prompt with XML tags. Those techniques help every tier — they are simply less optional as you move down.
The reverse also matters: instructions written to keep a smaller model on rails ("do not add commentary, do not restate the question…") are usually harmless on a larger model, so write prompts to the weakest tier you route to and let stronger tiers benefit.
Parameter behavior genuinely differs
This is where "same prompt, different tier" breaks in ways that produce errors, not just quality drift.
| Behavior | Haiku 4.5 | Sonnet 5 | Opus 4.8 / Fable 5 |
|---|---|---|---|
| Thinking control | Manual: {"type": "enabled", "budget_tokens": N} | Adaptive, on by default; {"type": "disabled"} to turn off | Adaptive only; Opus 4.8 off unless set; Fable 5 always on |
temperature / top_p / top_k | Accepted | Non-default values rejected (400) | Non-default values rejected (400) |
| Context window | 200K tokens | 1M tokens | 1M tokens |
Two consequences for routing code. First, the thinking parameter cannot be copied across tiers verbatim: manual enabled mode returns a 400 error on Fable 5, Opus 4.8, and Sonnet 5, while Haiku 4.5 still uses it. Second, if your Haiku configuration sets a custom temperature, strip it before routing the same request upward. The output_config: {"effort": ...} parameter (levels low through max) is the modern depth-and-verbosity control on the newer tiers, and per Anthropic's docs it is a behavioral signal rather than a strict budget.
There is also a quieter difference: Opus 4.7-and-later models, Fable 5, and Sonnet 5 use a newer tokenizer that produces roughly 30% more tokens than earlier models for the same text. If you count tokens to enforce budgets, recount against the model you will actually call — the free count_tokens endpoint is model-specific.
One eval set, per-tier scores
The reliable way to manage tier differences is the same eval discipline covered in running your own evals: keep one test set and score every tier you route to. That converts "Haiku is probably fine for this" into a measured pass rate, and tells you exactly where the routing boundary belongs.
from anthropic import AnthropicBedrockMantle
client = AnthropicBedrockMantle(aws_region="us-east-1")
for model in ["anthropic.claude-haiku-4-5-20251001-v1:0",
"anthropic.claude-sonnet-5",
"anthropic.claude-opus-4-8"]:
msg = client.messages.create(
model=model, max_tokens=1024,
system=SHARED_PROMPT,
messages=[{"role": "user", "content": sample_input}],
)
print(model, grade(msg.content[0].text))
Note the model IDs: on Amazon Bedrock they carry the anthropic. prefix, and older models like Haiku 4.5 are date-versioned; other platforms use bare IDs like claude-sonnet-5. Keep the ID mapping in configuration so tier changes don't require code changes.
Where to go next
See model tiering for the cost architecture, the model lineup explained for what each tier is for, and extended thinking for reasoning controls in depth.