Skip to content
AI

LLM Pricing Comparison: The Cached-Token Math Behind the Price War

LLM Pricing Comparison: The Cached-Token Math Behind the Price War

Short version for the impatient: after this week’s releases, the cheapest model on the price sheet is not always the cheapest model on your invoice. Which one wins depends on how much of your input is cached, and most LLM pricing comparisons leave that column out of the conversation.

Here’s how I got here. On Monday Grok 4.7 came out. On Tuesday Anthropic shipped Claude Opus 5.5, and about an hour later OpenAI shipped GPT-6 Sol and GPT-6 Luna. Simon Willison put all the new prices in one table, which saved me an evening of tab-hopping. Then I did what I suspect half of you did. I sorted by output price and started mentally moving client projects around.

I stopped myself, because I’ve been burned by that exact sort before. I wrote about the general problem in LLM cost optimization after the free lunch ended. This time I wanted to do the boring arithmetic first. It changed the ranking, and not in the direction I expected.

The price sheet everyone is sharing

Prices per million tokens, from Simon’s table:

Model Input Cached input Output
GPT-6 Luna $0.10 $0.01 $0.50
GPT-6 Sol $2 $0.20 $10
Grok 4.7 $2 $0.50 $6
Claude Opus 5.5 $4 $0.20 $20
Claude Fable 5.1 $10 $0.25 $50
GPT-6 Astra $10 $1 $50

Look at only the first and last columns and two conclusions pop out. Grok 4.7 has by far the cheapest output of the mid-tier models. Fable 5.1 and GPT-6 Astra cost the same. For the work I actually bill for, both conclusions are wrong.

The Opus change is also bigger than the headline. Anthropic’s Opus 5.5 page lists input and output at $4 and $20, down 20% from Opus 5’s $5 and $25. The line people skim past is cache reads: $0.20 per million, down from $0.50. That’s a 60% cut on the column that dominates agent workloads.

Two workloads, two different winners

I priced two request shapes, because they’re the two I run most often.

The first is a plain chat call. A 2,000-token prompt with nothing cached and a 500-token answer. Think of a support-reply draft, or a classification call with a longish instruction block.

The second is one turn of an agent loop. 200,000 tokens of context, of which 180,000 are a cache hit from the previous turn, plus 8,000 tokens of output. Simon mentions that in longer agentic conversations 90% or more of input tokens go through at the cached price, so 90% felt like a fair middle guess.

Here’s the tiny function I used. It ignores cache writes (Anthropic charges extra to write the cache, $5 per million on Opus 5.5), which slightly flatters the Claude numbers on the first turn of a session and makes no real difference after that.

PRICES = {  # per million tokens: input, cached input, output
    "gpt-6-luna":      (0.10, 0.01, 0.50),
    "gpt-6-sol":       (2.00, 0.20, 10.00),
    "grok-4.7":        (2.00, 0.50, 6.00),
    "claude-opus-5.5": (4.00, 0.20, 20.00),
    "claude-fable-5.1":(10.00, 0.25, 50.00),
    "gpt-6-astra":     (10.00, 1.00, 50.00),
}

def call_cost(model, input_tokens, cached_ratio, output_tokens):
    p_in, p_cached, p_out = PRICES[model]
    cached = input_tokens * cached_ratio
    fresh = input_tokens - cached
    return (fresh * p_in + cached * p_cached + output_tokens * p_out) / 1_000_000

chat  = {m: call_cost(m, 2_000, 0.0, 500) for m in PRICES}
agent = {m: call_cost(m, 200_000, 0.9, 8_000) for m in PRICES}

And the results, per call:

Model Chat call Agent turn
GPT-6 Luna $0.0004 $0.0078
Grok 4.7 $0.0070 $0.1780
GPT-6 Sol $0.0090 $0.1560
Claude Opus 5.5 $0.0180 $0.2760
Claude Fable 5.1 $0.0450 $0.6450
GPT-6 Astra $0.0450 $0.7800

