TLDR
xAI, Requesty, EU Router and Cortecs report the cost per call directly in the response — and there, our formula matched to the cent. OpenAI, Anthropic, Gemini and Mistral report only token counts, so you have to calculate the costs yourself. For that, you need current prices, which OpenAI, Mistral and Gemini publish only on web pages. And with routers: listed isn't charged. Part 3 of the series "What an LLM call really costs".
Introduction
As soon as a second department uses an AI application, the question comes up: who accounts for which part of the bill? The provider's monthly invoice doesn't help much — it knows no customers, no projects, no departments. Cost transparency only exists if you price every call yourself or the provider reports the cost per call.
Both are harder than they look. This part shows why.
Isn't billing per API key enough?
The obvious objection: the provider already bills per API key — why price every call yourself? The key answers exactly one question: which application? Every other cost question needs the cost per call.
- Customer, user or feature. A SaaS product serves many customers with the same code. Creating, distributing and rotating a key per customer doesn't scale. Without cost per customer, though, you can't see margins or outliers.
- Cost per transaction. What does a processed invoice, an answered ticket, an agent run cost? Only unit costs show the benefit; the total per key has no denominator.
- Budgets in real time. A run should stop before the budget is reached, not at the end of the month. For that, the admin APIs are too coarse or missing: OpenAI reports costs only per day. Anthropic denies an individual organisation access (HTTP 403). For the Gemini API there is only Cloud Billing (not measurable). Mistral offers a usage API only on the enterprise plan (not measurable). EU Router has only the dashboard (not measured).
- Several providers and routers. The costs sit in several dashboards and in different units: Cortecs reports micro-euros, xAI dollar ticks. With routers, the upstream actually chosen determines the price (section 7 and part 4); a key per router tells you nothing about that.
- Internal allocation or recharging. If you allocate costs to cost centres or recharge them to customers with a markup, you need line items you can back up.
- Proving an optimisation. Whether prompt caching or a lower reasoning effort really saves money shows only in the cache and reasoning tokens per call (part 1 and part 2).
1. The formula
The cost of a call comes from five token types and their prices:
cost = (input − cache read − cache write) × input price
+ cache read × read price
+ cache write × write price (5 min or 1 h)
+ output × output price (reasoning is part of output)
If a cache price is missing, we use the input price — better slightly overestimated than costs left out. Where the provider itself reported costs, this formula deviated by 0.0 %: at xAI, Requesty, EU Router and for Claude via Cortecs.
2. The field-name chaos
The formula is simple. Collecting the numbers isn't — every provider names the same quantity differently:
| Provider | Input includes cached tokens? | Cache read | Cache write | Reasoning |
|---|---|---|---|---|
| Anthropic | no | cache_read_input_tokens |
cache_creation_input_tokens (+ 5 min / 1 h) |
in output |
| OpenAI Responses | yes | input_tokens_details.cached_tokens |
input_tokens_details.cache_write_tokens |
output_tokens_details.reasoning_tokens |
| OpenAI-compatible | yes | prompt_tokens_details.cached_tokens |
differs by router | completion_tokens_details.reasoning_tokens |
| Gemini | yes | cachedContentTokenCount |
– | thoughtsTokenCount |
| Requesty | yes | cached_tokens |
caching_tokens (+ 5 min / 1 h) |
– |
At Anthropic, input_tokens doesn't include cached tokens; in OpenAI-compatible APIs, they're included in prompt_tokens. The field names differ at every provider. Requesty reports cache writes as caching_tokens. Gemini reports thinking as thoughtsTokenCount.
Note: A particularly tricky trap: when OpenAI stops a response because of max_output_tokens, the usage for that call reports 0 tokens — input included. The good news from the admin API (section 4): these calls weren't billed either. The bad news: there was no answer either — your application has to detect the case and retry.
3. Cost per call: who reports it?
| Provider / router | Cost in the response | Format | Deviation from our formula |
|---|---|---|---|
| xAI | yes | cost_in_usd_ticks (10¹⁰ = 1 USD) |
0.0 % |
| Requesty | yes | usage.cost in USD |
0.0 % |
| EU Router | yes | cost (USD) and cost_eur |
0.0 % (Claude) |
| Cortecs | yes | cost as an integer in micro-euros |
0.0 % (Claude) |
| OpenRouter | yes | usage.cost in USD |
0.0 % (Claude) |
| OpenAI, Anthropic, Gemini, Mistral, IONOS | no | token counts only | – |
xAI reports costs in ticks; 10 billion ticks are one dollar. Requesty and OpenRouter report USD, and EU Router also reports euros at the daily ECB rate. Contrary to its own API reference, Cortecs also reports costs in every response — as an integer in micro-euros, which isn't documented anywhere. No provider sends a cost header in the HTTP response.
4. Costs via admin APIs
If you don't get costs per call, some providers let you query aggregated costs — usually only with an admin key:
| Provider | Endpoint | Resolution | Status |
|---|---|---|---|
| OpenAI | /v1/organization/costs, /usage/completions |
costs per day and project, usage per minute and key | matched exactly |
| xAI | management-api.x.ai/v1/billing/teams/{team}/usage |
down to the second | matched exactly |
| Anthropic | /v1/organizations/cost_report, /usage_report/messages |
costs per day and workspace | blocked for individual organisations |
| Cortecs | /v1/manage/logs, /manage/usage |
per request, including fallbacks | not measured |
| Gemini | Cloud Billing only | hours to days of delay | not measurable |
| Mistral | admin API on Enterprise plans only | per day | not measurable |
At OpenAI, the admin API reported exactly the same token counts for our key as our raw data; it didn't even count the three aborted responses with 0 usage as requests — so they weren't billed either. The daily costs matched our formula to the cent for all three models and were available the same day. Costs come only per day, and the usage API has no reasoning field. The bill also shows a separate line item for 12-hour cache writes. xAI's management API reported exactly the sum of the cost fields of all individual responses. According to its docs, Anthropic reports costs per day and workspace (not measured) — for our individual organisation, the API refused access even with an organisation-wide key (HTTP 403). For the Gemini API, there's only Cloud Billing (not measurable). Mistral offers a usage API only on Enterprise plans (not measurable). EU Router has no API for cost history, only the dashboard (not measured).
Tip: For a clean match with admin APIs, every application needs its own key in its own project or workspace. Otherwise, the costs of several applications end up in the same pot.
5. Where the prices come from
Calculating costs yourself needs current prices. Only some providers publish them in machine-readable form:
- Via API: xAI , Cortecs (in EUR) , EU Router, Requesty and OpenRouter.
- Web page only: OpenAI, Mistral and Gemini. Anthropic's
/v1/modelsreturns capabilities and effort levels, but no prices.
There are also public collections. models.dev has no price for the 1-hour cache write ; LiteLLM does. The cache factors in the OpenRouter documentation differ from the providers' pricing pages.
And prices have tiers: xAI doubles all prices from 200,000 tokens of context , GPT-6.1 Sol from 272,000 input tokens. So one price per model isn't enough — you need a price per model and context size.
6. Our catalogue, checked
For CompanyWizard, we run our own model catalogue. It combines models.dev (which models exist) with LiteLLM (more precise cache prices, including 1 hour) and checks the prices against OpenRouter; deviations above 25 % go to the log. Router prices sit next to them as separate entries per host, in euros for Cortecs.
We held the catalogue against the measurements:
- Prices: For every measured model the catalogue lists, the prices matched the pricing pages.
- Effort levels: The levels matched as well — for Claude, Gemini and GPT-5.6 Luna exactly the values the API accepted.
- Gaps: Three of the measured models were missing: Claude Sonnet 5 at Anthropic direct, and GPT-6.1 Sol and gpt-5-mini at OpenAI.
The gaps come from the approval step: new models enter the catalogue as a proposal and reach the applications only after a manual check. That's intentional — a wrong price is worse than a missing one — but it costs freshness.
7. Listed isn't charged
Routers add another uncertainty: the listed price is often the price of the cheapest upstream, while you're charged for the one actually chosen.
| Router | Model | Deviation from the listed price (median) |
|---|---|---|
| EU Router | gpt-oss-120b | +103 % |
| Cortecs | gpt-oss-120b | +50 % to +67 % |
| Cortecs | DeepSeek V4.1 Flash | +44 % |
| Cortecs, EU Router, Requesty | Claude Sonnet 5 | 0 % |
For open-weight models, the charged costs were up to 170 % above the list price. For Claude they matched, but EU Router lists Claude Sonnet 5 10 % above the others.
8. How we solved it in .NET
Microsoft.Extensions.AI has a property for cache reads, but none for cache writes. So the Anthropic adapter puts them into an additional field — and only when the value is above 0:
public const String CacheCreationInputTokensKey = "CacheCreationInputTokens";
public TurnTokenUsage Add(UsageDetails? usage)
{
var cacheWrite = usage.AdditionalCounts?.TryGetValue(CacheCreationInputTokensKey, out var written) == true ? written : 0L;
return new TurnTokenUsage(
Input + (Int32) (usage.InputTokenCount ?? 0),
Output + (Int32) (usage.OutputTokenCount ?? 0),
CacheRead + (Int32) (usage.CachedInputTokenCount ?? 0),
CacheWrite + (Int32) cacheWrite);
}
Source: TurnTokenUsage (excerpt)
In CompanyWizard, TurnTokenUsage counts at every point where a conversation turn calls a model, including tool loops and after interruptions. A single booking path turns this into one record per turn.
Lesson Learned: A stream that breaks off midway books nothing in our setup — the Anthropic SDK reports usage only at the end of the stream. This gap is known and documented. It isn't closed.
9. What we deliberately left open
- Admin APIs: we didn't reconcile Anthropic (individual organisation, HTTP 403) and Cortecs (no management key).
- Batch, flex and priority tiers: not part of this series.
- Image prices: not part of it either.
Conclusion: patterns you can reuse
- Normalise usage per provider before you calculate — above all, whether input includes cached tokens.
- Use cost fields where they exist, and check your own formula against them.
- Store prices with a date and a context tier, not as one number per model.
- The key for the application, the cost per call for everything below it: a separate key per application keeps admin APIs attributable; customer, transaction and budget need the cost per call.
- Pin the upstream at routers if the price has to be predictable.
All raw data and scripts: github.com/evanto-media-ag/llm-caching-reasoning-costs
The series
- Part 1: Prompt caching compared — what carries the cache and what breaks it
- Part 2: Reasoning effort compared — seven values, twelve interpretations
- Part 3: Cost transparency — formulas, field names, price sources
- Part 4: Routers compared — Cortecs, EU Router, Requesty, OpenRouter
References
- OpenAI: Usage API cookbook
- Anthropic: Usage and Cost API
- xAI: Cost tracking
- Cortecs: Management API · Requesty: Cost tracking · EU Router: Generation API
- models.dev · LiteLLM price table
Want to show AI costs per customer or department? Let's spend 30 minutes on your specific workflow — no obligation, no cost, with concrete next steps at the end. Book a 30-minute call
Note: Article created with AI assistance.