High Token Consumption — Full Curriculum

Personal study reference, not customer-facing. A practical playbook for finding where Workers AI / AI Gateway tokens go and cutting them without cutting quality — updated and re-verified against current docs (original was dated July 2026; several facts, a deprecated model, and a major new feature — Auto Router — have changed since). Every fact below is sourced from live Cloudflare docs (full list at the bottom).

Module 1How to use this & why AI Gateway matters

Six root causes, covered in the order they usually drive real dollar problems. For each: how to spot it in AI Gateway, how to fix it, and code to drop in.

Why AI Gateway matters here. You can't fix what you can't measure. AI Gateway is the observability layer that turns "our AI bill is high" into "request #47 spent 12,000 tokens because the system prompt was 8,000 tokens and we sent it 47 times today."

#Root causeTypical token wasteFix effort
1No response cache20–60% of tokens10 minutes
2Huge system prompt resent every request30–70% of input tokens (agent workloads)1 hour
3No max_tokens ceiling10–40% of output tokens15 minutes
4Wrong model for the jobCost multiplier of 10–50×1 day (eval work)
5Chatty conversation historyGrows linearly with turns, unbounded2 hours
6Retries & duplicate requests5–20% of all tokens10 minutes

Module 2Cause 1 — No response cache

The single fastest cost win. If any part of your app asks the model the same question more than once, every duplicate is 100% wasted spend.

How to detect it

AI Gateway → Analytics → Cached Requests percentage. Turn caching on for 24 hours and see what percentage of traffic is exactly-duplicate — that's your savings ceiling.

Honest caveat CF Docs

Caching is exact-match only. Two prompts differing by a single character are two cache entries. Semantic/similarity caching is still roadmap-only as of today — not shipped.

Fix — dashboard toggle

  1. AI → AI Gateway → your gateway → Settings
  2. Toggle Cache Responses on
  3. Set TTL — 3600s (1hr) is a good start. Range is 60s to 1 month. Note: a newly auto-created gateway defaults to caching OFF — don't assume it's on by default.

Fix — per-request in code

const resp = await env.AI.run(
  "@cf/meta/llama-3.1-8b-instruct-fast",
  { messages: [{ role: "user", content: prompt }] },
  {
    gateway: {
      id: "your-gateway-id",
      skipCache: false,
      cacheTtl: 3600,
      cacheKey: "optional-custom-key",   // not in the original playbook
      collectLog: true,                 // not in the original playbook
    },
  }
);

Header rename to know about: the per-request HTTP headers cf-cache-ttl and cf-skip-cache are now cf-aig-cache-ttl and cf-aig-skip-cache. Old names still work but are deprecated — use the new ones going forward.

Live proof. On cf-demo-app, a repeat identical RAG query: cold path 1,507ms → cached hit 144ms (10× faster), and zero tokens billed on the cached hit.

Module 3Cause 2 — Huge system prompt resent every request

The silent killer for agentic workloads. An 8,000-token system prompt sent 500 times a day is 4M input tokens before the user says a word.

How to detect it

AI Gateway → Logs → open any request's input body. System message over 500 tokens and identical across requests? That's you. Analytics' tokens-per-request average over 3,000 suggests a large shared prefix.

Good news CF Docs

Workers AI has prefix caching built in for supported models. Cached prefix tokens bill at a discounted rate and skip the prefill compute stage.

Fix — enable prefix caching with session affinity

const response = await env.AI.run(
  "@cf/moonshotai/kimi-k2.6",
  {
    messages: [
      { role: "system", content: LONG_SYSTEM_PROMPT },  // static, front-loaded
      { role: "user",   content: userQuery },           // dynamic, at the end
    ],
  },
  { extraHeaders: { "x-session-affinity": "ses_" + sessionId } }
);

Field-verification correction: the cached-token count is not a top-level usage.cached_tokens field — it's nested at usage.prompt_tokens_details.cached_tokens. Update any code checking the old shape.

Model list correction — kimi-k2.5 is deprecated (removed May 30, 2026). Current prefix-caching-supported models include kimi-k2.6, kimi-k2.7-code, glm-5.2, glm-5.3, glm-5.3-flash, and deepseek-v4-flash/pro. Notably, glm-4.7-flash has no cached-token pricing tier — don't claim it supports prefix-caching discounts.

