AppAIGatewayDocs
Providers

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.

Every request that reaches a provider is recorded with a cost in USD. The gateway needs a price for the model before it will forward the request, so a model it cannot price is refused rather than served for free. This page covers where prices come from and how to add your own.

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, and per-minute or per-hour rates for transcription models. Prices are market data about models rather than something you configure, so the catalog ships with the gateway and is updated with it.

The catalog is keyed by provider type and the provider's own model ID: gpt-5.6 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.

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.

Where the catalog errs high

Where a provider's real price depends on something the gateway cannot see, the shipped entry is the higher rate, so the recorded cost is never lower than what you pay:

  • DeepSeek charges half price outside its peak hours. The catalog carries the peak rate, so off-peak traffic records as costing more than it did.
  • Batch and discounted serving tiers, offered by ByteDance, Moonshot and others, are priced per request rather than per model. The catalog carries the standard rate.

Override the entry with a custom price if your traffic sits mostly on the cheaper side. Erring high keeps a spending limit a limit: a cost recorded too low would let traffic through that the limit was meant to stop.

One catalog entry surprises people. Mistral Medium costs more than Mistral Large. Mistral's size names describe model generations, not a price ladder: mistral-medium-latest resolves to the current frontier model at $1.50 in and $7.50 out per million tokens, while mistral-large-latest resolves to the older Mistral Large 3 at $0.50 and $1.50. mistral-small-latest is $0.15 and $0.60. Pick by the model card, not by the name.

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 three columns:

ColumnMeaning
ModelThe model ID exactly as your app sends it, for example gpt-brand-new.
Input $/1MPrice per million input tokens.
Output $/1MPrice per million output tokens.

Then Save pricing. Every row needs a model name and both prices, as numbers of zero or more. 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 is a two-level merge: the provider's own custom price for the requested model first, then the shipped catalog. A custom price therefore overrides the catalog for that provider only. Custom prices carry input and output rates only; a catalog entry's cached-input or long-context rates do not apply to a model you priced yourself.

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 } } }

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 is also an unmetered one. 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. Nothing unpriced ever proxies, no usage goes unaccounted, and the cost on a usage event is never empty. 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

One route reports what it charged instead of relying on a local price: OpenRouter. Every OpenRouter chat-completions response carries the cost of that request, and the gateway records that figure. So OpenRouter models proxy with no local price; the rule is "priced locally or reported upstream".

The Pricing dialog says so 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." 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_sourceMeaning
reportedThe provider said what the request cost, and that figure is the recorded one. Only OpenRouter reports one today.
computedPriced locally, from the provider's custom pricing or the shipped catalog.
unresolvedNeither 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 priced only by a deleted custom price can no longer be repriced until the price is entered again somewhere.

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. See the API reference for the request shape.

On this page