# Cost accounting

> Where pricing comes from, how spend is tracked, and how to query usage.

URL: https://boelabs.com/en/docs/ai-gateway/cost-accounting
Language: English

## Where pricing comes from

Every model's rate is declared in cents-per-1M-tokens in its catalog entry's `pricing`
(`inputCentsPerMTokens`, `outputCentsPerMTokens`, `cacheReadCentsPerMTokens`, `cacheWriteCentsPerMTokens`)
and reranking search units use `searchUnitCents`. Token and search-unit components are additive when
both are configured.

Image tokens have their own rates, `imageInputCentsPerMTokens` and `imageOutputCentsPerMTokens`,
because providers bill them far above text (Gemini 3 Pro Image: $12 per 1M text output, $120 image
output). Uncached image input and image output are charged at those rates; absent, they fall back to
the input and output rates. Adapters report the split when the provider does (Gemini's per-modality
counts, OpenAI's `input_tokens_details`/`output_tokens_details`); output of an image endpoint that
the provider does not split is counted as image output. The Vercel snapshot inherits a first-party
model's image rates only when Vercel resells it at the provider's own price.

Audio tokens work the same way with `audioInputCentsPerMTokens` and `audioOutputCentsPerMTokens`:
Gemini's Live models bill audio well above text ($3 against $0.75 per 1M input, $12 against $4.50
output), and realtime sessions report the split per modality.

Video output tokens use `videoOutputCentsPerMTokens`; absent, they fall back to the output rate. Video
jobs finish after the request returns, so the gateway charges them when the job reaches a terminal
state, from the provider's reported usage (see [Videos](https://boelabs.com/docs/ai-gateway/videos#gemini-omni-flash)).
A deployment's own `pricing` field, if set, **overrides** the catalog rate entirely for that deployment
— useful for a negotiated rate or a custom model with no catalog entry at all. A model with no pricing
anywhere (no catalog rate, no override) normally bills as **zero cost**, not an error. Reranking has
one guarded fallback: when no configured rate exists and OpenRouter reports `usage.cost`, that value
is used so a paid request is not falsely recorded as free. When both are present, configured pricing
is consumer cost and the upstream value is retained separately for reconciliation.

## How spend is tracked

On every request with usage (tokens and/or reranking search units actually consumed), the gateway computes the cost from the
resolved pricing and:

- **Redis** is the hot-path counter: virtual-key TPM windows and the running budget total are checked
  and incremented here, on the request's critical path, because it needs to be fast and atomic.
- **Postgres** (`spend_cents` on the virtual key, plus consumer and estimated upstream cost in `gateway_operations`)
  is the durable source of truth, updated on every billed request.

Master-key traffic is never billed against any budget (the master key has no limits) — see
[Security](https://boelabs.com/docs/ai-gateway/security) for the full auth model.

## Budgets and RPM/TPM (per virtual key)

Covered in full in [Virtual keys](https://boelabs.com/docs/ai-gateway/virtual-keys#budgets-and-limits): a budget's window resets on
its configured cadence (`hourly`, `daily`, `weekly`, or `monthly`), and exceeding it or an RPM/TPM limit
returns `429 rate_limit_exceeded` with the matching `x-ratelimit-*` headers so a well-behaved client can
back off before being throttled.

## Querying usage

`GET /admin/usage` aggregates cost and request counts, grouped by `public_model`, `virtual_key`,
`day`, or ungrouped (`none`) — the same filters as `GET /admin/logs` apply (date range,
virtual key, public model, deployment, adapter, call type, status, cache hit). See
[Admin API reference](https://boelabs.com/docs/ai-gateway/reference-admin-api#logs-and-usage).

```bash
curl "$GATEWAY/admin/usage?groupBy=public_model&start=2026-06-01&end=2026-07-01" \
  -H "Authorization: Bearer $MASTER_KEY"
```

For per-request detail, `GET /admin/logs` returns consumer cost and estimated upstream cost; the detail
route includes every deployment attempt. A cache hit is the one exception to "every request with usage
is billed": it returns before cost accounting runs at all, logs with `cost: null`, and never touches the
virtual key's spend or TPM counters — see [Caching](https://boelabs.com/docs/ai-gateway/caching).

Reranking is never response-cached. Search-unit cost still increments virtual-key spend and budgets
when `totalTokens` is zero. RPM applies normally; TPM is not synthesized from search units.

## Next steps

- [Virtual keys](https://boelabs.com/docs/ai-gateway/virtual-keys) — budgets, RPM/TPM limits, and their reset cadence.
- [Observability](https://boelabs.com/docs/ai-gateway/observability) — operation logs and telemetry beyond cost.
- [Model catalog](https://boelabs.com/docs/ai-gateway/model-catalog) — the `pricing` schema.

