# How to read a cost figure

What to check before you forward a number: which rows it covers, which it left out, and whether it was measured or modelled.

Source: https://tokentriage.com/docs/build/cost-figures

---
import Callout from '../../../components/Callout.astro';
import Details from '../../../components/Details.astro';
import NextSteps from '../../../components/NextSteps.astro';
import CostVerdictDiagram from '../../../components/CostVerdictDiagram.astro';

A cost figure is about to leave your hands. It goes in a slide, an email to finance, a
justification for a change. Once it leaves, nobody downstream can reconstruct what it covered.

So this page is a reading habit rather than a feature tour: four questions to ask of any dollar
this system renders, and where each answer is written down. Three of them apply to a plain
total. The fourth applies to any figure that claims a *saving*, which is a different and much
stronger kind of claim.

The vocabulary itself (the three verdicts, and the rule that unpriced is not zero) is defined
in [the honesty model](/docs/concepts/honesty-model). This page is about meeting it in the wild.
[Verdicts, methods and baselines](/docs/build/cost-figure-reference) is the lookup table.

## 1. What did it not cover?

Every total is computed over the rows it could price, and it tells you how many it could not.

```text
TOTAL   412   13087434   358093   90705   0   0   0.7%   $66.371826
COST excludes 35 unpriced rows of 412 (no price resolved; the $0 is a missing price, not $0 of spend)
```

A row is **unpriced** when the pricing table has no entry for the model it used: a fine-tuned
model, a self-hosted endpoint, a provider added faster than the catalog. Its cost is not zero
and it is not small. It is unknown. Counting it as `$0.00` produces a total that looks
complete and understates spend with total confidence, so instead the row is excluded from the
dollar and counted beside it.

Every surface applies the same rule with its own spelling, which is worth knowing if you parse
any of them:

| Surface | An unpriced figure looks like |
|---|---|
| `top`, `report` | `-` in the cost cell, with the excluded-row line beneath the table |
| the HTTP API | `"usd": null` with `"unpriced": true` |
| a FOCUS export | an empty `EffectiveCost` cell plus an `x_tt_unpriced_reason` |
| a chargeback CSV | the literal string `UNPRICED` in the cost column |

None of them is `0`. A declared-free model is different and does render `$0`, with
`unpriced: false` beside it, because genuine zero and unknown are separate facts.

<Callout type="key">
Read the exclusion line before the number above it. Thirty-five unpriced rows out of four
hundred is a total you can forward with a caveat. Thirty-five out of forty is not a total.
</Callout>

## 2. Were the tokens measured or estimated?

A second flag, `estimated`, says whether the token counts underneath a figure came from the
provider or from a tokenizer approximation. It renders as `EST`, and it is set for two
different reasons.

The first is that the provider did not report usage, so the counts were estimated. The second
is subtler: the counts were exact, but the *price table* had no rate for one of the token
buckets, so a cache-read or reasoning bucket was billed at the base input or output rate
instead. Both produce a number worth having and neither is exact, which is why one flag covers
them.

An `EST` figure is fine for a trend and poor for an invoice dispute.

## 3. What are the buckets?

The most common arithmetic error in reading these figures is assuming the token buckets
overlap. They do not. Input, output, cache-read, cache-write and reasoning are **disjoint**,
and the cost is their simple sum.

That matters because providers do not report them that way. Here is one request as the upstream
described it, and as the ledger recorded it:

```json
{
  "usage": {
    "prompt_tokens": 1200,
    "completion_tokens": 350,
    "prompt_tokens_details": { "cached_tokens": 256 }
  }
}
```

```json
{
  "usage": {
    "input_tokens": 944,
    "output_tokens": 350,
    "cache_read_tokens": 256,
    "cache_write_tokens": 0,
    "reasoning_tokens": 0,
    "total_tokens": 1550,
    "source": "provider"
  }
}
```

OpenAI's `prompt_tokens` **includes** its cached tokens as a discounted subset, so the dialect
subtracts: 1,200 − 256 = 944 fresh input tokens, and 256 cache reads priced separately.
Anthropic reports the two already separated and maps straight through. Either way, by the time
a figure exists the buckets no longer overlap.

Which makes the whole cost hand-checkable. Those counts at `gpt-4o-mini`'s rates:

```text
944 × $0.15/M  = $0.0001416     input
350 × $0.60/M  = $0.00021       output
256 × $0.075/M = $0.0000192     cache read
                 -----------
                 $0.0003708
```

The ledger recorded `"usd": 0.0003708`. The rates come from
`tokentriage prices show openai/gpt-4o-mini`, and every figure on every surface is this sum,
scaled up.

<Callout type="warning">
Never describe a cache read as part of the input. "32 of its input tokens came from cache" is
wrong under this contract and double-counts if you act on it: the input bucket already excludes
them. Say "944 fresh input tokens and 256 served from cache". A published page got this wrong
and had to be corrected.
</Callout>

## 4. Is it a saving? Then two more questions