Module 4Cause 3 — No max_tokens ceiling

Without max_tokens, the model generates until it decides to stop. On expensive models, a runaway response can cost 40× what the actual answer needed.

How to detect it

AI Gateway → Analytics → Output tokens per request. High variance (P50=200, P95=3,500) is the tell.

Rule of thumb

Set max_tokens to 1.5× the longest useful answer. Chat: 500–1000. Summaries: 300–500. Code: 1500–2500. Unbounded is a bug.

const resp = await env.AI.run(
  "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
  { messages: [...], max_tokens: 500, temperature: 0.3 }
);

Bonus tactic: add a length instruction to the system prompt — "Keep responses under 4 sentences unless explicitly asked for more." Costs zero extra tokens once cached.

Module 5Cause 4 — Wrong model for the job

Most workloads don't need a 200B-parameter model. Prices differ by 10–50× across the catalog.

Current verified Workers AI pricing CF Docs · Oct 2026

ModelInput $/MOutput $/MCached $/MGood for
llama-3.3-70b-instruct-fp8-fast$0.293$2.253—General reasoning, RAG, mid-complexity
glm-4.7-flash$0.060$0.400—Fast classification, routing, tool-calling
gpt-oss-20b$0.200$0.300—Cheap classification/routing alternative
glm-5.3-flash$0.150$0.500$0.030New — approaches larger-model coding/agentic quality at a fraction of the cost
glm-5.2$1.400$4.400$0.260Long-context reasoning, complex agents
glm-5.3$1.400$4.400$0.260New, 1M context agentic coding
kimi-k2.6$0.950$4.000$0.160New frontier model, replaces deprecated kimi-k2.5
kimi-k2.7-code$0.950$4.000$0.190Code generation, tool-use agents (now has concrete pricing — previously listed as "varies")
nemotron-3-120b-a12b$0.500$1.500—New, hybrid Mamba-transformer, built for multi-agent

One pricing discrepancy flagged for hands-on confirmation: @cf/meta/llama-3.1-8b-instruct-fast is confirmed still active (not deprecated) but does not appear in the current aggregate pricing table — only the -fp8-fast variant does, at $0.045 in / $0.384 out. Verify this specific model's live price in the dashboard before quoting it to a customer.

Deprecated since the original playbook: kimi-k2.5 (May 30, 2026) plus several older base models — llama-3-8b-instruct(-awq), llama-3.1-8b-instruct(-awq), llama-3.1-70b-instruct, llama-2-7b-chat variants, mistral-7b-instruct-v0.1, gemma-7b-it, gemma-3-12b-it, phi-2. The -fast and -lora suffixed variants were explicitly preserved.

Fix — model tiering pattern (route by task complexity)

// 1. Cheap classifier call
const route = await env.AI.run(
  "@cf/zai-org/glm-4.7-flash",
  {
    messages: [
      { role: "system", content: "Classify this request as one of: SIMPLE, COMPLEX, CODE. Respond with only the word." },
      { role: "user", content: userQuery },
    ],
    max_tokens: 5,
  }
);

// 2. Route to the right-sized model
const model = route.response.includes("CODE")    ? "@cf/moonshotai/kimi-k2.7-code"
            : route.response.includes("COMPLEX")  ? "@cf/meta/llama-3.3-70b-instruct-fp8-fast"
            :                                        "@cf/zai-org/glm-4.7-flash";

const answer = await env.AI.run(model, { messages: [...] });

New since the original playbook — Auto Router. AI Gateway now ships a built-in cloudflare/auto model string that automatically classifies task difficulty and routes to the cheapest model that can handle it, with session affinity (cf-aig-session-id) to preserve prompt-cache hits across a multi-turn session. This is a purpose-built, zero-code-maintenance replacement for the manual classifier-routing pattern above — worth leading with instead of hand-rolling the pattern for new builds.

Don't skip evals. Build a labelled test set of 50–100 real production requests with known-good answers before swapping models. AI Gateway logs give you the production data to build that set — export requests, replay against the cheaper model, compare.

Module 6Cause 5 — Chatty conversation history

