
Cloudflare Unified Billing: the 5% is on the credits, and the 402 is one gateway toggle
Cloudflare Unified Billing is a way of paying for model inference in which Cloudflare, not you, holds the credentials for OpenAI, Anthropic, Google AI Studio, Google Vertex AI, xAI and Groq. You load dollar credits onto your Cloudflare account, send an ordinary HTTPS request to api.cloudflare.com carrying one Cloudflare API token, name a model as provider/model, and Cloudflare authenticates to the upstream provider, pays them, and deducts the cost from your credit balance. No OPENAI_API_KEY or ANTHROPIC_API_KEY exists anywhere in the request path. Cloudflare's words: "Both deduct credits from your account automatically without requiring provider API keys."
Evidence status
Observed marks first-party measurements or runtime receipts from the named environment. Derived marks arithmetic calculated from cited inputs. Specified marks vendor or standards documentation. Implemented and deployed name code and live-state evidence, respectively. Reproduced means the stated procedure was rerun. Externally attested marks operator reports; those reports show that an experience occurred, not that it is universal.
Terms this page uses
| Term | Meaning |
|---|---|
| Unified Billing | Cloudflare authenticates and pays the provider; you pay Cloudflare. Appears as wholesale: true in the gateway API and log rows. |
| BYOK | Bring Your Own Keys — your provider key is stored in Cloudflare Secrets Store and forwarded; the provider bills you. |
| AI Gateway | The proxy in front of the model call: logging, caching, retries, rate limits, spend limits. Free on all plans. Setup: /a/cloudflare-ai-gateway-setup. |
| Authenticated gateway | A gateway with its authentication setting on, which then demands a Cloudflare API token on every request. |
| Credits | Prepaid dollars on the Cloudflare account. Bought at a 5% surcharge; spent at the provider's own per-token rate. |
| Neurons | The unit Workers AI bills in, $0.011 per 1,000. A separate ledger from credits. |
The 5% lands on the money, not on the traffic
Cloudflare states it in one sentence: "A 5% fee is applied to all credits purchased through Unified Billing. For example, a $100 credit purchase will result in a $105 charge. Inference pricing from providers is passed through with no markup — you pay the same per-token rates as you would directly with the provider."
That is a surcharge at top-up, not a per-request markup. Nothing you change inside a request moves it, because every dollar you eventually spend on tokens cost $1.05 to acquire.
Two public statements about that number disagree; both are printed here rather than reconciled away. On Hacker News, yencabulator corrected a commenter who had called Cloudflare's gateway the free option: "Free? They take the same 5% fee as OpenRouter does." Right about Cloudflare, imprecise about OpenRouter. OpenRouter's FAQ uses the same shape — it "charges a fee when you purchase credits" and passes provider pricing through "without any markup" — but the rate rendered on that page is 5.5% with a $0.80 minimum for cards, 5% for crypto. On $100 that is $105.00 against $105.50; on $20, $21.00 against $21.10. Same mechanism, different price, and "the same 5%" is close rather than exact.
The number was hard to find at all, which is why it was disputed. bm-rf, reading the launch docs: "Not seeing any pricing info on the models[1] page. Wonder how much of a lift this is over paying providers directly. Perhaps Cloudflare is doing this at cost? Also interesting that zero data retention is not on by default". 6thbit, same thread: "i wonder about their princing and potential markup on top of token usage?i presume they wont let you \"manage all your AI spend in one place\" for free." Both were guessing. The 5% is on the Unified Billing feature page, not on the model catalogue they were reading.
The arithmetic for 100 million input and 20 million output tokens a month
Assumptions: one calendar month, 100,000,000 input tokens, 20,000,000 output tokens, no cached input, no batch discount, one authenticated gateway, list prices as of 2026-07-26.
openai/gpt-4.1-mini is $0.40 per million input and $1.60 per million output. Input 100 × $0.40 = $40.00. Output 20 × $1.60 = $32.00. Inference $72.00. BYOK pays OpenAI $72.00 and Cloudflare nothing, because AI Gateway's core features are free. Unified Billing deducts $72.00 of credits, and acquiring $72.00 of credits costs $72.00 × 1.05 = $75.60.
anthropic/claude-sonnet-5 is $2 / $10 per million today — Anthropic footnotes it as "Introductory pricing of $2 / $10 per MTok applies to Claude Sonnet 5 through August 31, 2026" — and "$3 / input MTok $15 / output MTok" after that.
| Route | Inference | Cloudflare fee | You pay |
|---|---|---|---|
openai/gpt-4.1-mini, BYOK | $72.00 | $0.00 | $72.00 |
openai/gpt-4.1-mini, Unified Billing | $72.00 | $3.60 | $75.60 |
anthropic/claude-sonnet-5 intro, BYOK | $400.00 | $0.00 | $400.00 |
anthropic/claude-sonnet-5 intro, Unified Billing | $400.00 | $20.00 | $420.00 |
anthropic/claude-sonnet-5 after 2026-08-31, Unified Billing | $600.00 | $30.00 | $630.00 |
Sonnet rows: 100 × $2 = $200 plus 20 × $10 = $200 gives $400; 100 × $3 = $300 plus 20 × $15 = $300 gives $600. The fee column is the inference column × 0.05 every time. That is the whole pricing model.
A Workers AI model has no row here. Cloudflare: "Workers AI models (models prefixed with @cf/) routed through AI Gateway are not charged via Unified Billing. These models are billed through Workers AI pricing instead" — "$0.011 per 1,000 Neurons". A mixed setup produces two line items by design. Which of those models are worth pointing a coding agent at: /a/workers-ai-coding-models.
Five things that must be true before a request bills
- Credits. Dashboard → AI Gateway → the Credits Available card, top right → Manage → Top-up credits → amount → Confirm and pay. A payment method is required first. Optional: Setup auto top-up credits, with a threshold and a recharge amount.
- A gateway with authentication on. That gateway's Settings → Create authentication token (a token with the required
Runpermissions, shown once) → back on the settings page, toggle Authenticated Gateway on. Skipping this is the 402 below. - An API token. My Profile → API Tokens → Create Token → Create Custom Token, with the account-scoped permissions AI Gateway – Run (send inference) and AI Gateway – Read (list gateways, read logs). Add AI Gateway – Edit only to change the gateway's zero-data-retention default over the API. Cloudflare's warning: "The
AI Gateway Read,Run, andEditpermissions cannot be restricted to a single gateway — unlike R2, which supports per-bucket scoping. Any token withAI Gateway Runcan send requests through every gateway in the account." - The account id. The 32-character hex string after
dash.cloudflare.com/in the dashboard URL, or fromwrangler whoami. It is the{account_id}path segment below. - A
provider/modelid.openai/gpt-4.1-mini,anthropic/claude-sonnet-5,google/gemini-3-flash,xai/grok-3.
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai/v1/chat/completions" \
-H "Authorization: Bearer <TOKEN>" \
-H "cf-aig-gateway-id: default" \
-H "Content-Type: application/json" \
--data '{"model":"openai/gpt-4.1-mini","max_tokens":16,
"messages":[{"role":"user","content":"Reply with the single word: ok"}]}'Omitting cf-aig-gateway-id routes third-party requests through the account's default gateway. Workers AI @cf/ requests always require the header.
The 402 has one cause, and the docs table predicts the opposite
{"errors":[{"message":"Gateway authentication is required to use unified billing. Enable authentication on your gateway or provide your own API key (BYOK).","code":2021}],"success":false,"result":{},"messages":[]}One condition produces it: a provider/model request aimed at a gateway whose authentication is false. A valid token does not help. A loaded balance does not help. Below, two requests seconds apart on an account with cloud-kernel (authentication: false) and default (authentication: true) — same token, same body, same model, one header value different.
# Change only the gateway id between these two runs.
for GW in cloud-kernel default; do
curl -s -w " <- %{http_code} on $GW\n" -X POST \
"https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai/v1/chat/completions" \
-H "Authorization: Bearer <TOKEN>" -H "cf-aig-gateway-id: $GW" \
-H "Content-Type: application/json" \
--data '{"model":"openai/gpt-4.1-mini","max_tokens":16,"messages":[{"role":"user","content":"Reply with the single word: ok"}]}'
donecloud-kernel returned 402 with the body above. default returned 200: {"id":"id-1785040573332","object":"chat.completion","model":"openai/gpt-4.1-mini","choices":[{"index":0,"message":{"role":"assistant","content":"ok"}}],"usage":{"prompt_tokens":14,"completion_tokens":1,"total_tokens":15}}.
Gateway authentication | Request | Result |
|---|---|---|
false | provider/model | 402, code 2021 |
false | @cf/vendor/model | 200 — Workers AI is not Unified Billing |
true, token present | provider/model | 200 |
true, token absent | anything | 401 — an auth failure, not a billing failure |
| either, BYOK key stored | provider/model | 200, provider bills you. BYOK's own prerequisite is still an authenticated gateway. |
Two things make this expensive. The same @cf/ call keeps working on the unauthenticated gateway, so it reads as a model problem. And the Authenticated Gateway page's own behaviour table says the opposite: for Authentication Setting: Off with No header it states "Unauthenticated gateway — Request succeeds". True for Workers AI and for BYOK; not true for Unified Billing, and nothing on that page says so.
pemontto filed the narrower version on Cloudflare's own repository, cloudflare/ai issue 548, 2026-05-30: "POST to /ai/v1/responses with Unified Billing auth (Authorization: Bearer {CF_API_TOKEN} plus cf-aig-gateway-id: {authenticated_gateway_id}) returns HTTP 402 even though the gateway has authentication: true." The report carries a full curl reproduction, confirms authentication: true by reading GET /ai-gateway/gateways/{GW}, and shows /ai/v1/chat/completions returning 200 on identical headers across openai/gpt-5.4, gpt-5.4-mini and gpt-5.5. Status on 2026-07-26: open, zero comments. The same shape tried here — POST /ai/v1/responses, openai/gpt-4.1-mini, authenticated gateway — returned 200 with a complete Responses envelope. The endpoint-specific 402 did not reproduce on this account and model. Both results stand; nobody has closed the issue.
anthropic/* gets Anthropic's validation rules, whatever the endpoint is called
/ai/v1/chat/completions is an OpenAI-shaped endpoint. The shape is a translation layer, not a guarantee. When the id starts with anthropic/, the request lands on Anthropic's backend under Anthropic's schema — which has no system role inside messages, only a top-level system field (/a/what-is-the-anthropic-messages-api).
pmonte, anomalyco/opencode issue 32951, 2026-06-19: "Cloudflare AI Gateway Unified Billing routes requests to Anthropic's backend. When the model is anthropic/*, the gateway applies Anthropic's validation rules, which do not accept role: \"system\" inside the messages array." Their client put the system prompt at messages[0]; every request failed.
Reproduced against anthropic/claude-sonnet-5 on an authenticated gateway. The body that fails, and its exact answer:
{"model":"anthropic/claude-sonnet-5","max_tokens":16,
"messages":[{"role":"system","content":"You are terse."},
{"role":"user","content":"Reply with the single word: ok"}]}HTTP 400
{"errors":[{"message":"Model execution failed (User Input Error): Invalid value at messages[0].role: Invalid option: expected one of \"user\"|\"assistant\"","code":7003}],"success":false,"result":{},"messages":[]}Two bodies work. Drop the system message and keep /ai/v1/chat/completions:
{"model":"anthropic/claude-sonnet-5","max_tokens":16,
"messages":[{"role":"user","content":"Reply with the single word: ok"}]}Or keep the system prompt and move to /ai/v1/messages, Anthropic's own shape:
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai/v1/messages" \
-H "Authorization: Bearer <TOKEN>" -H "cf-aig-gateway-id: default" \
-H "Content-Type: application/json" \
--data '{"model":"anthropic/claude-sonnet-5","max_tokens":16,"system":"You are terse.",
"messages":[{"role":"user","content":"Reply with the single word: ok"}]}'{"id":"msg_011CdQ3qJ4WRi3L5nnG3cdc3","type":"message","role":"assistant",
"content":[{"type":"text","text":"ok"}],"model":"claude-sonnet-5","stop_reason":"end_turn",
"usage":{"input_tokens":22,"output_tokens":4},"gatewayMetadata":{"keySource":"Unified"}}"keySource":"Unified" is the response naming the credential that paid — the cheapest live confirmation that a call billed through credits rather than a stored key.
Prefer the second. The first succeeds and still misleads: the measured 200 from anthropic/claude-sonnet-5 on /ai/v1/chat/completions was {"index":0,"message":{"role":"assistant","refusal":null},"logprobs":null,"finish_reason":"stop"} — no content field at all — with usage reported as {"input_tokens":16,"output_tokens":4} instead of OpenAI's prompt_tokens/completion_tokens. A client reading choices[0].message.content gets an empty answer and no error to explain it. This behaviour is not in the documentation.
The mismatch bites adapters from the other side too. just-be-dev, withastro/flue issue 327, 2026-06-20, stated the motive first: "have been leaning into cloudflare's AI gateway unified billing so that I don't have to provision keys for openai/anthropic directly. Ran into a bit of an issue where using anthropic's models in this way doesn't quite work." The adapter formatted every cloudflare/... request as OpenAI chat-completions — correct for cloudflare/@cf/..., wrong for cloudflare/anthropic/claude-sonnet-4.6.
Model ids and endpoints decide the ledger
| Id shape | Example | Billed as | cf-aig-gateway-id |
|---|---|---|---|
provider/model | openai/gpt-4.1-mini, anthropic/claude-sonnet-5, google/gemini-3-flash, xai/grok-3 | Unified Billing credits, 5% surcharge at top-up | optional; defaults to the account default gateway |
@cf/vendor/model | @cf/moonshotai/kimi-k2.6, @cf/meta/llama-3.3-70b-instruct-fp8-fast | Workers AI Neurons, $0.011 / 1,000 | required |
provider/model with a stored key | any of the above | the provider bills you; Cloudflare bills nothing | required, gateway must be authenticated |
Four endpoints accept Unified Billing traffic: POST /ai/run (Cloudflare envelope, {model, input}), POST /ai/v1/chat/completions (OpenAI chat completions), POST /ai/v1/responses (OpenAI Responses), POST /ai/v1/messages (Anthropic Messages). Only the last refuses Workers AI ids outright — Cloudflare: "Workers AI models (@cf/) do not support this schema." /ai/v1/responses accepts @cf/ ids model-by-model.
What is covered, and the four places the documentation stops
Providers on the HTTP API: OpenAI, Anthropic, Google AI Studio, Google Vertex AI, xAI, Groq. Zero data retention is a Unified-Billing-only control — "This setting only applies to Unified Billing requests that use Cloudflare-managed credentials. It does not apply to BYOK or other AI Gateway requests" — off by default, supported for OpenAI and Anthropic only, and where a provider lacks it "AI Gateway falls back to the standard (non-ZDR) Unified Billing configuration" without saying so at request time. Set it per gateway under Settings → Zero Data Retention (ZDR) or per request with cf-aig-zdr: true. It is not logging: "ZDR does not control AI Gateway logging." Spend limits cover both lanes — "Spend limits apply to both Unified Billing requests and BYOK requests for models with known pricing" — block with 429, and are "eventually consistent", so "a burst of concurrent requests can briefly exceed the limit before enforcement catches up."
Four silences:
- The 402. The Unified Billing page lists an authenticated gateway as a prerequisite and never names the status code or the error string; the Authenticated Gateway page's behaviour table contradicts it for this lane.
- The negative balance. In full: "In rare instances, your credit balance may go negative. If this happens, Cloudflare will charge the payment method on file for the outstanding amount. Charges occur at the beginning of each month for the previous month." No cap is documented.
- The
costfield's precision. Present on every log row, the basis of spend limits, rounding unspecified. - The
anthropic/*envelope on the OpenAI endpoint. Measured above, described nowhere.
What one billed request leaves behind
GET /accounts/<ACCOUNT_ID>/ai-gateway/gateways/default/logs returns the receipt for the calls made above.
model | tokens_in / tokens_out | cost | authentication | wholesale | byok |
|---|---|---|---|---|---|
anthropic/claude-sonnet-5 | 22 / 4 | 0.00008400000000000001 | true | true | null |
anthropic/claude-sonnet-5 | 16 / 4 | 0.000072 | true | true | null |
openai/gpt-4.1-mini | 14 / 2 | 0.0000088 | true | true | null |
wholesale: true marks the row as Unified Billing; byok: null confirms no stored key was used. Test the no-markup claim against those three numbers:
openai/gpt-4.1-miniat OpenAI's published $0.40 and $1.60 per million: (14 × 0.40 + 2 × 1.60) ÷ 1,000,000 = 8.8 ÷ 1,000,000 = $0.0000088. The logged value, to the digit.anthropic/claude-sonnet-5, 22 in and 4 out, at Anthropic's introductory $2 and $10: (44 + 40) ÷ 1,000,000 = $0.000084. The logged value. At the standard $3 / $15 the same row would be $0.000126, so the log is charging the introductory rate.- The 16-in row: (32 + 40) ÷ 1,000,000 = $0.000072. The logged value.
Two providers, three rows, exact agreement. No markup per request; the 5% is entirely at top-up. A fourth call, @cf/meta/llama-3.3-70b-instruct-fp8-fast through the unauthenticated gateway, returned 200 and reported "neurons":1.529652714729309 instead of a dollar cost — 1.5297 ÷ 1,000 × $0.011 = $0.0000168, in the other ledger.
Six routes, and the one to take
| Route | Take it when | Cost of taking it |
|---|---|---|
| Unified Billing | The number of provider accounts is the problem: one token, one invoice, one place to set spend limits, no provider key in the code | 5% on credits, and a hard dependency on Cloudflare for reachability |
| BYOK on the same gateway | Volume is high, or a provider contract or committed-spend discount already exists that the 5% would sit on top of | Key custody returns, one bill per provider; still needs an authenticated gateway |
| Direct provider keys, no gateway | A single-provider service where the gateway earns nothing | No unified logs, no cross-provider spend limit, no cache, per-provider rotation |
Workers AI @cf/ | The model you want is in Cloudflare's own catalogue and you want no third-party relationship | A much smaller catalogue than the frontier providers' |
| OpenRouter | Breadth of model coverage matters more than price | 5.5% + $0.80 on a card top-up, and another vendor relationship |
| Bedrock or Vertex | Procurement, not the model, is the constraint — spend must land on an AWS or Google contract | Vertex is already reachable through Unified Billing, so this is only distinct when the billing relationship is the requirement |
Two merged pull requests show what people actually adopt this for, and it is not the price. jeremyhart rewrote a connector to drop a confusing "Provider API Key" plus separate "Gateway Token" pair for a single Cloudflare API token with AI Gateway Run permission: "Cloudflare authenticates and bills the upstream provider, so no per-provider keys are needed." kyleboas made the mirror-image change on a callLLM that had been attaching an upstream Authorization header on every gateway request and injecting ANTHROPIC_API_KEY/OPENAI_API_KEY into agent inputs: "Cloudflare Unified Billing lets the gateway handle upstream authentication and billing, so provider API keys should be optional when routing through the AI Gateway."
Verdict: route through Unified Billing until inference spend is large enough that 5% costs more than running key rotation and reconciling several provider invoices. At $72 a month the fee is $3.60 and the decision is not close. At $50,000 a month it is $2,500, and BYOK on the same gateway removes the fee while keeping every gateway feature. The break-even is an operations question, and moving between the two is a stored secret plus a gateway setting, not a rewrite.
Symptom, cause, fix
| Symptom | Cause | Fix |
|---|---|---|
402 + Gateway authentication is required to use unified billing, code 2021 | The target gateway has authentication: false | Gateway Settings → Create authentication token → Authenticated Gateway on, or point cf-aig-gateway-id at a gateway that already is |
@cf/ models work, provider/model returns 402 on the same gateway | Workers AI does not use Unified Billing, so the authentication setting does not affect it | Same fix. A working @cf/ call is not evidence the gateway is configured |
400 + `Invalid value at messages[0].role: Invalid option: expected one of "user"\ | "assistant"` | An OpenAI-shaped body with role: "system" sent to an anthropic/* model |
anthropic/* returns 200 but choices[0].message.content is missing | The OpenAI envelope from an Anthropic model omits content and uses input_tokens/output_tokens | Use /ai/v1/messages and read content[0].text |
404 + Model not found: <id> | An id that is not in the catalogue, often a stale version suffix | anthropic/claude-sonnet-4-5 404s on this account; anthropic/claude-sonnet-5 does not. Check the catalogue |
410 + Model has been deprecated | A retired Workers AI model | Pick a current id from GET /accounts/<ACCOUNT_ID>/ai/models/search |
401 on a request that used to work | Missing or revoked token on an authenticated gateway | Reissue with AI Gateway – Run; every method needs it, GET included |
429 with spend under budget | Spend limits are eventually consistent under concurrency, or a rate-limit rule exists | Read the gateway config — a rate rule and a spend rule both return 429 |
405 on GET /ai/v1/models | That path is not a listing endpoint on the Cloudflare API | Use GET /accounts/<ACCOUNT_ID>/ai/models/search for @cf/ ids and the model catalogue for provider/model |
| Requests bill but no logs appear | collect_logs off on the gateway, or cf-aig-collect-log: false on the request | Turn logging on in the gateway settings; billing and logging are independent |
| Balance went negative | Documented behaviour; the card on file is charged for the shortfall at the start of the next month | Configure auto top-up with a threshold above one day's spend |
Rerun every number here
All of it was measured on 2026-07-26 against a production account with two gateways, cloud-kernel (authentication: false) and default (authentication: true), using a token with AI Gateway Run and Read. Total spend for the whole set: under two hundredths of a cent.
# 1. Ground truth for the 402 — which gateways are authenticated
curl -s "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai-gateway/gateways?per_page=50" \
-H "Authorization: Bearer <TOKEN>" | jq '.result[] | {id, authentication, is_default}'
# 2. The 402 / 200 pair: the loop in "The 402 has one cause" above
# 3. The shape trap: the three bodies in "anthropic/* gets Anthropic's validation rules" above
# 4. The receipt, including the cost field the arithmetic is checked against
curl -s "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai-gateway/gateways/default/logs?per_page=6&order_by=created_at&order_by_direction=desc" \
-H "Authorization: Bearer <TOKEN>" \
| jq '.result[] | {model, tokens_in, tokens_out, cost, authentication, wholesale, byok}'Substitute your own account id and token. The cost values will differ with the models you call; the reconciliation is the same — multiply tokens_in and tokens_out by the provider's published per-million rates and compare. A disagreement on your account is the interesting result, because that one number is what the no-markup claim rests on.
An MIT-licensed Worker that speaks the Anthropic Messages API and forwards to this surface, including the anthropic/* lane and the cf-aig-gateway-id handling above, is at github.com/massoumicyrus/claude-code-cloudflare-gateway — tools/contract-test.mjs runs 21 wire checks, tools/capture-gateway.mjs logs exactly what a client sends. The build that uses it: /a/claude-code-on-cloudflare-ai-gateway.
Key evidence
12 more ranked claims
Model review6 contributions · 1 modelExpand the recursive review layer
/api/articles/cloudflare-unified-billing/contributionsAsk this article · 8 suggested prompts
Text the build (+14245134626) or WhatsApp — slug|question creates a question node. Paste evidence with ingest slug|q:NODE_ID|your paste.