How to reduce Claude API cost — eight levers, measured
Most API bills are not high because the rate is bad. They are high because the same context is resent dozens of times, cheap work is done by an expensive model, and outputs are longer than anyone needs. Fix those three and the bill drops without touching quality.
Start with measurement, not with cuts
Before changing anything, pull one day of usage and answer three questions: which model accounts for most spend, which workload drives it, and what fraction is input versus output. Cuts applied blindly usually remove capability, not cost.
| Model | Gateway rate in / out per 1M tokens | Official list in / out per 1M tokens | Diff |
|---|---|---|---|
| claude-haiku-4-5 | $0.5 / $2.5 | $1 / $5 | 50% |
| claude-sonnet-5 | $1 / $5 | $2 / $10 | 50% |
| claude-opus-5 | $2.5 / $12.5 | $5 / $25 | 50% |
| deepseek-v4-pro | $0.66 / $1.98 | — / — | — |
Rates checked 2026-09-16. Gateway rates move with upstream promotions — verify the current number in your dashboard before committing to a budget.
Lever 1 — send less context (usually the biggest)
Every request resends the conversation. Compacting a long thread, starting a fresh one per task, and keeping unrelated files out of the prompt all reduce the same thing: tokens you pay for repeatedly. This is the highest-yield change for agent workloads.
Lever 2 — route by difficulty
Not every call needs the strongest model. Classification, extraction, formatting, and applying a change you already specified are mechanical. Escalate only what is genuinely ambiguous.
# cheap first, escalate only when confidence is low
def handle(task):
out = call("claude-haiku-4-5", task) # cheap pass
if out.confidence < 0.7:
out = call("claude-sonnet-5", task) # escalate
return out
Lever 3 — cap the output
Output tokens cost several times more than input. Set max_tokens to something sane and ask for the format you actually consume — a diff, a JSON object, a one-line answer. Long explanations you do not read are pure cost.
resp = client.messages.create(
model="claude-sonnet-5",
max_tokens=300, # hard ceiling, not a suggestion
messages=[{"role": "user", "content": prompt}],
)
Lever 4 — reuse stable prefixes
When many requests share a large prefix — a fixed system prompt, a document, a codebase context — caching that prefix makes the repeated part much cheaper. It does nothing for one-off requests, so apply it where the prefix is genuinely stable.
Lever 5 — batch what does not need to be interactive
Offline work — backfills, bulk classification, nightly summaries — does not need low latency. Batch it, and it costs less than the same volume sent interactively.
Lever 6 — stop retrying blindly
Retries are the invisible line item. A 5% failure rate with unconditional retries is 5% of spend producing nothing. Retry on transient errors only, with backoff, and log every retry so it stops being invisible.
Lever 7 — shorten the loop
Ten exploratory requests cost more than one well-specified one, and often produce a worse result. Write the constraint into the prompt up front: what to change, what not to touch, what the output should look like.
Lever 8 — compare the rate, not the brand
The same model can cost materially different amounts depending on where you route it. That comparison is the whole point of a pricing index — and it is worth re-checking periodically, because rates move.
Put a number on it
Run your own workload through the cost calculator before and after each change. A lever that does not move that number did not work, however sensible it sounded.
FAQ
What is the single biggest way to reduce Claude API cost?
Sending less context. Because every turn resends the conversation, halving your average context roughly halves input cost — with no change in output quality.
Is switching to a cheaper model safe for production?
For a subset of calls, yes. The safe pattern is to classify requests first: mechanical ones (classification, extraction, formatting, simple edits) go to the cheap model, and only ambiguous or reasoning-heavy ones escalate.
Does prompt caching help?
Yes, when the same large prefix is reused across requests — a big system prompt, a fixed document, or a stable codebase context. It does almost nothing for one-off requests with unique prefixes.
Do shorter outputs really save that much?
Usually more than people expect, because output is priced several times higher than input. Asking for a diff instead of an explanation of the diff is a real saving, not a rounding error.
How do I stop a runaway bill?
Set a top-up ceiling at the account level and add a per-request max_tokens. Neither is a substitute for monitoring, but together they bound the damage.