Cloudflare Unified Billing: the 5% is on the credits, and the 402 is one gateway toggle
Route model traffic through Cloudflare with no provider keys: what the 5% credit fee costs on a real monthly volume, the exact request that returns HTTP 402, and the anthropic/* body shape that fails on the OpenAI-compatible endpoint.
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 Run permissions, 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, and Edit permissions cannot be restricted to a single gateway — unlike R2, which supports per-bucket scoping. Any token with AI Gateway Run can 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 from wrangler whoami. It is the {account_id} path segment below.
A provider/model id.openai/gpt-4.1-mini, anthropic/claude-sonnet-5, google/gemini-3-flash, xai/grok-3.
bash
1
2
3
4
5
6
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"}]}'
Line 6, Column 1Shell Script
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
json
1
{"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":[]}
Line 1, Column 1JSON
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.
bash
1
2
3
4
5
6
7
8
# 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"}]}'
done
Line 8, Column 1Shell Script
cloud-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:
json
1
2
3
{"model":"anthropic/claude-sonnet-5","max_tokens":16,
"messages":[{"role":"system","content":"You are terse."},
{"role":"user","content":"Reply with the single word: ok"}]}
Line 3, Column 1JSON
text
1
2
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":[]}
Line 2, Column 1Plain Text
Two bodies work. Drop the system message and keep /ai/v1/chat/completions:
json
1
2
{"model":"anthropic/claude-sonnet-5","max_tokens":16,
"messages":[{"role":"user","content":"Reply with the single word: ok"}]}
Line 2, Column 1JSON
Or keep the system prompt and move to /ai/v1/messages, Anthropic's own shape:
bash
1
2
3
4
5
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"}]}'
"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.
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 cost field'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-mini at 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.
bash
1
2
3
4
5
6
7
8
9
10
11
# 1. Ground truth for the 402 — which gateways are authenticatedcurl -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 againstcurl -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}'
Line 11, Column 1Shell Script
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/redacted/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.
5%
fee on credits purchased, charged at top-up and not per request
402
status Unified Billing returns on a gateway with authentication off
$0.000084
logged cost of one 22-in / 4-out call to anthropic/claude-sonnet-5
$3.60
monthly fee on 100M input + 20M output tokens of openai/gpt-4.1-mini
0
provider API keys in the request path once Unified Billing is on
Free? They take the same 5% fee as OpenRouter does.— yencabulator, Hacker News, 2026-05-31
References (25)
Cloudflare AI Gateway — Unified Billing — Cloudflare, Jun 22, 2026“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.”
Cloudflare AI Gateway — Authenticated Gateway — Cloudflare, Jun 17, 2026“The `AI Gateway Read`, `Run`, and `Edit` permissions cannot be restricted to a single gateway — unlike R2, which supports per-bucket scoping. Any token with `AI Gateway Run` can send requests through every gateway in the account, including any configured with stored provider keys through Bring Your Own Keys (BYOK), consuming those credentials.”
Cloudflare AI Gateway — REST API — Cloudflare, Jun 29, 2026“The `/ai/v1/messages` endpoint strictly uses Anthropic's API schema and supports routing to Anthropic and other third-party models. Workers AI models (`@cf/`) do not support this schema.”
Cloudflare AI Gateway — Pricing — Cloudflare, May 19, 2026“AI Gateway's core features available today are offered for free, and all it takes is a Cloudflare account and one line of code to get started. Core features include: dashboard analytics, caching, and rate limiting.”
Cloudflare AI Gateway — Spend limits — Cloudflare“Spend limits are eventually consistent. The current request's cost is recorded after completion, so a burst of concurrent requests can briefly exceed the limit before enforcement catches up.”
Cloudflare Workers AI — Pricing — Cloudflare“Workers AI is included in both the Free and Paid Workers plans and is priced at **$0.011 per 1,000 Neurons**.”
OpenRouter — FAQ, what are the fees for using OpenRouter — OpenRouter“OpenRouter charges a fee when you purchase credits. We pass through the pricing of the underlying model providers without any markup, so you pay the same rate as you would directly with the provider.”
Support anthropic models via cloudflare's unified billing — GitHub, Jun 20, 2026“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.”
Comment on "Cloudflare's AI Platform" — no pricing on the models page — Hacker News, Apr 16, 2026“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”
Allow Cloudflare Unified Billing without upstream provider keys — GitHub, Feb 25, 2026“Cloudflare Unified Billing lets the gateway handle upstream authentication and billing, so provider API keys should be optional when routing through the AI Gateway.”
First-party: the 402 reproduced against two gateways, 2026-07-26 — miscsubjects.com, Jul 26, 2026“{"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":[]}”
First-party: the anthropic/* shape trap and both working bodies, 2026-07-26 — miscsubjects.com, Jul 26, 2026“{"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":[]}”
{
"schema":"http://google-preview.miscsubjects.com/schemas/jarticle.json",
"status":"published",
"slug":"cloudflare-unified-billing",
"title":"Cloudflare Unified Billing: the 5% is on the credits, and the 402 is one gateway toggle",
"kind":"essay",
"summary":"Route model traffic through Cloudflare with no provider keys: what the 5% credit fee costs on a real monthly volume, the exact request that returns HTTP 402, and the anthropic/* body shape that fails on the OpenAI-compatible endpoint.",
"created":"2026-07-26T03:31:37.000Z",
"body": [
"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.\"\n\n## Evidence status\n\n**Observed** marks first-party measurements or runtime receipts from the named environment.\n**Derived** marks arithmetic calculated from cited inputs. **Specified** marks vendor or standards\ndocumentation. **Implemented** and **deployed** name code and live-state evidence, respectively.\n**Reproduced** means the stated procedure was rerun. **Externally attested** marks operator reports;\nthose reports show that an experience occurred, not that it is universal.\n\n## Terms this page uses\n\n| Term | Meaning |\n| --- | --- |\n| Unified Billing | Cloudflare authenticates and pays the provider; you pay Cloudflare. Appears as `wholesale: true` in the gateway API and log rows. |\n| BYOK | Bring Your Own Keys — your provider key is stored in Cloudflare Secrets Store and forwarded; the provider bills you. |\n| 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](/a/cloudflare-ai-gateway-setup). |\n| Authenticated gateway | A gateway with its `authentication` setting on, which then demands a Cloudflare API token on every request. |\n| Credits | Prepaid dollars on the Cloudflare account. Bought at a 5% surcharge; spent at the provider's own per-token rate. |\n| Neurons | The unit Workers AI bills in, $0.011 per 1,000. A separate ledger from credits. |\n\n## The 5% lands on the money, not on the traffic\n\nCloudflare 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.\"\n\nThat 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.\n\nTwo 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.\n\nThe 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.\n\n## The arithmetic for 100 million input and 20 million output tokens a month\n\nAssumptions: 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.\n\n`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.\n\n`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.\n\n| Route | Inference | Cloudflare fee | You pay |\n| --- | --- | --- | --- |\n| `openai/gpt-4.1-mini`, BYOK | $72.00 | $0.00 | **$72.00** |\n| `openai/gpt-4.1-mini`, Unified Billing | $72.00 | $3.60 | **$75.60** |\n| `anthropic/claude-sonnet-5` intro, BYOK | $400.00 | $0.00 | **$400.00** |\n| `anthropic/claude-sonnet-5` intro, Unified Billing | $400.00 | $20.00 | **$420.00** |\n| `anthropic/claude-sonnet-5` after 2026-08-31, Unified Billing | $600.00 | $30.00 | **$630.00** |\n\nSonnet 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.\n\nA 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](/a/workers-ai-coding-models).\n\n## Five things that must be true before a request bills\n\n1. **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.\n2. **A gateway with authentication on.** That gateway's **Settings** → **Create authentication token** (a token with the required `Run` permissions, shown once) → back on the settings page, toggle **Authenticated Gateway** on. Skipping this is the 402 below.\n3. **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`, and `Edit` permissions cannot be restricted to a single gateway — unlike R2, which supports per-bucket scoping. Any token with `AI Gateway Run` can send requests through every gateway in the account.\"\n4. **The account id.** The 32-character hex string after `dash.cloudflare.com/` in the dashboard URL, or from `wrangler whoami`. It is the `{account_id}` path segment below.\n5. **A `provider/model` id.** `openai/gpt-4.1-mini`, `anthropic/claude-sonnet-5`, `google/gemini-3-flash`, `xai/grok-3`.\n\n```bash\ncurl -X POST \"https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai/v1/chat/completions\" \\\n -H \"Authorization: Bearer <TOKEN>\" \\\n -H \"cf-aig-gateway-id: default\" \\\n -H \"Content-Type: application/json\" \\\n --data '{\"model\":\"openai/gpt-4.1-mini\",\"max_tokens\":16,\n \"messages\":[{\"role\":\"user\",\"content\":\"Reply with the single word: ok\"}]}'\n```\n\nOmitting `cf-aig-gateway-id` routes third-party requests through the account's default gateway. Workers AI `@cf/` requests always require the header.\n\n## The 402 has one cause, and the docs table predicts the opposite\n\n```json\n{\"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\":[]}\n```\n\nOne 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.\n\n```bash\n# Change only the gateway id between these two runs.\nfor GW in cloud-kernel default; do\n curl -s -w \" <- %{http_code} on $GW\\n\" -X POST \\\n \"https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai/v1/chat/completions\" \\\n -H \"Authorization: Bearer <TOKEN>\" -H \"cf-aig-gateway-id: $GW\" \\\n -H \"Content-Type: application/json\" \\\n --data '{\"model\":\"openai/gpt-4.1-mini\",\"max_tokens\":16,\"messages\":[{\"role\":\"user\",\"content\":\"Reply with the single word: ok\"}]}'\ndone\n```\n\n`cloud-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}}`.\n\n| Gateway `authentication` | Request | Result |\n| --- | --- | --- |\n| `false` | `provider/model` | **402, code 2021** |\n| `false` | `@cf/vendor/model` | 200 — Workers AI is not Unified Billing |\n| `true`, token present | `provider/model` | 200 |\n| `true`, token absent | anything | 401 — an auth failure, not a billing failure |\n| either, BYOK key stored | `provider/model` | 200, provider bills you. BYOK's own prerequisite is still an authenticated gateway. |\n\nTwo 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.\n\n**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.\n\n## `anthropic/*` gets Anthropic's validation rules, whatever the endpoint is called\n\n`/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](/a/what-is-the-anthropic-messages-api)).\n\n**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.\n\nReproduced against `anthropic/claude-sonnet-5` on an authenticated gateway. The body that fails, and its exact answer:\n\n```json\n{\"model\":\"anthropic/claude-sonnet-5\",\"max_tokens\":16,\n \"messages\":[{\"role\":\"system\",\"content\":\"You are terse.\"},\n {\"role\":\"user\",\"content\":\"Reply with the single word: ok\"}]}\n```\n```\nHTTP 400\n{\"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\":[]}\n```\n\nTwo bodies work. Drop the system message and keep `/ai/v1/chat/completions`:\n\n```json\n{\"model\":\"anthropic/claude-sonnet-5\",\"max_tokens\":16,\n \"messages\":[{\"role\":\"user\",\"content\":\"Reply with the single word: ok\"}]}\n```\n\nOr keep the system prompt and move to `/ai/v1/messages`, Anthropic's own shape:\n\n```bash\ncurl -X POST \"https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai/v1/messages\" \\\n -H \"Authorization: Bearer <TOKEN>\" -H \"cf-aig-gateway-id: default\" \\\n -H \"Content-Type: application/json\" \\\n --data '{\"model\":\"anthropic/claude-sonnet-5\",\"max_tokens\":16,\"system\":\"You are terse.\",\n \"messages\":[{\"role\":\"user\",\"content\":\"Reply with the single word: ok\"}]}'\n```\n```json\n{\"id\":\"msg_011CdQ3qJ4WRi3L5nnG3cdc3\",\"type\":\"message\",\"role\":\"assistant\",\n \"content\":[{\"type\":\"text\",\"text\":\"ok\"}],\"model\":\"claude-sonnet-5\",\"stop_reason\":\"end_turn\",\n \"usage\":{\"input_tokens\":22,\"output_tokens\":4},\"gatewayMetadata\":{\"keySource\":\"Unified\"}}\n```\n\n`\"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.\n\nPrefer 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.\n\nThe 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`.\n\n## Model ids and endpoints decide the ledger\n\n| Id shape | Example | Billed as | `cf-aig-gateway-id` |\n| --- | --- | --- | --- |\n| `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 |\n| `@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** |\n| `provider/model` with a stored key | any of the above | the provider bills you; Cloudflare bills nothing | required, gateway must be authenticated |\n\nFour 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.\n\n## What is covered, and the four places the documentation stops\n\nProviders 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.\"\n\nFour silences:\n\n- **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.\n- **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.\n- **The `cost` field's precision.** Present on every log row, the basis of spend limits, rounding unspecified.\n- **The `anthropic/*` envelope on the OpenAI endpoint.** Measured above, described nowhere.\n\n## What one billed request leaves behind\n\n`GET /accounts/<ACCOUNT_ID>/ai-gateway/gateways/default/logs` returns the receipt for the calls made above.\n\n| `model` | `tokens_in` / `tokens_out` | `cost` | `authentication` | `wholesale` | `byok` |\n| --- | --- | --- | --- | --- | --- |\n| `anthropic/claude-sonnet-5` | 22 / 4 | `0.00008400000000000001` | `true` | `true` | `null` |\n| `anthropic/claude-sonnet-5` | 16 / 4 | `0.000072` | `true` | `true` | `null` |\n| `openai/gpt-4.1-mini` | 14 / 2 | `0.0000088` | `true` | `true` | `null` |\n\n`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:\n\n- `openai/gpt-4.1-mini` at 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.\n- `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.\n- The 16-in row: (32 + 40) ÷ 1,000,000 = **$0.000072**. The logged value.\n\nTwo 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.\n\n## Six routes, and the one to take\n\n| Route | Take it when | Cost of taking it |\n| --- | --- | --- |\n| **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 |\n| **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 |\n| **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 |\n| **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' |\n| **OpenRouter** | Breadth of model coverage matters more than price | 5.5% + $0.80 on a card top-up, and another vendor relationship |\n| **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 |\n\nTwo 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.\"\n\n**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.\n\n## Symptom, cause, fix\n\n| Symptom | Cause | Fix |\n| --- | --- | --- |\n| `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 |\n| `@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 |\n| `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 | Move the system prompt to a top-level `system` field and call `/ai/v1/messages`, or drop the system message |\n| `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` |\n| `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 |\n| `410` + `Model has been deprecated` | A retired Workers AI model | Pick a current id from `GET /accounts/<ACCOUNT_ID>/ai/models/search` |\n| `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 |\n| `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 |\n| `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` |\n| 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 |\n| 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 |\n\n## Rerun every number here\n\nAll 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.\n\n```bash\n# 1. Ground truth for the 402 — which gateways are authenticated\ncurl -s \"https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/ai-gateway/gateways?per_page=50\" \\\n -H \"Authorization: Bearer <TOKEN>\" | jq '.result[] | {id, authentication, is_default}'\n\n# 2. The 402 / 200 pair: the loop in \"The 402 has one cause\" above\n# 3. The shape trap: the three bodies in \"anthropic/* gets Anthropic's validation rules\" above\n\n# 4. The receipt, including the cost field the arithmetic is checked against\ncurl -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\" \\\n -H \"Authorization: Bearer <TOKEN>\" \\\n | jq '.result[] | {model, tokens_in, tokens_out, cost, authentication, wholesale, byok}'\n```\n\nSubstitute 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.\n\nAn 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/redacted/claude-code-cloudflare-gateway](https://github.com/redacted/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](/a/claude-code-on-cloudflare-ai-gateway)."
],
"facts": {
"converted_from":"markdown",
"original_updated":"2026-07-26T05:37:32.278Z",
"register":"essay",
"category":null
},
"hero":"https://miscsubjects.com/img/up/cloudflare-unified-billing-hero-card.png",
"model":"Opus 5 (Claude Code)",
"tags": [
"tooling",
"cloudflare",
"billing",
"ai-gateway",
"unified-billing",
"llm-routing"
],
"sources": [
{
"id":"s1",
"type":"publisher_documentation",
"url":"https://developers.cloudflare.com/ai-gateway/features/unified-billing/",
"title":"Cloudflare AI Gateway — Unified Billing",
"publisher":"Cloudflare",
"date":"2026-06-22",
"quote":"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.",
"summary":"The fee, the no-markup pass-through, the two prerequisites (credits loaded, gateway authenticated), the supported provider list, the exclusion of Workers AI @cf/ models, the negative-balance caution, and the ZDR section. Positive on clarity of the fee, silent on the 402.",
"claim_ids": [
"c1",
"c11",
"c16",
"c2",
"c3",
"c4",
"c9"
],
"quote_sha256":"fafd2d847e1c7923cbd94fa022dea00b9f40f0b5616735dd8022404338d4126a"
},
{
"id":"s2",
"type":"publisher_documentation",
"url":"https://developers.cloudflare.com/ai-gateway/configuration/authentication/",
"title":"Cloudflare AI Gateway — Authenticated Gateway",
"publisher":"Cloudflare",
"date":"2026-06-17",
"quote":"The `AI Gateway Read`, `Run`, and `Edit` permissions cannot be restricted to a single gateway — unlike R2, which supports per-bucket scoping. Any token with `AI Gateway Run` can send requests through every gateway in the account, including any configured with stored provider keys through Bring Your Own Keys (BYOK), consuming those credentials.",
"summary":"The exact dashboard click path for turning authentication on, and the warning that AI Gateway token permissions are account-scoped. Also carries the behaviour table whose 'Off / No header → Request succeeds' row is contradicted by the measured 402 for Unified Billing. Negative on that row.",
"claim_ids": [
"c13",
"c4",
"c5"
],
"quote_sha256":"48279ec165be7ff71685b1c4edb730e217c9be3311ac9d9ee8fa04c7d2c788dd"
},
{
"id":"s3",
"type":"specification",
"url":"https://developers.cloudflare.com/ai-gateway/usage/rest-api/",
"title":"Cloudflare AI Gateway — REST API",
"publisher":"Cloudflare",
"date":"2026-06-29",
"quote":"The `/ai/v1/messages` endpoint strictly uses Anthropic's API schema and supports routing to Anthropic and other third-party models. Workers AI models (`@cf/`) do not support this schema.",
"summary":"The reference the endpoint and model-id behaviour comes from: four endpoints with a per-endpoint support matrix, the author/model vs @cf/author/model naming rule, the cf-aig-gateway-id requirement, and the per-request cf-aig-* header table.",
"claim_ids": [
"c6",
"c7",
"c8"
],
"quote_sha256":"b1a95ce20304585ff40f0e476fbb779fbf76f4f7ff862c8137e08fed8071c0ac"
},
{
"id":"s4",
"type":"publisher_documentation",
"url":"https://developers.cloudflare.com/ai-gateway/reference/pricing/",
"title":"Cloudflare AI Gateway — Pricing",
"publisher":"Cloudflare",
"date":"2026-05-19",
"quote":"AI Gateway's core features available today are offered for free, and all it takes is a Cloudflare account and one line of code to get started. Core features include: dashboard analytics, caching, and rate limiting.",
"summary":"Establishes the zero in the BYOK column of the money table: the gateway itself costs nothing, so the only Cloudflare charge in the Unified Billing route is the 5% credit surcharge.",
"claim_ids": [
"c2"
],
"quote_sha256":"862b170e2808ae5ca15c56247b3dc621eead78c23053f7fc60019759095e479f"
},
{
"id":"s5",
"type":"publisher_documentation",
"url":"https://developers.cloudflare.com/ai-gateway/features/spend-limits/",
"title":"Cloudflare AI Gateway — Spend limits",
"publisher":"Cloudflare",
"date":"",
"quote":"Spend limits are eventually consistent. The current request's cost is recorded after completion, so a burst of concurrent requests can briefly exceed the limit before enforcement catches up.",
"summary":"Confirms spend limits cover both Unified Billing and BYOK, that they block with 429, and that they are computed from the same per-request cost field the log rows carry. The eventual-consistency admission is why a 429 can arrive under budget.",
"claim_ids": [
"c12"
],
"quote_sha256":"a9a33d8100b5df268bccb7bbbcc94bb6cdd2055b5fac3b774f32773814e20e25"
},
{
"id":"s6",
"type":"publisher_documentation",
"url":"https://developers.cloudflare.com/ai-gateway/configuration/bring-your-own-keys/",
"title":"Cloudflare AI Gateway — BYOK (Store Keys)",
"publisher":"Cloudflare",
"date":"2026-06-09",
"quote":"Ensure your gateway is authenticated.",
"summary":"The alternative route. Keys live in Secrets Store and the provider bills you directly — and the prerequisite list shows BYOK requires the same authenticated gateway Unified Billing does, so switching lanes does not remove that step.",
"claim_ids": [
"c14"
],
"quote_sha256":"2548cf8cac73da5de502e2d837deaf1bc81b28306b73bf2744328bb2ef093518"
},
{
"id":"s7",
"type":"publisher_documentation",
"url":"https://developers.cloudflare.com/workers-ai/platform/pricing/",
"title":"Cloudflare Workers AI — Pricing",
"publisher":"Cloudflare",
"date":"",
"quote":"Workers AI is included in both the Free and Paid Workers plans and is priced at **$0.011 per 1,000 Neurons**.",
"summary":"The other ledger. Gives the unit and rate that a measured @cf/ call's neurons field converts into, which is how the Workers AI row of the money table is priced.",
"claim_ids": [
"c3"
],
"quote_sha256":"5986a435cbd32a103328c42223ec3873c1e22b77b540ea4c0baa310411878372"
},
{
"id":"s8",
"type":"publisher_documentation",
"url":"https://developers.openai.com/api/docs/pricing",
"title":"OpenAI — API pricing",
"publisher":"OpenAI",
"date":"",
"quote":"gpt-4.1-mini 0.4 0.1 1.6",
"summary":"OpenAI's own per-million rates for gpt-4.1-mini: $0.40 input, $0.10 cached input, $1.60 output. Used to test Cloudflare's no-markup claim against a real logged cost field, and to price the worked example.",
"claim_ids": [
"c10",
"c15"
],
"quote_sha256":"4ccdfe0ea600d2a27754ffc2fe3a6c9da482c7da1b95b6743457d92ab37830ff"
},
{
"id":"s9",
"type":"publisher_documentation",
"url":"https://openrouter.ai/docs/faq",
"title":"OpenRouter — FAQ, what are the fees for using OpenRouter",
"publisher":"OpenRouter",
"date":"",
"quote":"OpenRouter charges a fee when you purchase credits. We pass through the pricing of the underlying model providers without any markup, so you pay the same rate as you would directly with the provider.",
"summary":"The other side of the disagreement over 'the same 5%'. Identical mechanism to Cloudflare's, but the rate rendered on the page is 5.5% with a $0.80 minimum for card payments and 5% for crypto — so the mechanism matches and the number does not.",
"claim_ids": [
"c16"
],
"quote_sha256":"1154c560494b45dab33aee09a2c8b2194e8ca9728a31d2bc6f7327fa79debf2b"
},
{
"id":"s10",
"type":"publisher_documentation",
"url":"https://platform.claude.com/docs/en/about-claude/models/overview",
"title":"Anthropic — Model overview and pricing",
"publisher":"Anthropic",
"date":"",
"quote":"Introductory pricing of $2 / $10 per MTok applies to Claude Sonnet 5 through August 31, 2026.",
"summary":"The published rate the measured Cloudflare cost field is reconciled against for anthropic/claude-sonnet-5, plus the standard $3 / $15 that replaces it after 2026-08-31 — which is what makes the last row of the money table move.",
"claim_ids": [
"c10",
"c15"
],
"quote_sha256":"86bcee1f021529a53395d6f6b24bda168fee8ce8118c819861108451347b7092"
},
{
"id":"s11",
"type":"github",
"url":"https://github.com/cloudflare/ai/issues/548",
"title":"AI Gateway REST: /ai/v1/responses rejects Unified Billing on an authenticated gateway",
"author":"pemontto",
"publisher":"GitHub",
"date":"2026-05-30",
"quote":"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`.",
"summary":"The canonical 402 report, filed on Cloudflare's own repository with a full curl reproduction: identical headers and model return 402 on /ai/v1/responses and 200 on /ai/v1/chat/completions, across openai/gpt-5.4, gpt-5.4-mini and gpt-5.5. Negative. Open with zero comments; did not reproduce on this account today, and both outcomes are published.",
"claim_ids": [
"c17",
"c5"
],
"quote_sha256":"534b8e298299719f21838d5fece3ce0516aab593a6180fd81ff1baf255e31186"
},
{
"id":"s12",
"type":"github",
"url":"https://github.com/anomalyco/opencode/issues/32951",
"title":"Cloudflare AI Gateway (Unified Billing) rejects `role: \"system\"` in messages array when proxying Anthropic models",
"author":"pmonte",
"publisher":"GitHub",
"date":"2026-06-19",
"quote":"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**.",
"summary":"The shape trap named precisely. opencode 1.17.8 against anthropic/claude-sonnet-4.6 failed on every request because the OpenAI-compatible client puts the system prompt in messages[0]. Negative — the OpenAI-compatible veneer leaks Anthropic's schema.",
"claim_ids": [
"c18",
"c7"
],
"quote_sha256":"e1197b6fdaa49914f681baf6d9f3f5e68d2848341265acc411e4af23e679c47d"
},
{
"id":"s13",
"type":"github",
"url":"https://github.com/withastro/flue/issues/327",
"title":"Support anthropic models via cloudflare's unified billing",
"author":"just-be-dev",
"publisher":"GitHub",
"date":"2026-06-20",
"quote":"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.",
"summary":"States the motive plainly — no per-provider keys — then hits the same mismatch from the adapter side: the Cloudflare binding formats every cloudflare/... request as OpenAI chat-completions, which works for cloudflare/@cf/... ids and not for cloudflare/anthropic/claude-sonnet-4.6. Mixed, leaning negative; patched locally and verified from a bare setup.",
"claim_ids": [
"c18",
"c7"
],
"quote_sha256":"fa0104ff395d3db921a1eee5a4664ab62d71864cebae8e957ee20bd260e70afc"
},
{
"id":"s14",
"type":"hn",
"url":"https://news.ycombinator.com/item?id=48346648",
"title":"Comment on \"OpenRouter raises $113M Series B\" — the Cloudflare gateway is not free",
"author":"yencabulator",
"publisher":"Hacker News",
"date":"2026-05-31",
"quote":"Free? They take the same 5% fee as OpenRouter does.",
"summary":"Direct correction of a commenter who called Cloudflare's gateway the free option, citing the unified-billing docs page. Negative on the pricing-surprise axis, and the claim this page reconciles against OpenRouter's own published 5.5% + $0.80 card fee.",
"claim_ids": [
"c16"
],
"quote_sha256":"8bf62666398d89638c04051c554cba04dd9db50e48454878872a7183bef7be0d"
},
{
"id":"s15",
"type":"hn",
"url":"https://news.ycombinator.com/item?id=47793121",
"title":"Comment on \"Cloudflare's AI Platform\" — no pricing on the models page",
"author":"bm-rf",
"publisher":"Hacker News",
"date":"2026-04-16",
"quote":"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",
"summary":"Read the launch docs and could not find pricing at all, and separately noticed zero data retention is off by default. Negative — the pricing-opacity complaint that precedes the later 5% discovery, and the earliest public note that ZDR is opt-in.",
"claim_ids": [
"c11",
"c19"
],
"quote_sha256":"95a9c8109dd0908c7aa80e3b39a07c2f4710b4a4e1053e985dcd9681a7e463bf"
},
{
"id":"s16",
"type":"hn",
"url":"https://news.ycombinator.com/item?id=47793207",
"title":"Comment on \"Cloudflare's AI Platform\" — markup on token usage",
"author":"6thbit",
"publisher":"Hacker News",
"date":"2026-04-16",
"quote":"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.",
"summary":"Grants Cloudflare is well positioned network-wise but assumes a markup on token usage. Negative — the suspicion that turns out to be half right: there is a fee, but it sits on the credit purchase and not on the tokens, which the measured cost rows confirm.",
"claim_ids": [
"c15",
"c19"
],
"quote_sha256":"e19b720ab4957f81add8b89dd360d1ac8542535afaff5bc74569a6aa0079af34"
},
{
"id":"s17",
"type":"github",
"url":"https://github.com/jeremyhart/claworc/pull/6",
"title":"Simplify Cloudflare AI Gateway to Unified Billing; fix migration guard",
"author":"jeremyhart",
"publisher":"GitHub",
"date":"2026-06-26",
"quote":"Cloudflare authenticates and bills the upstream provider, so no per-provider keys are needed.",
"summary":"Merged pull request rewriting a connector to drop the confusing \"Provider API Key\" plus separate \"Gateway Token\" pair in favour of a single Cloudflare API token with AI Gateway Run permission. Positive — the auth collapse is the payoff people are actually after.",
"claim_ids": [
"c1",
"c20"
],
"quote_sha256":"af0e20971e6ef52ffd4b4a97be067b0ac1de2316b9898e78bfd00da4b4d45cf2"
},
{
"id":"s18",
"type":"github",
"url":"https://github.com/kyleboas/blob/pull/124",
"title":"Allow Cloudflare Unified Billing without upstream provider keys",
"author":"kyleboas",
"publisher":"GitHub",
"date":"2026-02-25",
"quote":"Cloudflare Unified Billing lets the gateway handle upstream authentication and billing, so provider API keys should be optional when routing through the AI Gateway.",
"summary":"Merged pull request fixing a callLLM that attached an upstream Authorization header on every gateway request and injected ANTHROPIC_API_KEY/OPENAI_API_KEY into agent inputs, blocking pure Unified Billing flows. Now requires only aiGatewayToken, with provider keys as an explicit BYOK fallback. Positive.",
"claim_ids": [
"c1",
"c20"
],
"quote_sha256":"670bbc18829bbdcaf5a0bb9e4fcdfa1471893351b2d08f391e9b3a0b619cad33"
},
{
"id":"s19",
"type":"repository",
"url":"https://github.com/redacted/claude-code-cloudflare-gateway",
"title":"claude-code-cloudflare-gateway — a Worker that speaks Anthropic Messages and forwards to this surface",
"publisher":"GitHub",
"date":"",
"quote":"tools/contract-test.mjs",
"summary":"MIT-licensed reference implementation of the routing described here, including the anthropic/* lane and cf-aig-gateway-id handling, with a 21-check wire contract test and a capture tool that logs exactly what a client sends. Where a reader can read the code rather than trust the prose.",
"claim_ids": [
"c6"
],
"quote_sha256":"677281229555e069646268d0da19477b96b099ac07813f6c1c81a7d2cb6dfc10"
},
{
"id":"s20",
"type":"independent_measurement",
"url":"https://miscsubjects.com/a/claude-code-on-cloudflare-ai-gateway",
"title":"Measured per-turn cost of coding traffic through the same gateway",
"publisher":"miscsubjects.com",
"date":"2026-07-25",
"quote":"MCP attached GLM-5.2 149,443 in / $0.20922644 / 10.9s; Kimi 149,187 in / $0.02852109",
"summary":"Independent of this page's own probes: per-turn token and cost figures captured with tools/capture-gateway.mjs and read back from gateway logs, for a real coding agent on the same account. Gives the 5% a denominator in monthly terms rather than per-request cents.",
"claim_ids": [
"c15"
],
"quote_sha256":"78ddc087db8c4150ed75a6d71ce8ef34c4b7406f9921e490494189bb6741e237"
},
{
"id":"s21",
"type":"runtime_receipt",
"url":"https://miscsubjects.com/a/cloudflare-unified-billing",
"title":"First-party: the 402 reproduced against two gateways, 2026-07-26",
"publisher":"miscsubjects.com",
"date":"2026-07-26",
"quote":"{\"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\":[]}",
"summary":"Two POSTs to /ai/v1/chat/completions with model openai/gpt-4.1-mini, the same token and the same body, differing only in cf-aig-gateway-id: the gateway with authentication:false returned 402 code 2021, the gateway with authentication:true returned 200 with content 'ok' and usage 14/1. Method published in the article so it can be rerun.",
"claim_ids": [
"c13",
"c5"
],
"quote_sha256":"1b2902177260d6c6a3d30f08ad32cb705004959c270d9d6c54ee9c8db7f5ae72"
},
{
"id":"s22",
"type":"runtime_receipt",
"url":"https://miscsubjects.com/a/cloudflare-unified-billing",
"title":"First-party: the anthropic/* shape trap and both working bodies, 2026-07-26",
"publisher":"miscsubjects.com",
"date":"2026-07-26",
"quote":"{\"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\":[]}",
"summary":"anthropic/claude-sonnet-5 on an authenticated gateway: an OpenAI-shaped body with role 'system' at messages[0] returned HTTP 400 with this error; the same body without the system message returned 200 but with no content field; /ai/v1/messages with a top-level system returned 200 with the text and gatewayMetadata keySource 'Unified'.",
"claim_ids": [
"c18",
"c21",
"c7"
],
"quote_sha256":"bca787c42c28179bb2df7a7aa2d10445e33f39188858710b167fae3369115320"
},
{
"id":"s23",
"type":"runtime_receipt",
"url":"https://miscsubjects.com/a/cloudflare-unified-billing",
"title":"First-party: gateway log rows with the cost field, reconciled against published rates",
"publisher":"miscsubjects.com",
"date":"2026-07-26",
"quote":"\"tokens_in\":22,\"tokens_out\":4,\"cost\":0.00008400000000000001,\"authentication\":true,\"wholesale\":true,\"byok\":null",
"summary":"Three rows read back from GET /ai-gateway/gateways/default/logs for the calls made in this article. Each cost value equals tokens times the provider's published per-million rate to the digit, on two providers — the no-markup claim tested rather than repeated. wholesale:true is the Unified Billing marker; byok:null confirms no stored key.",
"claim_ids": [
"c10",
"c15",
"c22"
],
"quote_sha256":"dc648b805a9966cc7c3414d787d83c4b3b53324e4303bb5a5ebf68368901c22f"
},
{
"id":"s24",
"type":"runtime_receipt",
"url":"https://miscsubjects.com/a/cloudflare-unified-billing",
"title":"First-party: /ai/v1/responses returned 200, so issue 548 did not reproduce here",
"publisher":"miscsubjects.com",
"date":"2026-07-26",
"quote":"\"object\":\"response\",\"model\":\"openai/gpt-4.1-mini\",\"status\":\"completed\",\"usage\":{\"input_tokens\":14,\"output_tokens\":2",
"summary":"POST /ai/v1/responses with openai/gpt-4.1-mini and cf-aig-gateway-id pointed at the authenticated gateway returned HTTP 200 with a full Responses envelope. Negative result for the endpoint-specific 402 in cloudflare/ai issue 548 on this account and model; the issue remains open and uncommented, so both are published rather than one being chosen.",
"claim_ids": [
"c17"
],
"quote_sha256":"d04415e39733192087d10fdc395daa9ba8af3525d68f4c44cdb6870166478634"
},
{
"id":"s25",
"type":"runtime_receipt",
"url":"https://miscsubjects.com/a/cloudflare-unified-billing",
"title":"First-party: a Workers AI model on the unauthenticated gateway, billed in Neurons",
"publisher":"miscsubjects.com",
"date":"2026-07-26",
"quote":"\"usage\":{\"prompt_tokens\":42,\"completion_tokens\":2,\"total_tokens\":44,\"prompt_tokens_details\":{\"cached_tokens\":0},\"neurons\":1.529652714729309}",
"summary":"@cf/meta/llama-3.3-70b-instruct-fp8-fast through the same gateway that 402s on provider/model returned HTTP 200 and reported neurons rather than a dollar cost. Direct evidence that the authentication requirement belongs to Unified Billing and not to the gateway, and that a working @cf/ call proves nothing about the configuration.",
"claim_ids": [
"c13",
"c3"
],
"quote_sha256":"8194589e7c7d7952abed67709fa4cb9dabf3ce652b9a995f0330b36b54072aa5"
}
],
"claims": [
{
"id":"c1",
"text":"Unified Billing means Cloudflare authenticates and pays the upstream provider, so a client holds one Cloudflare API token and no provider keys.",
"section":"What Unified Billing is",
"tier":"system",
"source_ids": [
"s1",
"s17",
"s18"
],
"why_material":"Without this the reader cannot tell the feature apart from a proxy that forwards their own key.",
"who_claims":"Opus 5 (Claude Code)",
"evidence_status":"specified + externally attested"
},
{
"id":"c2",
"text":"The only Cloudflare charge is a 5% surcharge on credits purchased; AI Gateway itself is free on all plans and inference is passed through at the provider's per-token rate.",
"section":"The 5% lands on the money, not on the traffic",
"tier":"system",
"source_ids": [
"s1",
"s4"
],
"why_material":"This is the entire price of the arrangement and the number the whole decision turns on.",
"who_claims":"Opus 5 (Claude Code)",
"evidence_status":"specified"
},
{
"id":"c3",
"text":"Models named @cf/vendor/model are billed as Workers AI Neurons at $0.011 per 1,000 and are explicitly not charged through Unified Billing.",
"section":"Model ids and endpoints decide the billing lane",
"tier":"system",
"source_ids": [
"s1",
"s25",
"s7"
],
"why_material":"A mixed setup produces two separate line items, which a reader should expect rather than debug.",
"who_claims":"Opus 5 (Claude Code)",
"evidence_status":"observed + specified"
},
{
"id":"c4",
"text":"Unified Billing requires both loaded credits and a gateway with authentication turned on, created through Settings then Create authentication token then the Authenticated Gateway toggle.",
"section":"Five things that must be true before a single request bills",
"tier":"system",
"source_ids": [
"s1",
"s2"
],
"why_material":"These are the two prerequisites, and the second one is the cause of the most common failure.",
"who_claims":"Opus 5 (Claude Code)",
"evidence_status":"specified"
},
{
"id":"c5",
"text":"A provider/model request routed at a gateway with authentication off returns HTTP 402 with code 2021 and the message 'Gateway authentication is required to use unified billing'.",
"section":"The 402 comes from one setting",
"tier":"system",
"source_ids": [
"s11",
"s2",
"s21"
],
"why_material":"It is the single most common failure and it presents as a model problem rather than a settings problem.",
"who_claims":"Opus 5 (Claude Code)",
"evidence_status":"observed + specified + externally attested"
},
{
"id":"c6",
"text":"Four REST endpoints accept Unified Billing traffic — /ai/run, /ai/v1/chat/completions, /ai/v1/responses and /ai/v1/messages — and only /ai/v1/messages refuses Workers AI @cf/ ids outright.",
"section":"Model ids and endpoints decide the billing lane",
"tier":"system",
"source_ids": [
"s19",
"s3"
],
"why_material":"Choosing the wrong endpoint for the model family is the second most common failure.",
"who_claims":"Opus 5 (Claude Code)",
"evidence_status":"specified + implemented"
},
{
"id":"c7",
"text":"With Unified Billing an anthropic/* model id is validated by Anthropic's schema, so an OpenAI-shaped body carrying role 'system' in messages[0] fails with 'Invalid value at messages[0].role: Invalid option: expected one of \"user\"|\"assistant\"'.",
"section":"anthropic/* gets Anthropic's validation rules",
"tier":"system",
"source_ids": [
"s12",
"s13",
"s22",
"s3"
],
"why_material":"This is the exact point where the OpenAI-compatible veneer leaks, and the error names a field the client author never set.",
"who_claims":"Opus 5 (Claude Code)",
"evidence_status":"observed + specified + externally attested"
},
{
"id":"c8",
"text":"A Unified Billing model id is written provider/model while a Workers AI id is written @cf/vendor/model, and the Workers AI form additionally requires the cf-aig-gateway-id header.",
"section":"Model ids and endpoints decide the billing lane",
"tier":"system",
"source_ids": [
"s3"
],
"why_material":"Without the id convention a reader cannot predict which ledger a call lands in.",
"who_claims":"Opus 5 (Claude Code)",
"evidence_status":"specified"
},
{
"id":"c9",
"text":"The HTTP API covers OpenAI, Anthropic, Google AI Studio, Google Vertex AI, xAI and Groq.",
"section":"What is covered, and the four places the documentation stops",
"tier":"system",
"source_ids": [
"s1"
],
"why_material":"Coverage is the reason to adopt the arrangement, so the list has to be stated rather than implied.",
"who_claims":"Opus 5 (Claude Code)",
"evidence_status":"specified"
},
{
"id":"c10",
"text":"Logged per-request cost equals token counts multiplied by the provider's published per-million rate exactly, on both openai/gpt-4.1-mini and anthropic/claude-sonnet-5.",
"section":"What one billed request actually leaves behind",
"tier":"system",
"source_ids": [
"s10",
"s23",
"s8"
],
"why_material":"It is the only test of the no-markup claim, and it is testable from data the account already holds.",
"who_claims":"Opus 5 (Claude Code)",
"evidence_status":"observed + specified"
},
{
"id":"c11",
"text":"Zero data retention applies only to Unified Billing requests on Cloudflare-managed credentials, is off by default, is supported for OpenAI and Anthropic only, and falls back to the non-ZDR configuration silently for other providers.",
"section":"What is covered, and the four places the documentation stops",
"tier":"system",
"source_ids": [
"s1",
"s15"
],
"why_material":"A reader with a data-handling constraint would otherwise assume the default is safe.",
"who_claims":"Opus 5 (Claude Code)",
"evidence_status":"specified + externally attested"
},
{
"id":"c12",
"text":"Spend limits cover Unified Billing and BYOK, block with HTTP 429, and are eventually consistent, so concurrent bursts can exceed the limit before enforcement catches up.",
"section":"What is covered, and the four places the documentation stops",
"tier":"system",
"source_ids": [
"s5"
],
"why_material":"It is the only cost control available and its failure mode is a 429 that looks like rate limiting.",
"who_claims":"Opus 5 (Claude Code)",
"evidence_status":"specified"
},
{
"id":"c13",
"text":"Workers AI @cf/ models keep returning 200 on the same gateway that returns 402 for provider/model, so a working @cf/ call is not evidence the gateway is configured correctly.",
"section":"The 402 comes from one setting",
"tier":"system",
"source_ids": [
"s2",
"s21",
"s25"
],
"why_material":"It is the false signal that sends people to debug the model id instead of the gateway setting.",
"who_claims":"Opus 5 (Claude Code)",
"evidence_status":"observed + specified"
},
{
"id":"c14",
"text":"BYOK requires an authenticated gateway too, so switching off Unified Billing does not remove the setting that caused the 402.",
"section":"Five routes, ranked",
"tier":"system",
"source_ids": [
"s6"
],
"why_material":"A reader treating BYOK as the escape hatch from the 402 would make the same request fail a second way.",
"who_claims":"Opus 5 (Claude Code)",
"evidence_status":"specified"
},
{
"id":"c15",
"text":"On 100,000,000 input and 20,000,000 output tokens a month of openai/gpt-4.1-mini the inference is $72.00 and the Unified Billing fee is $3.60, against $72.00 and no fee for BYOK.",
"section":"The arithmetic",
"tier":"system",
"source_ids": [
"s10",
"s16",
"s20",
"s23",
"s8"
],
"why_material":"The fee is meaningless without a denominator, and this is the arithmetic that decides which route a reader takes.",
"who_claims":"Opus 5 (Claude Code)",
"evidence_status":"observed + specified + externally attested"
},
{
"id":"c16",
"text":"Cloudflare charges a flat 5% on credit purchases while OpenRouter's published card fee is 5.5% with a $0.80 minimum, so the widely repeated claim that they charge the same 5% is close but not exact.",
"section":"The 5% lands on the money, not on the traffic",
"tier":"system",
"source_ids": [
"s1",
"s14",
"s9"
],
"why_material":"Two public statements disagree and the page has to publish both rather than pick one.",
"who_claims":"Opus 5 (Claude Code)",
"evidence_status":"specified + externally attested"
},
{
"id":"c17",
"text":"The report that /ai/v1/responses returns 402 on an authenticated gateway is open and uncommented on cloudflare/ai, and did not reproduce on this account with openai/gpt-4.1-mini on 2026-07-26.",
"section":"The 402 comes from one setting",
"tier":"mechanistic",
"source_ids": [
"s11",
"s24"
],
"why_material":"A reader hitting the same wall needs to know the report exists, is unresolved, and is not universally reproducible.",
"who_claims":"Opus 5 (Claude Code)",
"evidence_status":"observed + externally attested"
},
{
"id":"c18",
"text":"Removing the system message makes the OpenAI-shaped request succeed, and moving it to a top-level system field on /ai/v1/messages keeps the system prompt and succeeds.",
"section":"anthropic/* gets Anthropic's validation rules",
"tier":"system",
"source_ids": [
"s12",
"s13",
"s22"
],
"why_material":"Naming the failure without giving both working bodies leaves the reader exactly where they started.",
"who_claims":"Opus 5 (Claude Code)",
"evidence_status":"observed + externally attested"
},
{
"id":"c19",
"text":"Cloudflare's launch documentation carried no pricing on the model catalogue page, which is why operators publicly assumed a per-token markup that does not exist.",
"section":"The 5% lands on the money, not on the traffic",
"tier":"anecdotal",
"source_ids": [
"s15",
"s16"
],
"why_material":"It explains why the number is disputed at all and where a reader should look for it.",
"who_claims":"Opus 5 (Claude Code)",
"evidence_status":"externally attested"
},
{
"id":"c20",
"text":"Teams have merged changes that delete provider API keys from their code in favour of a single Cloudflare API token with AI Gateway Run permission.",
"section":"Five routes, ranked",
"tier":"anecdotal",
"source_ids": [
"s17",
"s18"
],
"why_material":"It is the evidence that the auth collapse, not the price, is what people adopt this for.",
"who_claims":"Opus 5 (Claude Code)",
"evidence_status":"externally attested"
},
{
"id":"c21",
"text":"A successful anthropic/* call on /ai/v1/chat/completions returns no content field and reports usage as input_tokens and output_tokens rather than OpenAI's prompt_tokens and completion_tokens.",
"section":"anthropic/* gets Anthropic's validation rules",
"tier":"system",
"source_ids": [
"s22"
],
"why_material":"A client that parses the OpenAI envelope will read an empty answer and a missing key without any error to explain it.",
"who_claims":"Opus 5 (Claude Code)",
"evidence_status":"observed"
},
{
"id":"c22",
"text":"Every Unified Billing log row carries wholesale true and byok null, which is how a request's billing lane is confirmed after the fact.",
"section":"What one billed request actually leaves behind",
"tier":"system",
"source_ids": [
"s23"
],
"why_material":"It is the only after-the-fact way to prove which credential paid for a given call.",
"who_claims":"Opus 5 (Claude Code)",
"evidence_status":"observed"
}
],
"widgets": [
{
"type":"stat",
"value":"5%",
"label":"fee on credits purchased, charged at top-up and not per request"
},
{
"type":"stat",
"value":"402",
"label":"status Unified Billing returns on a gateway with authentication off"
},
{
"type":"stat",
"value":"$0.000084",
"label":"logged cost of one 22-in / 4-out call to anthropic/claude-sonnet-5"
},
{
"type":"stat",
"value":"$3.60",
"label":"monthly fee on 100M input + 20M output tokens of openai/gpt-4.1-mini"
},
{
"type":"stat",
"value":"0",
"label":"provider API keys in the request path once Unified Billing is on"
},
{
"type":"note",
"title":"Turn authentication on before anything else",
"text":"A `provider/model` request at a gateway with `authentication: false` returns **HTTP 402**, code `2021`, no matter how valid the token or how loaded the balance. `@cf/` models on the same gateway keep returning 200, which makes it read as a model problem. It is one toggle in the gateway's **Settings**."
},
{
"type":"note",
"title":"The fee is on the money, not the traffic",
"text":"$100 of credits costs $105. Those credits then buy tokens at the provider's own published rate. Nothing you change per request moves the effective 5% — only the size and frequency of top-ups, and only because a card-fee floor does not exist here (it does at OpenRouter)."
},
{
"type":"note",
"title":"`anthropic/*` is not OpenAI-shaped, whatever the endpoint is called",
"text":"`/ai/v1/chat/completions` will accept the model id and then reject `role: \"system\"` in `messages[0]` with `Invalid option: expected one of \"user\"|\"assistant\"`. Even when it succeeds it returns no `content` field. Send `anthropic/*` to `/ai/v1/messages` with a top-level `system`."
},
{
"type":"note",
"title":"Zero data retention is off by default and only covers this lane",
"text":"`zdr` applies only to Unified Billing requests on Cloudflare-managed credentials, is supported for OpenAI and Anthropic only, and falls back to the standard configuration silently elsewhere. It does not turn off AI Gateway logging — that is a separate setting."
},
{
"type":"quote",
"text":"Free? They take the same 5% fee as OpenRouter does.",
"cite":"yencabulator, Hacker News, 2026-05-31"
}
],
"type":"article",
"updated":"2026-10-08T11:33:17.023Z",
"version":1,
"hash":"4cc430aa5fd8040baed74ff38d6fec34ea983387e1f5f243e1703b0b653d5dc3",
"styling": {
"scope":"shared",
"how":"no value on the page is set by hand: each comes from this theme, and each theme value becomes the CSS variable named beside it. PATCH http://google-preview.miscsubjects.com/api/jarticles/theme changes them.",
"widget_style": {
"what":"every widget may carry style: {…}; these are the only keys it reads",
"rotate":"degrees, a number",
"offset_x":"pixels, a number",
"offset_y":"pixels, a number",
"pulse":"true to pulse"
},
"theme": {
"color": {
"page_bg":"#f5f5f7",
"sheet_bg":"#ffffff",
"ink":"#1d1d1f",
"soft":"#424245",
"muted":"#6e6e73",
"dim":"#86868b",
"line":"#d2d2d7",
"accent":"#0071e3",
"accent_soft":"rgba(0,113,227,0.08)",
"link":"#0066cc",
"selection":"rgba(0,113,227,0.18)",
"widget_bg":"#f5f5f7",
"widget_border":"#d2d2d7",
"card_bg":"#ffffff",
"code_bg":"#f5f5f7",
"json_bg":"#161b19",
"json_ink":"#e6e1d4",
"json_key":"#7fd3c3",
"json_string":"#e9c46a",
"json_number":"#f4a261",
"json_bool":"#c792ea",
"json_null":"#928d7e",
"json_punct":"#6b6759",
"json_bar":"#0e1512",
"kind_badge_bg":"#0e6e60",
"kind_badge_ink":"#ffffff"
},
"color_dark": {
"page_bg":"#000000",
"sheet_bg":"#1d1d1f",
"ink":"#f5f5f7",
"soft":"#d2d2d7",
"muted":"#a1a1a6",
"dim":"#86868b",
"line":"#424245",
"accent":"#2997ff",
"accent_soft":"rgba(41,151,255,0.12)",
"link":"#2997ff",
"selection":"rgba(41,151,255,0.25)",
"widget_bg":"#2c2c2e",
"widget_border":"#424245",
"card_bg":"#1d1d1f",
"code_bg":"#2c2c2e",
"json_bg":"#0a0d0c",
"json_ink":"#e6e1d4",
"json_key":"#7fd3c3",
"json_string":"#e9c46a",
"json_number":"#f4a261",
"json_bool":"#c792ea",
"json_null":"#757062",
"json_punct":"#5d665f",
"json_bar":"#060807",
"kind_badge_bg":"#4fc1ab",
"kind_badge_ink":"#0b0f0d"
},
"font": {
"display":"-apple-system,BlinkMacSystemFont,\"SF Pro Display\",\"SF Pro Text\",\"Helvetica Neue\",Helvetica,Arial,sans-serif",
"body":"-apple-system,BlinkMacSystemFont,\"SF Pro Display\",\"SF Pro Text\",\"Helvetica Neue\",Helvetica,Arial,sans-serif",
"mono":"ui-monospace,SFMono-Regular,\"SF Mono\",Menlo,Consolas,monospace",
"google_fonts":""
},
"size": {
"body":"17px",
"small":"17px",
"eye":"17px",
"lead":"17px",
"h1":"52px",
"h2":"34px",
"h3":"22px",
"byline":"17px",
"h1_measure":"none",
"code":"17px",
"json":"17px",
"widget_title":"17px"
},
"leading": {
"body":"1.7",
"head":"1.18",
"lead":"1.5",
"json":"1.6"
},
"weight": {
"display":"700",
"head":"700",
"body":"400",
"strong":"700",
"eye":"700"
},
"tracking": {
"eye":"0.14em",
"display":"-0.03em",
"body":"-0.006em",
"badge":"0.08em"
},
"layout": {
"sheet_width":"1000px",
"text_width":"880px",
"gutter":"16px",
"sheet_padding":"56px 64px 64px",
"sheet_padding_mobile":"24px 18px 40px",
"sheet_margin":"28px auto 72px",
"block_gap":"1.15em",
"widget_gap":"28px",
"grid_gap":"14px"
},
"border": {
"sheet":"0px solid",
"sheet_radius":"30px",
"widget":"1px solid",
"widget_radius":"24px",
"card_radius":"24px",
"badge_radius":"999px",
"rule":"1px solid",
"code_radius":"18px",
"accent_bar":"3px"
},
"shadow": {
"sheet":"0 1px 1px rgba(0,0,0,.02),0 6px 26px rgba(0,0,0,.05)",
"widget":"none",
"card_hover":"0 10px 28px -16px rgba(28,27,23,0.35)"
},
"motion": {
"hover":"160ms ease"
}
}
},
"page":"http://google-preview.miscsubjects.com/a/cloudflare-unified-billing",
"json":"http://google-preview.miscsubjects.com/a/cloudflare-unified-billing.json",
"markdown":"http://google-preview.miscsubjects.com/a/cloudflare-unified-billing.md",
"theme":"http://google-preview.miscsubjects.com/article-theme.json",
"calls": [
{
"what":"this article as a page (no key)",
"method":"GET",
"url":"http://google-preview.miscsubjects.com/a/cloudflare-unified-billing",
"headers": {}
},
{
"what":"this article as its object (no key)",
"method":"GET",
"url":"http://google-preview.miscsubjects.com/a/cloudflare-unified-billing.json",
"headers": {}
},
{
"what":"this article as Markdown (no key)",
"method":"GET",
"url":"http://google-preview.miscsubjects.com/a/cloudflare-unified-billing.md",
"headers": {}
},
{
"what":"the object with its hash, drafts too",
"method":"GET",
"url":"http://google-preview.miscsubjects.com/api/jarticles/cloudflare-unified-billing",
"headers": {
"x-sheets-key":"$(cat /Users/cyrusmassoumi/.misc-sheets-key)"
}
},
{
"what":"one piece and that piece’s hash",
"method":"GET",
"url":"http://google-preview.miscsubjects.com/api/jarticles/cloudflare-unified-billing/at?path=facts",
"headers": {
"x-sheets-key":"$(cat /Users/cyrusmassoumi/.misc-sheets-key)"
}
},
{
"what":"grep: every key or value that matches, with the path to change it",
"method":"GET",
"url":"http://google-preview.miscsubjects.com/api/jarticles/cloudflare-unified-billing/grep?q=owner",
"headers": {
"x-sheets-key":"$(cat /Users/cyrusmassoumi/.misc-sheets-key)"
}
},
{
"what":"change pieces (set, merge, delete, append, insert, move); if-match is the hash",
"method":"PATCH",
"url":"http://google-preview.miscsubjects.com/api/jarticles/cloudflare-unified-billing",
"headers": {
"x-sheets-key":"$(cat /Users/cyrusmassoumi/.misc-sheets-key)",
"content-type":"application/json",
"if-match":"<hash from the read>"
},
"body": {
"ops": [
{
"op":"set",
"path":"facts.example",
"value":1
}
]
}
},
{
"what":"replace the whole object",
"method":"PUT",
"url":"http://google-preview.miscsubjects.com/api/jarticles/cloudflare-unified-billing",
"headers": {
"x-sheets-key":"$(cat /Users/cyrusmassoumi/.misc-sheets-key)",
"content-type":"application/json",
"if-match":"<hash>"
},
"body": {
"slug":"cloudflare-unified-billing",
"title":"…"
}
},
{
"what":"fetch every source, hash what came back and check the quote is in it",
"method":"POST",
"url":"http://google-preview.miscsubjects.com/api/jarticles/cloudflare-unified-billing/verify",
"headers": {
"x-sheets-key":"$(cat /Users/cyrusmassoumi/.misc-sheets-key)",
"content-type":"application/json"
},
"body": {}
},
{
"what":"an Obsidian-style command",
"method":"POST",
"url":"http://google-preview.miscsubjects.com/api/jarticles/cli",
"headers": {
"x-sheets-key":"$(cat /Users/cyrusmassoumi/.misc-sheets-key)",
"content-type":"application/json"
},
"body": {
"command":"backlinks",
"file":"cloudflare-unified-billing"
}
}
]
}