Naive chat implementations send the entire conversation history every turn. Input tokens grow linearly, cost grows quadratically. Users don't feel it until the bill arrives.

How to detect it

AI Gateway → Logs, sort by input tokens descending. Top requests at 20k+ input tokens with the same "hello" from 40 turns ago still in the list? That's you.

Anti-pattern

Blindly appending every user + assistant message to a list forever, then sending the whole list every call.

Fix — cap the window with a rolling summary

const RECENT_TURNS = 6;
const older = history.slice(0, -RECENT_TURNS);
const recent = history.slice(-RECENT_TURNS);

let summary = "";
if (older.length > 0) {
  summary = await summarizeOnce(older);  // cheap model, ~300 tokens out
}

const messages = [
  { role: "system", content: SYSTEM_PROMPT },
  ...(summary ? [{ role: "system", content: "Prior context: " + summary }] : []),
  ...recent,
  { role: "user", content: userQuery },
];

Compounds well with Cause 2. Keep the system prompt stable and recent turns at the end — prefix caching kicks in for the shared history, saving on both prompt token count and cached-tier pricing for what remains.

Module 7Cause 6 — Silent retries & duplicates

Client retries on timeout, frontend double-clicks, queue redelivery — all bill twice. 5–20% of AI spend on undisciplined codebases is duplicates.

How to detect it

AI Gateway → Logs, filter by user/session ID, look for identical prompts within seconds of each other. Or compare request count to unique-user count in Analytics.

Fix at two layers

Layer 1: response cache (Cause 1) turns the duplicate into a free cache hit. Layer 2: app-level idempotency — dedupe by request hash before calling the gateway.

Fix — rate limiting as the seatbelt

AI Gateway Settings → Rate Limiting → set a per-token or per-IP cap. Won't fix bad code, but stops a runaway loop from becoming a runaway bill.

Fix — centralize retries at the gateway

AI Gateway Settings → Retry Requests → configure backoff (current defaults: up to 5 retries, 0.1–60s delay, Constant/Linear/Exponential backoff). Then remove the retry loop from client code — one place to reason about retries beats a dozen scattered implementations.

Module 8Settings map & what's new since July

Which setting fixes which cause

SettingFixes causeRecommended default
Collect LogsAll 6 — diagnosis foundationON
Cache Responses#1, #6ON, TTL 3600s (off by default on new gateways)
Rate Limiting#6 — blast-radius capStart at 100 req/min per token, tune
Spend LimitsAll — hard budget stopSet to 120% of expected monthly; scope by cf.user_id or custom metadata
Retry Requests#6 — centralize retries2 retries, exponential backoff
Authenticated GatewayStops unauthorized traffic from spending your tokensON

Spend Limits is now GA — it launched as Beta June 2026 but no longer carries a Beta label as of the current Features page, now listed as a standard Cost Optimization feature. Max 20 rules per gateway; default breach behavior is a 429 block, with an optional fallback-to-cheaper-model via Dynamic Routing.

Metadata & per-user limits — a key name correction

await env.AI.run(
  "@cf/mistral/mistral-7b-instruct-v0.1",
  { prompt: "..." },
  { gateway: { id: "gateway_id", metadata: { team: "AI", user_id: 12345 } } }
);

Max 5 metadata entries per request; values must be string/number/boolean, no nested objects. If the gateway sits behind Cloudflare Access, the authenticated user ID is automatically attached as the reserved key cf.user_id — you don't need to pass it yourself, and customer-supplied keys starting with cf. are stripped. Without Access, pick your own key (e.g. user_id) and reference that exact name in Dynamic Route conditions / Spend Limit dimensions.

What's genuinely new since July 2026

FeatureWhy it matters for cost
Auto Router (cloudflare/auto) NewBuilt-in difficulty-aware routing to the cheapest capable model, with session affinity to preserve prompt-cache hits — see Module 5
User Insights — "Potential Savings" view NewDirectly flags which requests could use a cheaper/faster model without quality loss — free, zero setup, probably the single most relevant new cost feature
Workers AI + AI Gateway billing unification NewWorkers AI can now bill from prepaid AI Gateway credits; frontier models get 50 RPM (vs. 20 RPM standard) when paid via credits — changes capacity planning math
Custom Costs — cache-read/write rates NewNeeded if you've negotiated cache pricing directly with a provider
Web Search API BetaLets agents ground responses instead of burning tokens on hallucination-correction loops

