VynarisEarly betaGet your API key

Metering dollars per call: why your agent's usage object hides 73% of the bill

A reasoning step on Opus 4.8 bills 9,500 output tokens for a 1,500-token answer; a naive meter reads 32% low. A 20-line recipe to reconstruct true dollars per call from any provider's usage object, verified 2026-07-20.

An agent step on Opus 4.8 that shows a 1,500-token answer can bill for 9,500 output tokens and cost $0.2750, while a cost meter built from prompt length plus completion length reads $0.1875, 32% low. The gap is reasoning tokens, which bill at the output rate and account for 73% of that step, plus cache reads you overcount at full input price. This is a recipe to reconstruct true dollars per call from the usage object, verified 2026-07-20.

The finding, before the code

Every provider returns a usage object with token counts. None of them return dollars. The counts do not map to price the obvious way: reasoning tokens bill at the output rate but never appear in the text you read, cache reads bill at a tenth of input, and cache writes bill at a premium above it. A meter that multiplies prompt_tokens by the input price and completion_tokens by the output price gets all three wrong, and the errors do not cancel. On a reasoning model the reasoning line alone can be most of the bill.

We built this from public pricing only. No product data. Every price is from a provider's live page, captured 2026-07-20, and every number below is reproducible from the token counts.

Why the usage object won't tell you

The counts you need are there, under names that vary by provider, and the price each count carries is not in the response at all. Three token categories break a naive meter.

The price map you actually need

The unit of a meter is not "the model's price." It is the price per billed category. Here are the categories, per million tokens, verified 2026-07-20.

