LLM Cost Cheat Sheet: Prices, Formulas, and Levers
A one-page reference for LLM costs: token prices at the time of writing, the cost formula with caching and batches, quick estimates, and the levers that cut a bill.
On this page· Prices
Why this matters
Cost questions come up in every design review: "what will this feature cost per user?", "is caching worth it here?", "should this be a batch job?". This page has the numbers and formulas to answer them in minutes. Prices change; the table shows list prices at the time of writing, so check the official pricing page before committing to a budget.
Prices#
USD per million tokens, list prices at the time of writing:
| Model | Input | Cache write (5 min) | Cache read | Output |
|---|---|---|---|---|
claude-opus-5-5 | $4.00 | $5.00 | $0.20 | $20.00 |
claude-sonnet-5-5 | $2.00 | $2.50 | $0.20 | $10.00 |
claude-haiku-4-5 | $1.00 | $1.25 | $0.10 | $5.00 |
- Cache writes cost 1.25× input for the 5-minute lifetime and 2× input for the 1-hour lifetime.
- Batches API: 50% off all token prices for asynchronous work.
- Output costs five times input on these models, so answer length matters.
Where the money goes in one request#
- Uncached input
- Cached input reads
- Output
Show text descriptionHide text description
One request to claude-opus-5-5 at list prices at the time of writing ($4 per million input tokens, $0.20 per million cached input tokens read, $20 per million output tokens). The cache write on the first request costs 1.25 times the input price and is not shown.
| Request | Uncached input | Cached input | Output | Total |
|---|---|---|---|---|
| Without caching | $0.0800 | $0.0000 | $0.0160 | $0.0960 |
| With a cached 18,000-token prefix | $0.0080 | $0.0036 | $0.0160 | $0.0276 |
The formula#
cost = ( uncached_input × input_price
+ cache_write × input_price × 1.25 (2 for the 1-hour cache)
+ cache_read × cache_read_price
+ output × output_price ) / 1,000,000
× 0.5 if sent through the Batches APIusage.input_tokens in a response already excludes cached tokens, which are reported separately as cache_creation_input_tokens and cache_read_input_tokens.
export const PRICES = {
"claude-opus-5-5": { input: 4, cacheRead: 0.2, output: 20 },
"claude-sonnet-5-5": { input: 2, cacheRead: 0.2, output: 10 },
"claude-haiku-4-5": { input: 1, cacheRead: 0.1, output: 5 },
} as const;
export interface Tokens {
input?: number; // uncached input tokens
cacheWrite?: number;
cacheRead?: number;
output?: number;
}
export function estimateUSD(model: keyof typeof PRICES, t: Tokens, opts: { cacheTtl?: "5m" | "1h"; batch?: boolean } = {}): number {
const p = PRICES[model];
const writeMultiplier = opts.cacheTtl === "1h" ? 2 : 1.25;
const raw =
(t.input ?? 0) * p.input + (t.cacheWrite ?? 0) * p.input * writeMultiplier + (t.cacheRead ?? 0) * p.cacheRead + (t.output ?? 0) * p.output;
return (raw / 1_000_000) * (opts.batch ? 0.5 : 1);
}import { test } from "node:test";
import assert from "node:assert/strict";
import { estimateUSD } from "./lib/estimate.ts";
const close = (a: number, b: number) => assert.ok(Math.abs(a - b) < 1e-9, `${a} != ${b}`);
test("a typical chat turn", () => {
// 2,000 new input tokens, 10,000 read from cache, 500 output tokens on Opus 5.5
close(estimateUSD("claude-opus-5-5", { input: 2_000, cacheRead: 10_000, output: 500 }), 0.008 + 0.002 + 0.01);
});
test("cache writes and batches", () => {
close(estimateUSD("claude-haiku-4-5", { cacheWrite: 1_000_000 }), 1.25);
close(estimateUSD("claude-haiku-4-5", { cacheWrite: 1_000_000 }, { cacheTtl: "1h" }), 2);
close(estimateUSD("claude-sonnet-5-5", { input: 1_000_000, output: 1_000_000 }, { batch: true }), 6);
});Rules of thumb#
| Quantity | Approximation |
|---|---|
| English text | About 4 characters, or 0.75 words, per token (count exactly with countTokens) |
| One page of prose | About 500 to 700 tokens |
| Code | More tokens per character than prose |
| A chat turn's input | Grows with the whole history: turn n resends turns 1 to n−1 |
| Monthly cost | requests per day × cost per request × 30 |
Levers, in the order to try them#
| Lever | Typical saving | Risk |
|---|---|---|
| Prompt caching for stable prefixes | 50–90% of input | None |
| Batches API for work that can wait | 50% | Latency only |
Shorter outputs (format instructions, max_tokens, lower effort) | 20–50% of output | Low |
| Less context (better retrieval, history trimming) | Varies | Medium |
| Model routing | 30–70% | Needs evals |
Common mistakes#
- Estimating from input only. Output is often the larger share.
- Forgetting history growth. Long chats cost more per turn unless cached or trimmed.
- Using another provider's tokenizer. Count with the API you use.
- No per-user limits. Costs are per request; abuse scales them.
Try it
Estimate a feature in five minutes
- Write down requests per day, input tokens (and how much is a stable prefix), and output tokens for one feature.
- Use
estimateUSDfor the cost per request without and with caching, and multiply to a monthly figure. - Compare with the revenue or budget per user and decide whether you need limits.
Check your understanding
Key takeaways
- Cost = uncached input + cache writes + cache reads + output, each at its own price, halved for batches.
- Output is the expensive side; history growth is the hidden one.
- Try levers in order: caching, batching, shorter outputs, less context, then routing with evals.
- Check current prices before committing to a budget.
Next steps#
Finished this article? Track it to see your progress on the roadmap.
Related articles
- Prompt Caching and Batches: Cutting LLM API Costs in TypeScriptCut LLM costs with prompt caching and the Batches API: how prefix caching works, where to put breakpoints, how to verify cache hits, and how to model costs.
- Cost Engineering for LLM Apps: Unit Economics, Routing, and BudgetsModel and cut LLM costs in TypeScript: unit economics, a cost model for levers like caching and batching, model routing with escalation, and per-feature budgets.
- LLM API Keys, Billing, and Security for Web DevelopersKeep LLM API keys server-side, understand token-based billing, compute the cost of every call, and enforce per-user budgets, timeouts, and retries in Node.js.