Model pricing
How the gateway prices each request, how to add prices for models the catalog does not know, and why unpriced models are refused.
The gateway records provider attempts and their measured cost in USD. Before forwarding, it requires either a local model price or a route that reports its own cost, such as xAI or OpenRouter. A successful response without readable usage is marked unresolved, so recorded spend can differ from your provider's invoice. This page covers local prices, reported cost and unresolved usage.
The catalog
The gateway ships with a price catalog: input and output prices per million tokens for the common models of each provider type, plus cached-input, cache-write and long-context rates where the provider has them, per-minute or per-hour rates for transcription models, and per-image rates for a few image models. Prices are market data about models rather than something you configure, so the catalog ships with the gateway. The hosted service updates it with gateway releases. Self-hosted gateways receive catalog updates when you update your deployment. Use the provider's Pricing action to cover a new or changed model without waiting for a release.
The catalog is keyed by provider type and the provider's own model ID:
gpt-5.6-sol under openai, claude-opus-5 under anthropic,
gemini-2.5-flash under gemini, grok-4.5 under xai. A model priced in
the catalog works on every provider of that type, direct or routed through a
gateway, because model IDs stay canonical
on every route.
Every catalog entry also says what the model does: text, image, speech,
transcription, embedding or realtime (a model served only over a
realtime connection). A custom endpoint
offers and accepts only models of its own kind. A model you price yourself
with the Pricing action counts as a text model; a catalog model you
re-price keeps its kind.
Two provider types ship with no catalog at all, because no static list could be right for them: Fireworks AI names models per account, and Hugging Face re-prices the same model ID on every upstream its router might pick. Their models proxy once you price them, and not before.
Prompt cache writes
Anthropic charges more for a cache write kept for an hour than for one kept
the default five minutes: 2x the input price against 1.25x. Its responses say
how many written tokens were kept for each, and the gateway bills each share
at its own rate (cache_write_1h and cache_write in the catalog). If a
response gives no split, every written token bills at the five-minute rate.
Image generation models
The catalog covers image models that are priced per token, with separate rates for image tokens where the provider charges them differently from text:
- OpenAI GPT Image models, such as
gpt-image-2andgpt-image-2.5-sunburst. Prompt text and input images (in an edit) each bill at their own input rate, and generated images at the image output rate. - Gemini image models, such as
gemini-3-pro-imageandgemini-nano-banana-2.1. Generated image tokens bill at the image output rate, and text and thinking at the text rate.
Both providers report how many of a request's tokens were images, and the gateway bills each kind at its own rate. If a response does not break its tokens down, all of them bill at the model's highest rate, so an image is never billed at the cheaper text rate. The same applies when part of a prompt was served from cache: the response does not say whether the cached tokens were text or images, so the uncached tokens are billed as images first. The recorded cost can then be above what the provider charged, but never below.
A model billed a flat price per image is in the catalog only when its
response says how many images it billed. Today that is BytePlus Seedream:
dola-seedream-5-0-flash-260915, seedream-5-0-lite-260128,
seedream-4-5-251128 and seedream-4-0-250828. Each one bills its price times
the images the response counts in usage.generated_images, which leaves out
images that failed or were withheld by moderation, as BytePlus's own bill does.
If a response does not count its images, the event is marked unresolved
rather than recorded at $0.
Other image models are not in the catalog when their price depends on more than a count of images, such as size, quality, steps or a provider's estimate. That covers Together AI's image models, Seedream 5.0 pro (two pixel tiers, a charge for input images and per-layer tiers) and Gemini 2.5 Flash Image. Custom prices are per token, so they cannot bill these models either, and the models stay unsupported. The exception is a provider that reports its own cost: xAI's image models proxy with no price (see Providers that report their own cost).
Treat recorded cost as usage accounting
Catalog prices estimate the provider cost from the usage it reports. Discounts, serving tiers, tools and other provider-specific charges may not be represented by a model's token rates. Check your provider's current pricing and invoice when exact charges matter, and use custom prices where appropriate.
GET /v1/admin/prices shows the built-in catalog this gateway uses; the
provider's Pricing dialog shows your custom overrides. Model names alone do not tell you their relative price, and a
catalog entry does not guarantee that your provider account can still use it.
Custom prices
A model the catalog does not know, or knows at a stale price, is unblocked by adding it to the provider's custom pricing. Custom prices belong to one provider: a model priced only there is usable through that provider alone, which is why the model pickers in an app's proxy policy are per slug.
Open Providers, open the row's actions menu, and choose Pricing. The dialog is titled Custom model pricing and says: "For models the built-in catalog does not cover, or prices it in a way you disagree with. Requests for unpriced models are rejected until a price is set here. Enter $0 for a model that is genuinely free."
Click Add model for each row and fill in its columns:
| Column | Meaning |
|---|---|
| Model | The model ID exactly as your app sends it, for example gpt-brand-new. |
| Input $/1M | Price per million input tokens. |
| Output $/1M | Price per million output tokens. |
| Cached $/1M | Optional. Price per million input tokens served from the provider's prompt cache. |
| Cache write $/1M | Optional. Price per million input tokens written to the provider's prompt cache. |
Then Save pricing. Every row needs a model name and the input and output prices, as numbers of zero or more. A cache price left blank bills at the input price. A model cannot appear twice. A row left entirely blank is dropped, so a spare one does no harm. Custom prices are ordinary data, not secrets, so the dialog shows the stored values and anyone who can read the console can see them.
Lookup has two levels: the provider's own custom price for the requested model first, then the shipped catalog. A custom price replaces the catalog's rates for that model on that provider only. It does not inherit any of them: a rate you leave out bills as it would on a catalog entry without it (a cache rate at the input price, a long-context rate at the normal one), never at the catalog's figure. If the provider gives a cache discount, enter it, or cached traffic records at the full input price. If you priced a model yourself before the catalog covered it, such as a GPT Image model, delete that custom price so the catalog's full rates apply.
The API equivalent sets pricing on the provider. It is a full replace, keyed
by model ID, and null clears every custom price:
PUT /v1/admin/providers/{id}
{ "pricing": { "gpt-brand-new": { "input": 1.25, "output": 10, "cached_input": 0.125 } }, "revision": 7 }Through the API, a custom price accepts every per-token rate the catalog
uses, under the same names as in GET /v1/admin/prices: cached_input,
cache_write, cache_write_1h, the image, audio and video rates, and the
long_* rates. The long_* rates also need long_context_threshold, the
prompt size in tokens above which they apply. The dialog shows the cache
rates and keeps the others as they are when you save.
The same field is accepted on POST /v1/admin/providers when you create the
provider.
Free models
$0 is a valid price, and it is the right one for a model you host yourself
and pay no per-token price for. Enter it deliberately: a request priced at
zero counts toward nothing in any spending limit, so a
free model still records requests and tokens, but contributes no spend.
Request limits still apply. The dialog refuses an empty price field
for that reason; zero has to be a typed answer.
Unpriced models are refused
A request for a model that neither the provider's custom pricing nor the catalog knows is refused before it reaches the provider:
{
"error": {
"code": "pricing_not_configured",
"message": "..."
}
}The status is 400, and the message names where to fix it. Entering the price
is what allows the model on a locally priced route. This applies to
custom base URLs too: a model served from your own endpoint is almost
certainly not in the catalog and needs a price on that provider. See
Custom base URLs.
Providers that report their own cost
Two provider types report what they charged instead of relying on a local price:
- xAI, on its Chat Completions, Responses and image generation answers.
- OpenRouter, on its chat-completions answers, the only API it serves here.
The gateway records that figure, so on those APIs their models proxy with no local price; the rule is "priced locally or reported upstream". A report counts only where the answer reaches the gateway as the provider wrote it: directly, or through Cloudflare AI Gateway. Through Vercel, an xAI model bills on its local price like any other.
The Pricing dialog says which of a provider's APIs need a price. For an OpenRouter provider: "OpenRouter reports the cost of every request, so its models proxy with no price here; a price entered here is only used if a response ever comes back without one." For an xAI provider, it names the APIs xAI reports on and says that its other APIs still need a price. A custom price on such a provider is honoured as the fallback for a response that arrives without a cost.
Where a cost comes from
Every usage event records how its cost was arrived at, in cost_source:
cost_source | Meaning |
|---|---|
reported | The provider said what the request cost, and that figure is the recorded one. xAI and OpenRouter report one. |
computed | Priced locally, from the provider's custom pricing or the shipped catalog. |
unresolved | Neither source could answer on a successful response. |
Reported cost wins because it is what you were charged. Local pricing is the
default and the fallback: if a cost-reporting route ever answers without a
cost, a local price for that model is used and the event is computed.
An event is unresolved when the provider answered successfully but the
gateway could not measure the spend. That happens when a provider reports
usage in a shape the gateway does not know, when a cost-reporting route answers
without a cost and the model has no local price, when a price was removed
while a request was already in flight, or when a client aborts a streamed
response before the final chunk that carries the usage. Such an event is shown
as unresolved rather than $0.00 in the app's event list and can be
counted with the By cost source breakdown. Its cost is zero because nothing
was measurable, not because nothing was spent. See
Usage for the breakdowns and what a rising unresolved
count means.
Deleting a provider deletes its custom prices
Custom prices live on the provider. Update key keeps them; Delete provider discards them along with the key. Usage events outlive the provider, so past traffic whose serving provider was deleted can only be repriced if the built-in catalog covers it. Recreating the same slug creates a new provider; its custom prices do not reconnect to the deleted provider's events.
Repricing stored usage
If a price was wrong for a while, POST /v1/admin/apps/{app}/usage/reprice
recomputes the stored cost for one provider type, model and month. Each event
is priced through the provider that served it, falling back to the catalog, so
two providers of one type can reprice the same model differently. Run it with
"apply": false first: the preview reports matched_events (what would be
rewritten), unpriced_events (events whose serving provider was deleted and
which no catalog price covers) and unmetered_events (events with no readable
usage, which are rewritten to zero but keep their unresolved marker). With
"apply": true the call is strict: if any matched event cannot be priced, it
answers 400 invalid_request and writes nothing, so a month is never left
half-repriced. Events with a reported cost are never touched, since a local
price would replace a fact with an estimate. A repriced event's cost_source
becomes computed when readable usage was available.
Repricing covers retained raw events only, with at most 10,000 matching events per call, and requires token pricing. It does not reprice compacted history, time-based transcription charges or per-image charges. See the API reference for the request shape.