← All posts
Company

Cost Honesty: Unpriced Is Never $0 on Your LLM Bill

The most important number in an LLM gateway is what a call cost, and it is the easiest one to fudge. We read the provider's settled cost and pass it through exactly; when it is unknowable we say unpriced, never zero.

nRouter team · 10 min read
Cost Honesty: Unpriced Is Never $0 on Your LLM Bill

The position

We read the provider's settled cost for the call it actually served and bill exactly that. We do not compute a cost, and when a cost is genuinely unknowable we report it as unpriced — never as $0, because a confident zero is the most expensive lie a gateway can tell.

Every number a customer sees on nRouter traces back to one value: what a request cost. Your budget, your attribution split, your per-customer margin and your invoice are all arithmetic on that single figure. It is the most important number in the product and the easiest one to quietly get wrong in our own favour.

So we made a principle of it and wrote it down, because a principle nobody published is a preference. This post is what the principle is, what it costs you, and how to check that we are keeping it.

What we were asked for instead

The request we keep declining, stated at its strongest: fill every cell. Give us a cost column with a number in every row. A blank breaks the pivot table, a blank breaks the chargeback report, and a blank is the one thing an internal finance system cannot consume. Estimate it if you must — an approximate number beats a hole.

We understand why this is asked, and the case for it is genuinely strong:

Their argumentWhy it holds up
A blank breaks downstream systemsA chargeback pipeline that must handle nulls is a pipeline with a bug waiting in it
An estimate is better than nothingFor capacity planning, ±5% is usually decision-equivalent to exact
Dashboards with holes lose trustUsers read a gap as a broken product, not as an honest one
Everyone else shows a numberA competitor's fully-populated column looks more complete in a bake-off

The last row is the one that stings, because it is true and it costs us evaluations. A gateway that computes cost has a prettier dashboard than one that admits an unknown. We still will not do it, and the next section is why.

What that would cost you

A computed cost is plausible. That is exactly what makes it dangerous — you cannot tell a computed estimate from a settled charge by looking at it, so the incentive to shade it is enormous and the chance of detection is near zero. Three corruptions follow, and none of them requires anyone to act in bad faith:

  1. Stale prices. A price table is a copy of somebody else's data with no invalidation signal. Providers change per-model rates, split rates by cache tier, and ship new model families with different billing units. Stale-low undercharges you today and produces a shocking correction later; stale-high overcharges you quietly forever.
  2. Convenient rounding. Rounding policy sounds like a detail until every rounding decision goes the same direction. A half-cent per call, always up, is a margin nobody agreed to and nobody can see.
  3. Embedded markup. A markup baked into a computed rate is invisible by construction. You cannot decompose a single number into "provider rate" and "our cut" without being told the split, so the only defence is not to compute the number at all.

Work the arithmetic on the first one. This is an illustration, not a measurement of anything we have done:

Scenario (illustrative)Monthly provider spendPrice-table driftCost of the drift
Table 3% stale-high$20,000+3%$600/mo overstated, $7,200/yr
Table 3% stale-low$20,000−3%$600/mo understated, corrected later as a surprise
Unknown cost billed as $0any−100% on those callsBudgets never advance; the ceiling silently stops existing

The third row is the serious one. A missing price rendered as $0 does not just misreport — it disables the controls built on top of the number. A budget that sums zeros never trips. A rate ceiling keyed on spend never bites. The failure is silent, and silence is worse than a loud error, because nothing pages anybody.

What we do instead

The provider serves the call and reports what it settled at. We read that value and bill it. There is no price table to go stale, because we are not the ones pricing the call.

request → provider serves it → provider reports the settled cost

                        we read that authoritative value   ← the only number we trust

                        settle credits against THAT, exactly

                        write one ledger entry, sum reconcilable to the balance

Because that value is not always available, a cost has exactly three states, and we expose all three rather than collapsing them into one column:

StateHeader behaviourWhat it meansWhat you should do
exactx-nr-request-cost present, x-nr-cost-status: exactThe provider reported a settled cost; this is what you were billedUse it for attribution and chargeback directly
unpricedx-nr-request-cost absent, x-nr-cost-status: unpricedThe call succeeded but its cost is not knowable from what the provider returnedCount the call, do not invent a figure; reconcile at the ledger
In flightNeither finalA hold is placed before the call and settled after itRead the ledger, not the header, for the final figure

The reservation half of that is not an implementation detail either. A hold is taken before the request leaves, and every exit path either settles it or releases it — the mechanism is reserve-and-settle, and it is why a balance cannot go negative under concurrent load. One deliberately counter-intuitive rule falls out of the honesty principle: an unpriced call settles at the reserved amount rather than releasing, because releasing it would make the request free, and free requests are how a gateway leaks money while every dashboard reads green.

Why an absent header beats a zero

The header contract is the smallest place this principle becomes observable, so it is worth being precise about it. When the cost is unknown, x-nr-request-cost is absent — not 0, not null, not an empty string. x-nr-cost-status carries unpriced alongside it.

resp = client.chat.completions.with_raw_response.create(
    model="claude-sonnet-4-5-20250929",
    messages=[{"role": "user", "content": prompt}],
)

cost = resp.headers.get("x-nr-request-cost")     # None when unpriced
status = resp.headers.get("x-nr-cost-status")    # "exact" | "unpriced"

if cost is None:
    metrics.increment("llm.cost.unpriced")       # count it, never zero-fill it
else:
    metrics.observe("llm.cost.usd", float(cost))

