Pricing
burn prices a request when it finishes and stores the result on the row. The price list is public, cached locally, and never guessed.
Where prices come from
Prices come from the CloudPrice LiteLLM-compatible model map:
https://ai.cloudprice.net/api/v1/litellm_model_prices.json?mode=chatThe download is free and needs no key. burn keeps the mode: "chat" entries and converts per-token costs to USD per 1 million tokens. The slimmed copy is written to ~/.burn/prices.json with a fetchedAt timestamp and the source URL.
Thanks to CloudPrice for publishing the list. The data stays on their servers; burn stores only the slimmed model map it needs for matching.
BURN_PRICE_URL points the fetch somewhere else, which is useful for a mirror or a test.
The 24 hour cache
- If
prices.jsonexists and is less than 24 hours old, burn uses it. - If it is missing or older than 24 hours, the proxy starts a background refresh. The request in flight is not delayed.
- If the download fails, the cached copy stays in use. A failed refresh does not empty the price book.
- A first run with no cache uses the bundled fallback list while the refresh runs.
The bundled fallback is src/prices.fallback.json, a small hand-maintained snapshot of about 30 popular OpenAI, Anthropic, Google, and DeepSeek models. It is transcribed from each provider's published pricing page so a first run or an offline machine still prices common traffic. It is not the full CloudPrice dataset.
Live prices replace the fallback within 24 hours of the first run, unless BURN_OFFLINE is set.
Matching a request to a price
burn only considers entries that belong to the provider that handled the request. It tries these in order:
- The exact key
<provider>/<model>. This is how OpenRouter entries appear, for exampleopenrouter/anthropic/claude-sonnet-4. - The exact model name.
- The model name with a date or version suffix removed:
-20250929,-2024-10-22,-10-22-2024,-v2,-001,-latest, and:free,:batch,:nitro,:floor,:extended,:online. - The longest prefix match, where the next character in the model name is a separator such as
-,_,.,:,@,/, or a space.
A direct OpenAI or Anthropic request is never priced from bedrock/, vertex_ai/, azure/, together_ai/, or similar keys, because those hosts charge their own rates. burn matches on the key prefix, not on the litellm_provider field, which is mislabeled on some entries upstream.
Cache and reasoning tokens
The price list can carry separate rates for cache reads, cache writes, and reasoning tokens. burn uses them when present.
| Token type | If the price list has a rate | Fallback |
|---|---|---|
| Cache read, OpenAI style | cache_read_input_token_cost | 0.5 × input rate |
| Cache read, Claude style | cache_read_input_token_cost | 0.1 × input rate |
| Cache write, Claude style | cache_creation_input_token_cost | 1.25 × input rate |
| Cache write, OpenAI style | cache_creation_input_token_cost | 1 × input rate |
| Reasoning tokens | output_cost_per_reasoning_token | Priced at the output rate, or 0 if no reasoning rate exists |
OpenAI reports cached tokens inside the prompt token total, so burn subtracts them from input before pricing. Anthropic reports input, cache reads, and cache writes separately.
If a response reports reasoning tokens but the price list has no reasoning rate, the reasoning count is not charged twice. burn clamps it to the output token count and prices the normal output at the output rate.
Overrides
An override always wins over a fetched price. Set one with:
burn price set gpt-4o 2.5 10The two numbers are USD per 1 million tokens: input first, output second. The override is stored in config.json and applies to future requests. It can also be written directly:
{
"prices": {
"my-model": { "in": 0.5, "out": 1.5 },
"openai/my-model": { "in": 0.5, "out": 1.5 }
}
}burn checks the exact model key and then the <provider>/<model> key in config.json. An override has no cache-read, cache-write, or reasoning fields, so those tokens use the normal fallback multipliers based on the input and output rates.
Remove an override with:
burn price unset gpt-4oUnpriced models
If no key matches, the request is stored with cost = NULL. burn does not estimate a price from another model. Reports count the row as unpriced and print a notice:
3 requests have no price. Run "burn price update" to fetch prices,
then "burn price recalc" to fill them in.There are three ways to fix it:
burn price updatethenburn price recalc, after the model appears in the public list.burn price set <model> <in> <out>for a model that will never be in the list, such as a private endpoint.- Leave it unpriced and use the token counts. The row is still useful.
recalc
burn price update
burn price recalcburn price update fetches a fresh list. burn price recalc selects every row whose cost is NULL, prices it with the current book, and fills in the ones it can. It never rewrites a row that already has a cost, so changing a price later does not rewrite history.
A row that still has no match after recalc stays unpriced. Recalc is safe to run repeatedly.
Offline use
Set BURN_OFFLINE=1 or BURN_OFFLINE=true to stop burn from fetching prices:
BURN_OFFLINE=1 burn today
BURN_OFFLINE=1 burn proxyIn offline mode, burn uses prices.json if it exists, otherwise the bundled fallback. burn price update refuses to run and says offline mode is on. Proxying, logging, reporting, the dashboard, and burn add all still work.
Accuracy
Prices are best effort, come from a third party, and change often. Providers bill with their own rules: batch discounts, long-context tiers, priority tiers, free tiers, region, and negotiated rates. burn's numbers are an estimate for watching trends and catching surprises, not an invoice.
Verify anything that matters against the provider's own usage page.