Token bills grow quietly with usage; a few design choices often cut them several-fold.
Cost on the Claude API is tokens times price, and price depends on the model. Latency (waiting time) mostly depends on the model and how many output tokens it writes. Most savings come from a few levers, roughly in order of impact:
- Right-size the model. Route simple steps (classification, extraction) to a small model and keep the large one for hard reasoning. A cheap model screening requests first is a common pattern.
- Effort and thinking. Lower
efforton simple tasks; thinking tokens are output tokens and are billed in full even when you only see a summary. - Output length. Output tokens cost several times more than input. Ask for concise formats, use structured outputs instead of prose, and set a sensible
max_tokens. - Prompt caching. Cache long system prompts, documents and tool lists; reads are a small fraction of normal input price and also ease rate limits.
- Batches. Anything that can wait up to a day costs about half.
- Context hygiene. Trim old turns and large tool results in long conversations instead of resending them forever.
Measure before and after: log usage per request (input, output, cache read, cache write) with the model and a feature label, then sum by feature. count_tokens lets you estimate before running a big job. Set a monthly spend limit in the Console so a bug cannot burn the budget.
For latency, stream the answer, keep prompts short, and remember a smaller model or lower effort answers sooner.
Example
import anthropic
client = anthropic.Anthropic()
def route(question: str) -> str:
# cheap model decides if the question is hard
r = client.messages.create(
model="claude-haiku-5-5", max_tokens=5,
system="Reply only EASY or HARD.",
messages=[{"role": "user", "content": question}])
return "claude-opus-5-5" if "HARD" in r.content[0].text else "claude-haiku-5-5"
def answer(question: str):
model = route(question)
r = client.messages.create(model=model, max_tokens=400,
messages=[{"role": "user", "content": question}])
u = r.usage
print(f"{model}: in={u.input_tokens} out={u.output_tokens} "
f"cache_read={u.cache_read_input_tokens}")
return r.content[0].text
answer("What is 2 + 2?")Remember
- Biggest levers: model routing, output length/effort, caching, batches
- Log usage per feature so you know where money goes
- Set a spend limit as a safety net
Watch out for
- Optimising input tokens while ignoring output and thinking tokens, which cost more.