CommonBill

Records what every model call costs, by tag, and reports it. See what each client, area or profile costs you.

CommonBill writes one row for every successful model call: who made it, which model, how many tokens, what it cost, and the turn’s tags. Then it answers questions like “what did Acme cost this month?”

Id: bill · Table: model_calls · Route: GET /api/costs

What it records

CommonBill hooks post_api_request, which fires after each successful model call. It does not use post_llm_call, because that fires once per finished turn and would miss the extra calls inside a turn and calls in interrupted turns.

Column Holds
turn_id, profile_id Which turn and profile made the call
provider, model Who answered
input_tokens, output_tokens Token counts
cache_read_tokens, cache_write_tokens Prompt-cache token counts
cost_usd The cost, from pricing.toml
cost_status priced, or unpriced when the model has no price
tags The turn’s tags
created_at When

Child turns carry their parent’s tags, so work done by delegated helpers is billed to the same client.

Prices

Prices change, so none ship with CommonOS. Put them in ~/.commonos/pricing.toml, in US dollars per million tokens:

~/.commonos/pricing.toml
[models."<model id>"]
input_per_mtok = 0.0
output_per_mtok = 0.0
cache_read_per_mtok = 0.0
cache_write_per_mtok = 0.0

cache_read_per_mtok and cache_write_per_mtok default to 0. The file is read at start-up.

A model with no price is still recorded, with its tokens, cost_usd = 0 and cost_status = 'unpriced'. Add the price later and you can compute the cost from the stored tokens.

The mock model mock-1 has a built-in price (1.00 input, 2.00 output, 0.10 cache read and 1.25 cache write per million tokens), so reports work without a key.

Reports

http
GET /api/costs?group_by=tag&tag=client:acme&from=2026-10-01T00:00:00Z
Parameter Does
group_by total (default), tag, profile, model or day
tag Only calls with this tag
profile Only this profile’s calls
model Only this model’s calls
from, to Time range, RFC 3339
Response
{
  "group_by": "model",
  "rows": [
    {
      "group": "mock-1",
      "calls": 6,
      "input_tokens": 600,
      "output_tokens": 120,
      "cache_read_tokens": 0,
      "cache_write_tokens": 0,
      "cost_usd": 0.00084,
      "unpriced_calls": 0
    }
  ]
}

Examples

What did Acme cost in October?

Terminal
curl -s 'http://127.0.0.1:9120/api/costs?tag=client:acme&from=2026-10-01T00:00:00Z&to=2026-11-01T00:00:00Z'

Cost per day for one profile:

Terminal
curl -s 'http://127.0.0.1:9120/api/costs?group_by=day&profile=Household'

Which models had no price?

Terminal
curl -s 'http://127.0.0.1:9120/api/costs?group_by=model' | jq '.rows[] | select(.unpriced_calls > 0)'

The dashboard’s Costs panel shows the same report, grouped by tag, profile, model or day.

Edit this page on GitHub

Esc