Module 9Field notes — Dynamic & Fallback Routing (Beta)

Hands-on notes from Fallback and Dynamic Routing. Live demo: cf-demo-app.dustinburke23nc.workers.dev/ai-gateway.

What these are. Fallback Routing defines a chain of models — if the primary fails or rate-limits, the gateway automatically tries the next one. Dynamic Routing is the visual pipeline builder composing Rate Limit, Budget Limit, Model, and Fallback nodes into a route. Both map directly onto Cause 6's "seatbelt" pattern. Neither carries a Beta label on the current Features page anymore — treat as likely-GA, though no explicit "now GA" changelog post was found.

What worked (as of original testing)

Basic Fallback. Solid — if the primary model drops offline, the gateway routes to the fallback with no client-side change.

Per-user rate limiting on metadata. Worked once caching was off. In the demo, a heavy user gets blocked after their rate ceiling while a separate user is unaffected — the gateway blocks blast-radius requests before they reach any provider, so blocked calls cost zero tokens.

Bugs flagged at the time — retest live before relying on these

Important: these three issues were observed in July 2026. They are implementation details of a beta visual editor, not documented API contracts — current docs can't confirm whether they're fixed. The surrounding ecosystem (Spend Limits going GA, Auto Router shipping, standardized Dynamic Routing docs updated as recently as Oct 2, 2026) has matured substantially since. Retest directly in the dashboard before citing these as current in a customer conversation.

How to position this today

Customer questionHonest answer
"Can we set a hard dollar cap?"Yes — use Spend Limits (now GA) as the real hard cap on the platform. Use Dynamic Routing's Rate Limit node for request-count guardrails specifically.
"Can we downgrade to a cheaper model when a rate limit hits?"Verify live — this had a known UI bug as of mid-2026. If still broken, position it as "we block the abuser at the gateway and save the LLM cost" instead, which is arguably the better cost story anyway.
"Does caching still work alongside routing?"Yes, and that's desirable — cache hits cost nothing. Confirm live whether cached requests still bypass rate-limit counters before promising exact behavior.
"Is this production-ready?"Fallback Routing: yes. Dynamic Routing: treat as mature but verify current UI behavior before a live customer demo, given known July-era rough edges.

Module 10Quick-start checklist & honest caveats

First 30 minutes

If nothing else after this call, do these six things in order. ~30 minutes total. Expected cost reduction on a typical noisy workload: 30–50%.

  1. Turn on AI Gateway — 2 min. AI → AI Gateway → Create gateway.
  2. Route Workers AI calls through it — 5 min. Add gateway: { id: "..." } to every env.AI.run() call.
  3. Turn on Cache Responses — 1 min. Settings → toggle on, TTL 3600s (remember: off by default).
  4. Set max_tokens on every call — 10 min. Search the codebase for env.AI.run, add a ceiling to each.
  5. Add x-session-affinity to agent workloads — 10 min. Stable long system prompts become 30–70% cheaper.
  6. Set a Spend Limit — 2 min. Settings → Spend Limits → 120% of current monthly. The seatbelt.

After 24 hours of production traffic, check Analytics. Cached % should be non-zero, average input tokens lower than yesterday.

Honest caveats — what this playbook won't do

Self-testQuiz

Click a question to reveal the answer. No peeking before you commit to one.

1. What's the single fastest cost win covered in this playbook, and what's its real ceiling?

Turning on response caching (Cause 1). Its ceiling: caching is exact-match only — two prompts differing by a single character are two separate cache entries. Semantic caching is still roadmap-only, not shipped.

2. True or false: a newly auto-created AI Gateway has response caching enabled by default.

False — caching defaults to OFF on a new gateway. Don't assume it's already saving money without checking Settings.

3. Where does the actual cached-token count appear in the `usage` response object — and what's the common mistake?

It's nested at usage.prompt_tokens_details.cached_tokens, not a top-level usage.cached_tokens field. Checking the wrong path will always read as zero/undefined even when caching is working.

