Which mode do you want
You can also mix: set one mode globally and override per call with
extra_lago={"mode": "price"} in Python or lago: { mode: "price" } in TypeScript.
Token mode
The SDK emits one event per non-zero field. A field with no metric code is not emitted at all, so trimmingmetric_codes down to what you actually bill on is a legitimate way to cut event volume.
Register each one you plan to bill on as a
sum_agg billable metric on field_name: "value". Every event also carries model, provider, and api in its properties, so you can build filter-based charges without passing anything extra.
Subsets and additive fields
This is the one thing to get right, and it is the source of most mis-billing. Some fields are breakdowns inside another field. Some are additions to it. Summing a subset into its parent double-counts; forgetting to add an additive field under-counts.
So
llm_input_tokens is already your total prompt count on OpenAI and Gemini, but on Anthropic your true total is llm_input_tokens + llm_cached_input_tokens + llm_cache_creation_tokens.
The setup that gets this right
Rather than reasoning about it per provider, bill the parent fields at your standard rate and use the subset fields only to discount, with aprovider filter to separate the two rules.
For a mixed OpenAI / Anthropic / Gemini estate:
See Charges with filters for the filter syntax.
Price mode
Where prices come from
- OpenRouter’s public model list for native OpenAI, Anthropic, Mistral, and Gemini clients. No credentials.
- The AWS Bedrock Price List Bulk API for Bedrock, parsed per region. No credentials.
- Cloudflare’s own model catalog for Workers AI. This is the one source that needs credentials — see Cloudflare AI Gateway.
claude-sonnet-4-5 becomes claude-sonnet-4-5-20250929, Mistral’s -latest and Gemini’s aliases hot-swap the same way), and OpenRouter lists the snapshot. The adapters read the model off the response and fall back to the requested id only when the response is silent about it.
What it emits
Onellm_cost event per priced token type, each carrying precise_total_amount_cents at the top level plus token_type, unit (tokens of that type), value (cost after markup), base_cost (before markup), unit_price, markup, price_source, model, provider, and api in properties.
The exception is a cost the SDK did not compute. When a gateway reports its own metered cost per call and you pass it through, there is no per-field split to report, so that path emits a single event with no token_type.
Lago setup for price mode. Register a
sum_agg billable metric llm_cost on field_name: "unit" and attach a dynamic charge to it. Lago sums each event’s precise_total_amount_cents into a single fee, and unit is the displayed usage quantity.Group the charge by ["model", "token_type"] so one metric breaks the cost down by both dimensions.Markup
Setmarkup to resell at a profit — 1.2 means the customer pays your cost plus 20%. It applies per priced field, and both the pre-markup base_cost and post-markup value land on every event so the margin stays auditable. Override it for one call with extra_lago={"markup": 1.5}.
Known limits
Price mode prices five fields:input, output, cache_read, cache_write, and reasoning. Tool calls and the 5-minute / 1-hour cache-write splits carry no unit price, so they produce no cost event — meter those in token mode if you bill on them.
Audio and image tokens are priced at the text rate. They sit inside input/output, so they are billed, but at the model’s text price rather than its modality price. Providers often charge considerably more for audio. If your workload is audio-heavy, bill it in token mode using llm_audio_input_tokens and llm_audio_output_tokens and set your own rate.
Long-context and service-tier rates are not modelled. Some providers raise the per-token rate above a context threshold, and discount batch or flex tiers. The SDK prices everything at the model’s base rate.
A price miss never drops usage. If the table has not warmed up on the very first call, or the model is missing from the source, the SDK falls back to token-count events and reports a PricingUnavailableError through on_error. It never bills zero and never drops the call.
Databricks-hosted models are always billed as token counts, even in price mode. Their provider name is deliberately unmatched against the price sources, because the open-weight models Databricks hosts are listed elsewhere at a fraction of what Databricks charges. The SDK logs this once per model at info level rather than reporting an error. See Databricks.
AWS’s public bulk price data omits the current Claude models. It lists Titan, Llama, Mistral, Cohere and older Claude, but at time of writing not Claude 3.5/3.7/4. Bedrock calls for those fall back to token events. Native Anthropic clients are priced through OpenRouter and unaffected.
Custom metric codes
If your Lago tenant already uses different codes, override them at init:Next steps
Configuration reference
Every config knob, error type, and the
emit() escape hatch.Charges with filters
Price cache reads and cache writes at different rates.