For the chat call, Grok 4.7 comes in about 22% under GPT-6 Sol. For the agent turn the order flips, and Sol is about 12% cheaper than Grok. The whole difference is the cached column. Grok charges $0.50 per million for cache hits and Sol charges $0.20, and when 180,000 tokens per turn are cache hits, that column is most of your input bill.

Fable and Astra split the same way. Identical chat cost, but on the agent turn Fable is about 17% cheaper because its cache reads cost a quarter of Astra’s.

Scale it up and the gaps stop being rounding errors. At 1,000 agent turns a day for 30 days, Sol runs about $4,680 and Grok about $5,340. Opus 5.5 lands around $8,280 against roughly $11,700 for the same traffic on Opus 5 pricing. That’s a 29% drop from price alone, which is more than the 20% headline. Anthropic claims 40% on typical workloads because Opus 5.5 also uses fewer tokens per task. I can’t verify the token-efficiency half yet, so I’m only counting the part I can compute.

The cost nobody puts in a table: runaway output

Here’s the number that worried me more than any row above. In Simon’s usual pelican-on-a-bicycle test, Opus 5.5 at the “max” thinking level hit the 128,000-token output cap while still reasoning and returned nothing. He tried twice. Each failure cost $2.56 and took nearly 20 minutes.

That $2.56 is just 128,000 times $20 per million. It’s the worst-case price of a single call, and you pay it for an empty response. On my agent-turn numbers above, one of those failures costs as much as about nine normal Opus 5.5 turns. The same blowout on GPT-6 Luna would cost about six cents, which is its own argument for using cheap models on anything open-ended.

I haven’t run max effort on real client work, so I don’t know how often this happens outside a deliberately silly prompt. One test is one test. But I don’t need to know the frequency to put a ceiling on it:

def max_call_cost(model, max_output_tokens):
    _, _, p_out = PRICES[model]
    return max_output_tokens * p_out / 1_000_000

# pick the output cap from a budget, not the other way round
BUDGET_PER_CALL = 0.40
cap = int(BUDGET_PER_CALL / PRICES["claude-opus-5.5"][2] * 1_000_000)  # 20,000 tokens

If a task legitimately needs more than that, I’d rather find out from a truncated response I can inspect than from the invoice.

What I’m still unsure about

Price per token says nothing about how many turns a model needs. If a cheap model takes three tries to do what a pricier one does in one, a 3x price gap disappears. Luna is about 20 times cheaper than Sol per agent turn in my numbers, and I have no idea yet what its retry rate looks like on my workloads. The one real data point I have is secondhand: Simon moved his Datasette Agent demo to GPT-6 Luna and found it fast and competent at SQL and at building HTML and JavaScript. That’s encouraging. It isn’t my workload.

The other thing I’m unsure about is how long any of this holds. Simon notes GPT-5.6 has a 25% price increase scheduled for November, and Anthropic says Sonnet 5.5 and Haiku 5.5 are coming. Haiku 4.5 currently sits at $1 and $5, ten times Luna. If Haiku 5.5 lands anywhere near Luna, half of this table changes again. That’s the practical argument for keeping prices in config rather than hardcoding a model name in six places.

How I’m setting this up on client projects

Every provider I use returns cached-token counts in the usage block of each response. I log those next to the model name and the feature that made the call. Then the pricing comparison stops being a guess, because the cached ratio comes from real traffic instead of a blog post (including this one).

Routing then gets simple. Short, uncached, high-volume calls go to whichever model is cheapest on fresh input and output. Long agent sessions go to whichever model is cheapest on cached input at acceptable quality. Anything open-ended gets a hard output cap derived from a per-call budget. It’s the same setup I build into the AI automation work on my portfolio, and the price table is the only part that changes when a vendor ships something new.

What to do this week

Pull the last seven days of usage logs from your LLM provider. Divide cached input tokens by total input tokens, per feature, and write that ratio down. Plug your real averages into the call_cost function above and re-rank the models for each feature. Then set a max_tokens value on every call that doesn’t have one, picked from a dollar budget. If the ranking doesn’t change, you’ve lost twenty minutes. If it does, you’ve probably found money.