Define metrics once
Token metrics carry customer amount. Virtual metrics reuse those facts for revenue rollup and supplier cost.
Meter input, output, and cached tokens once. Let amount stay as customer revenue, then add analytics-only metrics for total revenue and cost so finance can chart margin without double charging customers.
The customer-facing token metrics keep their quantity and amount. Analytics-only metrics reuse those results for reporting, without entering debit.
Token metrics carry customer amount. Virtual metrics reuse those facts for revenue rollup and supplier cost.
The gateway sends customer prices and cost prices in the same event, keyed by metric.
The chart calls the public analysis query API for total_tokens and cost, then renders grouped series directly.
Create and publish through /v1/projects/:project_id/extractor-rule-sets.
{
"id": "ers_llm_gateway_finance",
"name": "LLM Gateway Finance Metrics",
"subject": {
"default_type": "api_key",
"type_path": "$.meta.subject_type",
"id_path": "$.meta.subject_id",
"required": true
},
"dimensions": [
{ "name": "provider", "path": "$.provider", "type": "string", "required": true },
{ "name": "model", "path": "$.model", "type": "string", "required": true },
{ "name": "customer_id", "path": "$.meta.customer_id", "type": "string", "required": true }
],
"charge_item_extractors": [
{
"name": "input-tokens",
"metric": "input_tokens",
"path": "$.usage.input_tokens",
"type": "number",
"unit": "token",
"skip_if_missing": true,
"currency": "USD",
"unit_price_expr": "dec('0')",
"amount_expr": "dec_mul(dec_div(dec(item['quantity']), dec(item['pricing_unit'])), dec(item['unit_price']))"
},
{
"name": "output-tokens",
"metric": "output_tokens",
"path": "$.usage.output_tokens",
"type": "number",
"unit": "token",
"skip_if_missing": true,
"currency": "USD",
"unit_price_expr": "dec('0')",
"amount_expr": "dec_mul(dec_div(dec(item['quantity']), dec(item['pricing_unit'])), dec(item['unit_price']))"
},
{
"name": "cached-tokens",
"metric": "cached_tokens",
"path": "$.usage.input_tokens_details.cached_tokens",
"type": "number",
"unit": "token",
"skip_if_missing": true,
"currency": "USD",
"unit_price_expr": "dec('0')",
"amount_expr": "dec_mul(dec_div(dec(item['quantity']), dec(item['pricing_unit'])), dec(item['unit_price']))"
},
{
"name": "total-tokens",
"metric": "total_tokens",
"quantity_expr": "dec_add(dec_add(dec(items['input_tokens']['quantity']), dec(items['output_tokens']['quantity'])), dec(items['cached_tokens']['quantity']))",
"type": "number",
"unit": "token",
"currency": "USD",
"amount_expr": "dec_add(dec_add(dec(items['input_tokens']['amount']), dec(items['output_tokens']['amount'])), dec(items['cached_tokens']['amount']))",
"control": { "analytics_only": "true" }
},
{
"name": "cost",
"metric": "cost",
"quantity_expr": "dec('0')",
"type": "number",
"unit": "event",
"currency": "USD",
"amount_expr": "dec_add(dec_add(dec_mul(dec_div(dec(items['input_tokens']['quantity']), dec(pricing_hints['cost.input_tokens']['pricing_unit'])), dec(pricing_hints['cost.input_tokens']['unit_price'])), dec_mul(dec_div(dec(items['output_tokens']['quantity']), dec(pricing_hints['cost.output_tokens']['pricing_unit'])), dec(pricing_hints['cost.output_tokens']['unit_price']))), dec_mul(dec_div(dec(items['cached_tokens']['quantity']), dec(pricing_hints['cost.cached_tokens']['pricing_unit'])), dec(pricing_hints['cost.cached_tokens']['unit_price'])))",
"control": { "analytics_only": "true" }
}
]
}The API returns metric buckets. The chart joins matching buckets, calculates profit and margin, then renders revenue and cost as two independent lines.
Start with your gateway data