An absent field forces the consumer to make a decision. A zero makes the decision silently and wrongly, and the decision it makes is "this call was free." Every downstream aggregate then inherits that claim: the team's spend total, the customer's margin, the model's cost-per-request ranking in cost vs usage analytics. One zero-filled column is a whole quarter of quietly wrong economics.

The same logic runs through the non-text modalities, where per-unit pricing is least standardised across providers. That is what multimodal cost floors are for: a missing price reads as a conservative charge, deliberately, rather than as a dishonest free call.

What honest cost makes possible

Cost honesty is not one feature. It is the precondition for four others, each of which is only worth what the underlying number is worth:

  • Hard spend limits cap real spend, so a cap means what it says. Sum estimates instead and your ceiling is an estimate too.
  • Attribution by team, customer and feature reconciles to the invoice, which is the only test that matters. Estimates sum to a number that is close to the bill and never equal to it — and "close" is exactly what an argument between two teams is made of.
  • Markup-free credits are only credible if the per-call cost carries no hidden cut. It cannot carry one, because we do not set it. Our revenue is the platform fee, charged visibly on top — the reasoning is in Every Feature on Every Plan: We Charge a Fee, Not a Gate.
  • Per-customer billing for AI apps only works if your cost of goods is real. Reselling on top of an estimate means your margin is an estimate, which you will discover at the worst possible time.

Pull the honest number out and every one of those becomes something you take on faith. Keep it, and they are all just arithmetic on a value you can verify. The cross-provider mechanics of getting that value in the first place are in the cost tracking guide.

The same refusal, everywhere

Cost is where this stance matters most, because cost is where the incentive to fudge is strongest. But it is one instance of a broader refusal to build a convenient fiction next to the truth:

  • No mocks, no demo mode. There is no environment where the system pretends. The argument is in why we ban mocks and demo mode, and it is the same argument as this one, pointed at behaviour instead of at money.
  • Real tenants, real isolation. Separation is enforced beneath the application rather than asserted by it, so "isolated" is a property that can be tested rather than a claim in a doc.
  • The ledger is the record. Every credit movement is an entry, and the entries sum to the balance. There is no second place where a balance is stored more conveniently.
  • Compliance stated at its actual stage. SOC 2 Type II is in progress. Not certified, not compliant — in progress, on the trust page, until the day it is not.

The trade we are making

This costs you things, and they are not hypothetical.

Your dashboard will sometimes have a hole. Where a competitor shows a confident figure, we show unpriced and a count. If your finance pipeline cannot consume a null, that is real integration work we are pushing onto you, and we know it.

We lose bake-offs on completeness. A fully-populated cost column looks better in a side-by-side grid than an honest one with gaps. We have watched this happen and we are not going to fix it by inventing numbers.

You cannot ask us to price a model we cannot price. There is no override that makes a number appear. If a provider does not report enough to settle a call, the answer stays unpriced until the provider changes what it reports.

Reconciliation is on you, if you want it. We publish the ledger and the header. Verifying that they agree is a job we make possible and do not do for you.

Who this is wrong for

  • Teams that require a fully populated cost column, unconditionally. If a chargeback system cannot represent "unknown" and cannot be changed, our honest answer is an integration problem you will fight every month. Pick a vendor that estimates, and know that is what you picked.
  • Anyone who wants a blended internal rate. Some organisations deliberately want one flat internal price per token, decoupled from provider reality, to keep budgeting simple. That is a legitimate model. It is the opposite of this one.
  • Buyers optimising a capability grid. If completeness of the cost column is a scored row, we score badly on it by design.
  • Teams that need cost known before the call, exactly. Pre-flight is a reservation, not a settlement. If you need an exact figure before egress, no gateway can honestly give you one.

How you can hold us to it

Not one of these requires talking to us.

  1. Read the header on a live call. Confirm x-nr-request-cost is absent — not 0 — on any call whose status is unpriced. If you ever see a zero paired with unpriced, that is a defect and we want the request id.
  2. Reconcile the ledger. Sum a day of ledger entries and compare against the balance change. Reading a spend ledger walks through it; the billing guide says where to find it.
  3. Cross-check one call against the provider's own rate card. Take an exact cost, look up the model on the vendor's public pricing page from the Sources list, and do the multiplication yourself.
  4. Compare the dashboard to the raw numbers. The analytics guide shows the aggregates; they should equal the sum of what you collected from the headers.
  5. Test the ceiling. Set a small budget and prove the 402 arrives before the provider call, not after. Handling 429 and 402 is the client-side half.
  6. Check what we charge on top. The pricing page states the platform fee. If our revenue ever appears anywhere other than that visible line, this post is false.

Try it

Point one existing service at https://api.nrouter.ai/v1 with NROUTER_API_KEY, log both cost headers for a day, and reconcile your total against the ledger. Then deliberately call something unusual — a model or modality at the edge of your catalog — and watch what happens when the cost is not knowable. The absent header is the whole argument of this post, and it takes about ten minutes to see. Start at signup; the current catalog is on the models page.

See also

Sources

Verified 2026-08-23. Provider rate cards change; if any link below has moved or changed, email hello@nrouter.ai and we will correct this post.

OpenAI, Anthropic and AWS are trademarks of their respective owners. nRouter is not affiliated with or endorsed by them. All claims above are sourced from their public pricing or documentation on the date shown.

Share
Written by nRouter teamEngineering, product, and company posts from the nRouter team — code-first, cost-honest, no vendor-marketing fluff.