A total describes what happened. A **saving** claims something did not happen, which needs a
comparison to something that never ran. Every savings figure this system produces therefore
travels with two extra pieces of paperwork, and neither is optional.

**What was it compared against?** A saving with no named baseline is unfalsifiable, so the
baseline travels with the number as a fixed sentence. `vs always serving the top tier` is a
different claim from `vs the tier that was serving this slice before the change`, and the
sentence says which one you are reading. Five baselines are registered and the sentence is a property of
the baseline, not prose someone wrote for that screen.

**How well is it known?** One of three verdicts, and they are kinds of claim rather than degrees
of confidence.

<CostVerdictDiagram />

Read it left to right. The figure starts from the method that computed it. A method that could
not meet its own evidence bar returns `UNPROVABLE`, which renders no dollar and says what was
missing. Otherwise the question is whether the method is one of the two that may license a
measurement. Both of those read rows that already happened, and both give `PROVEN`, with a link
to the evidence attached. Everything else gives `ESTIMATED`.

The verdict is decided by the method, not by strength of feeling, and the wire enforces it: a
figure that fails any of the licensing rules does not serialise at all rather than serialising
without its paperwork.

<Callout type="warning">
**A recommendation is `ESTIMATED` at best, however good its quality evidence looks.** It is a
forecast about traffic that has not happened, so repricing your logged usage produces a model.
Only [the post-apply measurement](/docs/build/cost-realized) produces `PROVEN`. A `PROVEN`
dollar is always a measurement of something you already did.
</Callout>

## Quality and impact stay apart

A recommendation carries two verdicts that people routinely merge, and merging them is how a
safe-looking change gets adopted on the strength of an unrelated number.

**Quality** answers *did the cheaper option answer as well?* It comes from graded rows with a
statistical lower bound on the pass rate. **Impact** answers *how many dollars?* It is the
savings verdict above.

A slice can have thoroughly proven quality and an `ESTIMATED` impact at the same time. That is
the normal case, not a contradiction: knowing the cheaper model is good enough tells you nothing
about how much traffic will use it next month. The chip on a recommendation card shows the
*impact* verdict.

<Details summary="A worked refusal, taken from a live scan">

This is what a figure looks like when the evidence is not there. The dollar is `null`, the
verdict is `UNPROVABLE`, and the reason names both the bound that was missed and the sample
size that would clear it.

```json
{
  "usd": null,
  "unpriced": true,
  "verdict": "UNPROVABLE",
  "basis": {
    "method": "shadow-measured-delta-v1",
    "rows": 96,
    "reason": "quality is not established on this slice: the Wilson lower bound over 11 graded rows (10 passing) is 0.623, below the 0.90 licensing bound. About 35 all-pass graded rows on this scope would license it; grade more shadow rows on this slice to collect them"
  },
  "baseline": {
    "kind": "measured-current",
    "desc": "vs this slice's actual recorded spend over the window"
  },
  "approximations": ["fixed_usage", "volume_extrapolation"]
}
```

Ten of eleven rows passed, which is a 91% pass rate and sounds convincing. Eleven rows is not
enough for that to mean anything, so the lower bound of the interval is 0.623 and the figure
declines. The actionable half is the last sentence: about thirty-five all-pass graded rows on
this scope would clear the bar.

Note the field name trap. `pass_rate` on the wire carries the **lower bound**, not the raw pass
rate: 0.623, not 0.909. Read `labeled_rows` and the reason text rather than that field alone.

</Details>

## What can go wrong

**A `$0.00` saving read as failure.** A realized measurement of exactly zero can mean two
things: the change was exercised and delivered nothing, or every measured row happened to be
decided the same way as before, so the counterfactual is zero by construction. The record
distinguishes them. When every measured row sat at the prior tier, the server refuses to emit
the figure without a note saying so.

**An estimated total treated as exact.** `EST` is not a rounding warning. On a figure that fed a
chargeback allocation it is worth chasing down which bucket lacked a rate.

**A verdict cited without its vocabulary.** `PROVEN` belongs to savings figures; `BEAT` belongs
to routing. They are unrelated enums and neither shares a value with the other. See
[two verdict vocabularies](/docs/concepts/honesty-model#two-verdict-vocabularies--dont-conflate-them).

<NextSteps items={[
  {title: "Verdicts, methods and baselines", href: "/docs/build/cost-figure-reference", description: "The closed registries: eleven methods, five baselines, the labels, and the rules a figure must satisfy to serialise."},
  {title: "The honesty model", href: "/docs/concepts/honesty-model", description: "Where the verdict vocabulary is defined, and the nine invariants behind it."},
  {title: "After you apply: the realized measurement", href: "/docs/build/cost-realized", description: "The only path to a PROVEN saving, and why it is never subtracted from its projection."},
  {title: "Two kinds of cache savings", href: "/docs/concepts/cache-accounting", description: "Why the provider's prompt-cache discount and the response cache's avoided spend are never added together."},
]} />
