Catalog, cost models and overrides
This guide covers the first half of pricing from cost: where provider prices come from, how a cost model records what you pay, and how your own rates win over the catalog.
Every amount in the examples is illustrative, and so are the providers (Vidlab, Textco) and the catalog entries shown: none is a real provider price.
Before you start
- You know the parts of pricing from cost and who controls each variable.
- You have a sandbox key with
config:readto browse the catalog andconfig:writeto create cost models.
The provider price catalog
The catalog is a list of provider models and their prices that Runledger maintains. It is read-only: you can’t add to it or change it. It is the same in the sandbox and in live. Reading it needs config:read.
A catalog model declares what it is metered in, with who controls each unit and dimension, as a cost model does. This is an invented entry:
Amounts are decimal strings in minor units of the price’s currency, as everywhere else in the API: "12" is 12 US cents for each second of 1080p video. The units, dimensions, per and tiers work as described below.
What every entry carries. A source (source_url and source_reference), the date it takes effect (effective_from), the date Runledger read it (as_of), and when Runledger last checked it against its source (last_verified_at). A published price version never changes. A price change, or a correction, is a new version with its own effective_from, never earlier than the day it is published, and a changes list of what differs from the version before. A model the provider has retired becomes deprecated: cost models that use it keep working with the last version, and no new versions are published.
Freshness target. Runledger commits to these targets for the providers and models the catalog covers:
A business day is Monday to Friday in Sydney time (Australia/Sydney). There is no public holiday calendar, so a public holiday counts as a business day. The clock starts when the provider publishes the change or the model, not when Runledger notices it, and published_at on a catalog price shows when Runledger published the version.
These are freshness targets, a commitment about how quickly the catalog follows providers. They are not a guarantee that a price is correct, and there is no service-credit SLA: missing a target gives no automatic credit or refund. Three things stay true however fresh the catalog is:
- Your overrides always take precedence. An override beats the catalog price on every line it matches, so a negotiated rate is never replaced by a catalog update.
- Recorded usage is never repriced. A new version applies from its
effective_fromonward, and a correction takes effect from the date it is published. Costs and events already recorded keep the version that priced them. A rate card you add to a custom cost model must start after every earlier version and can’t start in the past. - The invoice and the contract are what you pay. The catalog says what the source said on the
as_ofdate. Where your bill differs, fix your numbers with an override.
What it covers. A stated set of providers and models, not every provider. GET /v1/catalog/providers lists the providers covered and how many models each has, and GET /v1/catalog/models lists the models. Those lists are the statement of coverage: the targets above apply to what is listed, and a provider or model that isn’t listed isn’t covered and needs a custom cost model. Coverage includes units other than tokens (images, video seconds, audio seconds, GPU or session time) where providers publish a price. When trace() names a catalog model you haven’t set up, the call is priced from the catalog’s list price (priced_by: price_table), without overrides, and carries the warning catalog_model_available, which names the model to adopt so your own rates apply.
Cost models and rate cards
A cost model names a provider and model the way trace() does, declares its units and dimensions, and carries rate cards. It gets all of that in one of two ways, shown by its source:
catalog. You sendcatalog_model, and Runledger takes the provider, model, kind, units, dimensions and rates from the catalog. The model follows the catalog’s price versions: each one becomes a rate card of the cost model, with the same rates andeffective_from.custom. You declare the model and enter the rate cards yourself, for a model the catalog doesn’t have or rates the catalog can’t know.
That is the whole setup for a catalog model. The response has source: "catalog", the provider and model, and the active rate card. A custom cost model looks like the catalog entry above with a rate_card of its own, here for a self-hosted model that is billed by GPU second:
"0.35" is 0.35 of a cent for each GPU second. A custom cost model’s rate cards are yours to add (POST /v1/rate_cards). A catalog cost model refuses them with 409: its rates come from the catalog, and your changes go in overrides.
Units. A unit is a plural name for what you count. The names to use are input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, reasoning_tokens, images, video_seconds, audio_seconds, gpu_seconds, session_hours, requests and searches. Any other plural works, such as tool_calls.
Choosing a rate. A usage line has a unit, a quantity and dimension values. Runledger picks the rate for that unit whose dimensions all match the line. A rate that leaves a dimension out matches any value, and the most specific match wins. A line with no matching rate is recorded without a cost and with the warning rate_not_found, so a gap in the catalog shows up instead of a zero.
Per and tiers. Providers quote token prices per million, so a rate has a per: "per": "1000000" with "unit_amount_decimal": "300" is 300 cents per million tokens. A rate can have tiers instead of one amount. In graduated mode each band’s price covers the quantity inside it. In volume mode the band the whole quantity falls in prices all of it, which fits a provider that charges more for every token of a long prompt:
A tier applies to one call’s quantity. Tiers across a customer’s usage over a month aren’t supported.
Your own rates override the catalog
Some customers, especially larger ones, don’t pay list price. An override is your own rate for a catalog cost model, and it always wins over the catalog:
discount_percenttakes that share off the catalog rate of every matching line, tiers included."0.2"is 20% off.unit_amount_decimalreplaces the catalog rate for a unit, so it covers a private or provisioned deployment, a committed-spend rate you already know, and a unit the catalog model doesn’t list, which is how you extend a catalog model.unit,dimensions,effective_fromandeffective_tosay where and when it applies. With none of them it applies to every line of the model from the moment it is saved.categoryis a label for reporting, andreasongoes in the audit log.
Precedence. For each usage line, at the line’s own timestamp:
- An override in force that matches the line’s unit and dimensions. If several match, the most specific wins: one that names a unit and dimensions beats one that names only a unit, which beats one that names neither. Two equally specific overrides whose dates overlap are a validation error, refused with
400andconfig_invalid, so there is never a tie to guess at. - Otherwise the catalog price version in force.
- Otherwise no rate: the line is recorded without a cost and with
rate_not_found.
A custom cost model has no catalog layer: its own rate card in force is used, and it takes no overrides.
The cost on a line says which one priced it, so a cost can be traced to the rate behind it (see cost attribution). Say the catalog lists Textco Large at 300 cents per million input tokens (up to 200,000 tokens) and 1,500 per million output tokens. A call with 1,840 input tokens and 512 output tokens costs 0.552 + 0.768 = 1.32 at list price, and 1.32 × 0.8 = 1.056 with the first override above.
Overrides record the rate you pay. They don’t track spend against a commitment, so a rate that changes when you pass a spend threshold is entered as the new rate from the date you pass it. Overrides change only costs from their effective_from: nothing already recorded is repriced.
Next steps
- Price rules and margin guardrails turn a cost model into a price and a margin floor.
- Cost attribution and margin records what each call costs, so the margin is real.
- Provider price changes and simulation covers what happens when a catalog price moves.