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. Needs a Cloudflare account id and API token — see Cloudflare AI Gateway.
- Ramp Router’s own model catalog for calls through Router: the rates Router bills, rather than a public listing. The catalog is account-scoped, and
wrap()reads the Router key from the client you pass in — see Ramp Router.
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 carry no unit price, so they produce no cost event — meter those in token mode if you bill on them. The 5-minute / 1-hour cache-write splits are priced separately only where the price source publishes a rate for each, which today is Ramp Router; everywhere else the whole cache_write count bills at the single write rate.
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. The exception is Ramp Router, where the response names the tier it was served at: a non-default tier there is a reported price miss rather than a base-rate charge. See Ramp Router.
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.
Snowflake Cortex is always billed as token counts, on both its REST and AI SQL function surfaces. Snowflake meters Cortex in credits at a per-credit rate that depends on edition, region and contract, published in no API and no view, so there is no rate card to look up and no later refresh that could supply one. A price-mode caller still gets token events for these rows, with no price-miss error, because a structural absence of a rate card is not a lookup failure. See Snowflake Cortex.
Ramp Router is priced from Router’s own catalog, at the default tier. Router publishes per-model rates in GET /v1/models for your account, and those are the rates it bills on every vendor it fronts. A call served at a non-default tier, or through a backend whose rate the catalog does not publish, falls back to token events with a PricingUnavailableError that says why. See Ramp Router.
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.