Model                                                              Input  Output  Cache read  Cache write (5m)
-----------------------------------------------------------------  -----  ------  ----------  ----------------
Opus 4.8                                                           $5.00  $25.00  $0.50       $6.25
[GPT-5.6 Sol](https://vynaris.com/models#gpt-5-6-sol)              $5.00  $30.00  $0.50       $6.25
[Haiku 4.5](https://vynaris.com/models#claude-haiku-4-5)           $1.00  $5.00   $0.10       $1.25
[deepseek-v4-flash](https://vynaris.com/models#deepseek-v4-flash)  $0.14  $0.28   $0.0028     $0.14

Reasoning tokens have no column because they bill at the output rate. That is the whole trick: you meter them as output, and the usage object reports them separately so you can attribute them.

The core meter

One function. It takes the token counts a provider returns and the price map above, and returns dollars for the call.

PRICES = {  # $ per 1M tokens, verified 2026-07-20
    "opus-4.8":          dict(inp=5.00, out=25.00, read=0.50, write=6.25),
    "gpt-5.6-sol":       dict(inp=5.00, out=30.00, read=0.50, write=6.25),
    "haiku-4.5":         dict(inp=1.00, out=5.00,  read=0.10, write=1.25),
    "deepseek-v4-flash": dict(inp=0.14, out=0.28,  read=0.0028, write=0.14),
}
M = 1_000_000

def call_cost(model, fresh_input, cache_read, output, reasoning=0, cache_write=0):
    p = PRICES[model]
    return (
        fresh_input * p["inp"]   +   # tokens NOT served from cache
        cache_read  * p["read"]  +   # tokens served from cache
        cache_write * p["write"] +   # tokens stored to cache this turn
        output      * p["out"]   +   # visible completion
        reasoning   * p["out"]       # hidden thinking, billed as output
    ) / M

The only discipline this needs is splitting prompt_tokens into fresh_input and cache_read, and pulling reasoning out of the output details. Both numbers are in the usage object.

Mapping each provider's usage object

The field names differ. Here is where each count lives.

OpenAI returns prompt_tokens_details.cached_tokens and completion_tokens_details.reasoning_tokens. The reasoning count is already inside completion_tokens, so do not add it twice; subtract it out as the visible portion.

u = response.usage
cache_read  = u.prompt_tokens_details.cached_tokens
fresh_input = u.prompt_tokens - cache_read
reasoning   = u.completion_tokens_details.reasoning_tokens
output      = u.completion_tokens - reasoning
cost = call_cost("gpt-5.6-sol", fresh_input, cache_read, output, reasoning)

Anthropic returns input_tokens (already excludes cached), cache_read_input_tokens, cache_creation_input_tokens, and output_tokens (thinking tokens are already counted in output). Map cache creation to the write price for the window you used.

u = response.usage
cost = call_cost("opus-4.8",
    fresh_input = u.input_tokens,
    cache_read  = u.cache_read_input_tokens,
    cache_write = u.cache_creation_input_tokens,
    output      = u.output_tokens)   # thinking already inside output_tokens

DeepSeek returns prompt_cache_hit_tokens and prompt_cache_miss_tokens. The miss count is your fresh input; there is no write premium, so cache writes fold into input.

u = response["usage"]
cost = call_cost("deepseek-v4-flash",
    fresh_input = u["prompt_cache_miss_tokens"],
    cache_read  = u["prompt_cache_hit_tokens"],
    output      = u["completion_tokens"])

A worked step: correct versus naive

One agent step in steady state, context already cached. The prompt is 30,000 tokens, 25,000 of them served from cache and 5,000 fresh. The output is 1,500 visible completion tokens plus 8,000 reasoning tokens. On Opus 4.8:

Component       Tokens  Rate /M  Cost
--------------  ------  -------  -----------
Fresh input     5,000   $5.00    $0.0250
Cache read      25,000  $0.50    $0.0125
Visible output  1,500   $25.00   $0.0375
Reasoning       8,000   $25.00   $0.2000
**Total**                        **$0.2750**

Reasoning is 73% of the step. A naive meter, 30,000 x $5 input + 1,500 x $25 output, reads $0.1875, which is 32% low. The two mistakes pull in opposite directions and do not cancel: billing the 25,000 cached tokens at full input instead of the cache rate overcounts by $0.1125, while ignoring the 8,000 reasoning tokens undercounts by $0.2000. The reasoning leak is 1.8x the cache overcount, so the meter lands low. Budgeting from the visible answer is worse than it looks: you assume 1,500 output tokens where 9,500 are billed, 6.3x more. This is the per-step version of what we traced across a whole run in where the tokens actually go.

The first turn is different again, because it writes the cache instead of reading it. Storing that 25,000-token prefix at the $6.25 five-minute write rate costs $0.1563, so the opening step of a session runs $0.4188 against $0.2750 for a cached follow-up. A meter that treats every turn identically misreads both. To run your own token shape, the calculator takes the per-call counts directly.

Reconstructing the fields the API omits

Two cases where a count is missing rather than merely renamed.

The reasoning field is the one you must never reconstruct by guessing. If reasoning_tokens is present, use it. If your model does not expose it, the tokens are still billed inside output_tokens, so meter total output at the output rate and accept that you cannot split thinking from answer.

From per-call to a live meter

The function returns dollars for one call. A usable meter wraps every model call, accumulates by the dimensions you care about, and alerts on outliers.

class Meter:
    def __init__(self): self.total = 0.0; self.by_call = []
    def record(self, model, **counts):
        c = call_cost(model, **counts)
        self.total += c
        self.by_call.append((model, c, counts))
        if c > 0.50:                       # per-call alert threshold
            log.warning("expensive call: $%.4f on %s %s", c, model, counts)
        return c

Accumulate self.total per request to get cost per user action, per session to get cost per conversation, and per user id to find the accounts that cost you money. The outlier alert matters more than the total: a single step that reasons for 30,000 tokens costs $0.75 on Opus 4.8, and finding it is how you learn to cap the reasoning budget or route that step to a cheaper model. That is the same model-routing decision the reasoning-token tax post ends on.

Horizontal bar chart of the cost of one identical agent step across four models on a log scale, single blue hue: DeepSeek v4-flash $0.00343, Haiku 4.5 $0.055, Opus 4.8 $0.275, GPT-5.6 Sol $0.3225, with a 94x spread for the same token shape
Cost of one identical agent step (5k fresh + 25k cached input, 1.5k visible + 8k reasoning output) across four models, log scale. Same token shape, 94x price spread. Prices verified 2026-07-20.

The same step costs $0.00343 on deepseek-v4-flash and $0.3225 on GPT-5.6 Sol, a 94x spread on identical token counts. A meter is what tells you which steps are paying that 94x for no reason.

The honest tradeoff

A dollars-per-call meter is an estimate, not your invoice. Three things keep it from matching the bill to the cent. Prices change, so a hardcoded price map goes stale; re-verify it on a schedule and stamp the date, as this post does. Providers add token categories, like the cache-write split between five-minute and one-hour windows, and a meter that predates a category silently misattributes it. And per-request rounding differs from how the provider aggregates a monthly bill. The meter is right to a percent or two, which is enough to catch the step reasoning for 30,000 tokens and the user running you a hundred calls a minute. It is not a substitute for reconciling against the provider's own usage export monthly. Build the meter for control, not for accounting.

Where Vynaris fits

Product section, skip if you only want the DIY recipe. The meter above is the thing a gateway should hand you for free. Vynaris is an OpenAI-compatible gateway that returns a normalized usage object with fresh input, cache read, cache write, output, and reasoning already split and priced, so the per-call dollar figure is in the response instead of reconstructed downstream. It also routes each call to the cheapest right-sized model and caps reasoning budgets on the steps that overspend, which is the action the meter exists to trigger. One base URL swap. The DIY recipe here works without it; the gateway removes the field-mapping and the stale price map.

FAQ

Why is my agent's API bill higher than my token estimate? Almost always reasoning tokens. They bill at the output rate and do not appear in the answer text, so a meter built from prompt and completion length misses them. On a reasoning step they can be 73% of the cost, as in the Opus 4.8 example here.

How do I calculate the cost of an OpenAI API call? Split prompt_tokens into cached and fresh using prompt_tokens_details.cached_tokens, pull reasoning_tokens from completion_tokens_details, then price fresh input at the input rate, cached at the cache-read rate, and both visible and reasoning output at the output rate. The 20-line function above does it.

Do reasoning tokens cost money? Yes, at the output-token rate, which is 5x to 18x the input rate depending on the model. They are billed even though you never see them, and the usage object reports the count separately so you can attribute the spend.

How do I get token usage from a streaming response? On OpenAI, pass stream_options={"include_usage": True} and read the usage object from the final chunk. Without it a streamed call returns no counts and you are forced to estimate, which misses reasoning tokens entirely.

Is a homemade cost meter accurate? To within a percent or two, enough to catch expensive calls and costly users. It drifts when prices change or providers add token categories, so re-verify the price map on a schedule and reconcile against the provider's monthly usage export.

Sources

Prices change. We re-verify every figure in this post monthly and stamp updates. Numbers here are current as of 2026-07-20.