4. Which model mentioned in the original July 2026 version of this playbook has since been deprecated, and what replaced it?

kimi-k2.5, deprecated May 30, 2026 — replaced by kimi-k2.6 and kimi-k2.7-code.

5. Does `glm-4.7-flash` support prefix-caching discounts like the larger GLM models do?

No — it has no published cached-token pricing tier, unlike glm-5.2/glm-5.3/kimi-k2.6/kimi-k2.7-code. Don't claim prefix-caching savings for it.

6. What is Auto Router, and why wasn't it in the original playbook?

A built-in cloudflare/auto model string that automatically classifies task difficulty and routes to the cheapest capable model, with session affinity to preserve prompt-cache hits. It's a feature that shipped after the original July 2026 playbook was written — it replaces the need to hand-roll a classifier-based model-tiering pattern.

7. Is "Spend Limits" still a Beta feature?

No — it launched as Beta in June 2026 but no longer carries a Beta label on the current Features page; it's treated as GA.

8. If a gateway sits behind Cloudflare Access, how does per-user metadata get attached for rate limiting / spend limits — and what's the gotcha?

The authenticated user ID is automatically attached as the reserved key cf.user_id — you don't pass it yourself. The gotcha: keys starting with cf. are reserved, so a customer-supplied metadata field named cf.user_id would be silently stripped.

9. What's the actual mechanism for enabling Workers AI prefix caching on a request?

Send the x-session-affinity header with a stable session/agent identifier via extraHeaders in the env.AI.run() call options — not a dashboard toggle.

10. Name two per-request options in the `gateway: {}` object beyond `id`, `skipCache`, and `cacheTtl`.

Any two of: cacheKey (custom cache key), collectLog (boolean), metadata (object, max 5 entries).

11. What's the current correct name for the per-request header that used to be called `cf-skip-cache`?

cf-aig-skip-cache (and cf-cache-ttl → cf-aig-cache-ttl). Old names still function but are deprecated.

12. A customer asks for a hard dollar cap on their AI spend. Which feature is the right answer, and which feature is NOT the right tool for that specific job?

Spend Limits is the real hard budget cap. Dynamic Routing's Rate Limit node is for request-count guardrails, not dollar caps — don't conflate the two.

13. What were the three specific Dynamic/Fallback Routing bugs flagged in the original playbook, and what's the correct way to describe their current status?

(1) Budget Limit node only accepted whole-dollar integers, (2) a Rate Limit node's fallback-model branch kept reverting on save, (3) cached responses bypassed the Rate Limit counter entirely. Correct current framing: these can't be confirmed fixed or still-broken from docs alone — the beta UI has matured significantly since, so retest live before citing them as current in a customer conversation.

14. Why does capping conversation history (Cause 5) compound well with system-prompt prefix caching (Cause 2)?

If the system prompt stays stable and only recent turns are appended at the end, prefix caching can still match the shared, unchanged prefix — saving both on raw prompt token count (fewer history tokens) and on cached-tier pricing for what remains.

15. What's the real quantitative mistake in assuming cost-saving techniques stack additively?

They're multiplicative on the remaining cost, not additive on the percentage. A 30% reduction plus a 20% reduction compounds to about 44% combined (0.7 × 0.8 = 0.56, i.e. 44% saved), not 50%.

16. What new free, zero-setup AI Gateway feature directly flags which requests could use a cheaper model without quality loss?

The User Insights "Potential Savings" view — arguably the most directly relevant new cost-optimization feature to ship since the original playbook.

17. What changed about Workers AI frontier-model rate limits when billed through AI Gateway prepaid credits?

Frontier models get 50 RPM when paid via AI Gateway credits, versus the standard 20 RPM — a direct result of the Workers AI + AI Gateway billing unification. Relevant for capacity planning on high-throughput agentic workloads.

Sources AI Gateway Caching Worker binding methods (gateway options) Workers AI Prompt/Prefix Caching Workers AI Pricing AI Gateway overview AI Gateway features overview Rate Limiting Spend Limits Manage gateway (Retry Requests) Authenticated Gateway Custom metadata & cf.user_id Collect Logs / collectLog Model deprecations (May 2026) Spend Limits launch (Beta, June 2026) Kimi K2.7-code model page