Skip to content

feat: price the token volume at API list rates - #26

Merged
AqueGen merged 3 commits into
mainfrom
feat/cost-estimate
Aug 11, 2026
Merged

feat: price the token volume at API list rates#26
AqueGen merged 3 commits into
mainfrom
feat/cost-estimate

Conversation

@AqueGen

@AqueGen AqueGen commented Aug 11, 2026

Copy link
Copy Markdown
Owner

Tokens need translating; dollars do not. tokens now prices the volume it already counts.

At API list prices (rates as of 2026-08-11), this is what the subagent volume above would have cost on the Claude API:
  as it ran                                            $2,365
  had every subagent inherited its session model       $4,402
  difference                                           $2,037
  main sessions, same rates (not routable)            $15,560

Why this is not the dollar fiction the file warns about

The old note said "no dollar fiction", and the objection it aimed at was never the translation - it was the saving. A saving needs a baseline nobody measured, which is why the README already calls the volume chart an upper bound rather than a measurement. So there are two figures and a difference, all three labelled, and the difference inherits that same caveat verbatim rather than being presented as money recovered.

It is also a counterfactual by construction: on a subscription none of this is paid. The wording says so on its own line.

The correctness story is per token type

Base input, cache reads at 0.1x, five-minute cache writes at 1.25x, one-hour writes at 2x, output at its own rate. These are an order of magnitude apart and cache reads dominate real transcripts, so a flat volume multiply would overstate the bill several times over. Transcripts carry cache_creation.ephemeral_5m_input_tokens and ephemeral_1h_input_tokens, so the write split is exact; a line with only the flat total takes the cheaper five-minute rate rather than guessing upward.

Prices are enumerated per model rather than per family, for the reason EFFORT_SUPPORT already is: families span price changes, Opus 4.1 bills at three times Opus 4.5, and a loose pattern would quietly misprice a retired model. Anything absent from the table is excluded from every figure and declared on its own line - never counted as free. Sonnet 5 is the one model whose rate changes on a calendar date rather than with a new id, so a window is priced at the rates in effect at its end, not at today.

What biases the number, and which way

Downward, both of them, which is the direction to err in:

  • models from Opus 4.7, Sonnet 5 and Fable 5 onward use a tokenizer producing about 30% more tokens for the same text than Sonnet 4.6 and earlier, so re-pricing a cheap model''s token count at an expensive model''s rate understates what that work would really have cost there;
  • cache writes on lines with no TTL breakdown take the cheaper rate.

Tests

64 pass. Five are new: every token type at its documented multiplier asserted to the cent ($9.35 on haiku, $46.75 for the same counts on opus-5), the untyped-write fallback, unpriced volume excluded and declared, the section absent when nothing in the window can be priced, and the price stamp reaching the output instead of sitting in a comment where nobody checks it.

Tokens need translating and dollars do not, so the tokens report now prices what
it counted: the subagent volume as it ran, the same volume had every subagent
inherited its session model, the difference, and the main-session volume beside
them for scale.

The arithmetic is per token TYPE, not per token, and that is the whole
correctness story. Base input, cache reads at 0.1x, five-minute cache writes at
1.25x, one-hour writes at 2x and output at its own rate are priced an order of
magnitude apart, and cache reads dominate real transcripts - a flat volume
multiply would overstate the bill several times over. Transcripts carry the cache
write breakdown by TTL, so the split is exact rather than averaged; a line
carrying only a total is charged at the cheaper five-minute rate.

Prices are transcribed from the Anthropic pricing page and enumerated per model
rather than per family, for the reason the effort table already is: families span
price changes, and Opus 4.1 bills at three times Opus 4.5, so a loose pattern
would silently misprice a retired model. A model absent from the table is
excluded from every figure and declared on its own line, never counted as free.
Sonnet 5 is the one model whose rate changes on a calendar date rather than with
a new id, so a window is priced at the rates in effect at its end.

The framing is the part that keeps this honest. It is a counterfactual, not a
bill: on a subscription none of it is paid, and the difference inherits the same
assumption the volume chart in the README already flags as an upper bound.
Against that, two documented facts bias it downward - the tokenizer from Opus 4.7
and Sonnet 5 onward produces about 30% more tokens for the same text than earlier
models, so re-pricing cheap-model counts at expensive-model rates understates the
counterfactual, and untyped cache writes take the cheaper rate. Conservative in
both directions it can be, which is the direction to err in.

64 tests, five of them pricing: every token type at its documented multiplier
with an exact dollar assertion, the untyped-write fallback, unpriced volume
excluded and declared, the section absent when nothing can be priced, and the
date stamp reaching the output rather than sitting in a comment.
…ache buckets

Two reviewers went at the cost feature. The sharpest finding was against my own
tests: the headline fixture passed the same 1e6 for every token type, which makes
the total invariant under any permutation of the caching multipliers. Swapping
0.1 and 1.25 still printed $9.35. The counts are now 1M/2M/4M/8M so each rate is
separably pinned, and the one-hour multiplier is identified rather than merely
consistent with the five-minute one.

The TTL split took the known buckets whenever either was present and only fell
back to the flat total when both were absent, so a bucket added upstream would
vanish from volume AND cost - worse than the flat field it replaced. It now
charges the remainder, whatever it is, at the cheapest rate: known buckets at
their own rate, everything unaccounted for at 1.25x rather than disappearing.

The section was gated on the subagent figure alone, so a window with priced main
sessions and entirely unpriced subagents printed neither the main-session row nor
the unpriced declaration the README promises is always made. It now prints
whatever it has, and a window with nothing priceable says so rather than going
silent - silence would read as "this cost nothing".

A negative difference, which is what deliberate above-tier work produces, printed
as "$-37.40" and lost its thousands separators. The sign now sits outside the
dollar mark.

From the pricing-page check: the Haiku 3.5 row never matched anything, because
pre-4.x ids put the generation first (claude-3-5-haiku-...). The row one line
above already carried that trick for claude-opus-4-20250514, so the trap was
known and not applied. Both orders now match.

Also disclosed rather than silently ignored: fast mode and US-only inference are
invisible in a usage line and both raise real cost, and the rates are first-party
only - Bedrock and Google Cloud bill separately. The label column width is derived
from the labels instead of a hand-counted constant, and PRICES now has the same
consistency test the effort table has, since it is the third ordered regex over
the model string.

68 tests pass.
Round-2 leftovers from both reviewers, all non-blocking, both of whom reached a
mergeable verdict.

The label width was derived from a second copy of the labels, so the comment
claiming a rename could not break the column was false: renaming at the call site
alone made padEnd a no-op, which is the hand-counted-constant failure moved one
indirection away. Labels and amounts are now one list, and the width comes from
the rows actually printed.

Two wording fixes where the output claimed more than the figures on screen. The
counterfactual caveats printed even in a window with no difference row to
qualify, and the tokenizer caveat was attributed to "these figures" when it
biases only the inherited side and the difference - it does not touch what
actually ran. And the unpriced declaration said "excluded from every figure
above" whether or not any figure was above it.

The two table-consistency tests were near-identical; one helper takes the table
name, and the effort test keeps its extra assertion about levels belonging to the
ladder.

68 tests pass.
@AqueGen
AqueGen merged commit c00d736 into main Aug 11, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant