Cost, Tokens, Caching, and Batch Processing

Learning Objectives

1. Token Budgeting and Cost Modeling

Pre-Section Check

Across every current Claude model, how do output token prices relate to input token prices?

Output and input cost the same per token Output costs 5× the input rate Input costs 5× the output rate Output costs 2× the input rate

When prompt caching is active, what does usage.input_tokens actually report?

The full size of the entire prompt Only the tokens written to the cache this request Only the uncached remainder after the last cache breakpoint The sum of input plus output tokens

You need to forecast a prompt's input cost before running it. Which approach is correct?

Use OpenAI's tiktoken library as a close-enough estimate Use the free count_tokens endpoint against the exact model you'll use Run the request once and read output_tokens Divide the character count by four

Before you can optimize cost, you have to measure it. A token is the unit into which text is broken before the model processes it — roughly three-quarters of an English word. Claude's pricing is quoted per million tokens (MTok), and the single most important fact is that input and output tokens are priced differently: output consistently costs five times as much as input.

Key Points

Standard (non-cached, non-batched) pricing

ModelInput $/MTokOutput $/MTokOutput/Input ratio
Claude Fable 5$10.00$50.00
Claude Opus 4.8$5.00$25.00
Claude Sonnet 5 (intro, through Aug 31 2026)$2.00$10.00
Claude Sonnet 5 (from Sept 1 2026)$3.00$15.00
Claude Haiku 4.5$1.00$5.00

The pattern is that output costs five times as much as input across every model. Two design implications follow: bound your output (right-size max_tokens so a runaway generation can't cost 5× what you expected), and don't over-fear input (large context is comparatively cheap, and caching makes repeated input cheaper still).

The four token buckets in usage

FieldMeaningBilled at
input_tokensUncached prompt tokens (remainder after any cache breakpoint)Base input rate
output_tokensTokens the model generatedOutput rate
cache_creation_input_tokensTokens written to cache this request~1.25× or 2× input
cache_read_input_tokensTokens served from cache this request~0.1× input

The crucial subtlety: input_tokens is not the total prompt size when caching is in play — it is only the uncached remainder after the last breakpoint. To reconstruct full prompt size, add all three input buckets. A simple cost forecast for 100,000 support-ticket classifications on Haiku 4.5 (~400-token input, ~20-token output each): input = 100,000 × 400 × ($1.00/1M) = $40; output = 100,000 × 20 × ($5.00/1M) = $10; total realtime, no caching = $50. This baseline is what caching and batching drive downward.

Animation slot: interactive cost calculator — slide input/output token counts and model to watch the per-request bill (and the 5× output multiplier) update live.
Post-Section Check

Across every current Claude model, how do output token prices relate to input token prices?

Output and input cost the same per token Output costs 5× the input rate Input costs 5× the output rate Output costs 2× the input rate

When prompt caching is active, what does usage.input_tokens actually report?

The full size of the entire prompt Only the tokens written to the cache this request Only the uncached remainder after the last cache breakpoint The sum of input plus output tokens

You need to forecast a prompt's input cost before running it. Which approach is correct?

Use OpenAI's tiktoken library as a close-enough estimate Use the free count_tokens endpoint against the exact model you'll use Run the request once and read output_tokens Divide the character count by four

2. Prompt Caching

Pre-Section Check

What is the fundamental invariant that prompt caching relies on?

A fuzzy semantic match on prompt meaning An exact prefix (byte) match up to each breakpoint A match on the final user message only A match on total prompt token count

Using a 5-minute TTL (write 1.25×, read 0.1×), after how many requests does caching a shared prefix begin to pay off versus not caching?

After just 2 requests After 3 requests After 5 requests Only after 10 or more requests

Repeated, seemingly-identical requests show cache_read_input_tokens = 0. What is the most likely cause?

The output was too long A silent invalidator (e.g. datetime.now() or a UUID) sits in the cached prefix The API raised a caching error that was ignored Caching only works on the second call, never later ones

Where should the volatile part of a prompt (the user's current question, a timestamp) be placed to maximize cache hits?

Before the cache_control breakpoint, in the system prompt After the last cache_control breakpoint In the tool definitions, which render first It doesn't matter where volatile content goes

Most production prompts are mostly repetition. Prompt caching lets Claude reuse identical leading segments across requests: you pay full price once and roughly a tenth of the price every time after. The whole mechanism rests on one invariant — caching is an exact prefix match; any byte that changes at or before a breakpoint invalidates that entry and every entry after it. Prompts always render in the fixed order tools → system → messages.

Key Points

Cache pricing on Opus 4.8 ($5/MTok base input)

CategoryMultiplierPrice per MTok
Base input$5.00
5-minute cache write1.25×$6.25
1-hour cache write$10.00
Cache read (hit)~0.1×$0.50
Output$25.00

A cache hit costs about 90% less than reprocessing the same tokens. But a cache write costs slightly more than a plain request, so caching is a bet: you pay a small premium to write, and win only if enough reads land before the entry expires. ephemeral is the only supported cache type.

Figure 4.1: Fixed prompt render order and where a cache prefix ends

flowchart LR T["tools (rendered first)"] --> S["system prompt"] S --> BP{{"cache_control breakpoint"}} BP -->|"caches tools + system as one prefix"| CACHE["Cached prefix (exact-byte match)"] BP --> M["messages (volatile: user's question)"] M --> UNCACHED["Billed as input_tokens each request"] CACHE -.->|"hit on next request"| READ["cache_read_input_tokens (~0.1x)"]

Figure 4.2: Prompt cache read/write decision path per request

flowchart TD START["Request arrives with cache_control prefix"] --> MIN{"Prefix >= model minimum? (1024-4096 tok)"} MIN -->|No| NOCACHE["No caching (silent): both cache buckets = 0"] MIN -->|Yes| HIT{"Matching entry still live in cache?"} HIT -->|"Miss (first request or TTL expired)"| WRITE["Cache WRITE: 1.25x (5-min) or 2x (1-hour)"] HIT -->|Hit| READ["Cache READ: ~0.1x (about 90% cheaper)"] WRITE --> TTL["Entry lives for TTL (5 min or 1 hour)"] READ --> TTL

Break-even worked example

A 10,000-token contract, asked 5 questions on Opus 4.8. Without caching: 5 × 10,000 × ($5/1M) = $0.250. With 5-minute caching: request 1 writes (10,000 × $6.25/1M = $0.0625), requests 2–5 read (4 × 10,000 × $0.50/1M = $0.0200) = $0.0825 — a 67% cut on repeated input. Output is unaffected; caching applies to input only.

Common silent invalidators

Silent invalidatorWhy it breaks the cache
datetime.now() / Date.now() in the system promptPrefix changes every request
A UUID or request ID early in the contentEvery request is unique
json.dumps(d) without sort_keys=TrueNon-deterministic key order → different bytes
A session/user ID interpolated into the system promptPer-user prefix; no cross-user sharing
A tool set that varies per userTools render first; nothing caches across users
Animation slot: prefix-diff visualizer — highlight the byte that changed between two requests and show the cache entry (and everything after it) invalidating in real time.
Post-Section Check

What is the fundamental invariant that prompt caching relies on?

A fuzzy semantic match on prompt meaning An exact prefix (byte) match up to each breakpoint A match on the final user message only A match on total prompt token count

Using a 5-minute TTL (write 1.25×, read 0.1×), after how many requests does caching a shared prefix begin to pay off versus not caching?

After just 2 requests After 3 requests After 5 requests Only after 10 or more requests

Repeated, seemingly-identical requests show cache_read_input_tokens = 0. What is the most likely cause?

The output was too long A silent invalidator (e.g. datetime.now() or a UUID) sits in the cached prefix The API raised a caching error that was ignored Caching only works on the second call, never later ones

Where should the volatile part of a prompt (the user's current question, a timestamp) be placed to maximize cache hits?

Before the cache_control breakpoint, in the system prompt After the last cache_control breakpoint In the tool definitions, which render first It doesn't matter where volatile content goes

3. Batch Processing

Pre-Section Check

What is the pricing benefit of the Message Batches API, and its main cost?

50% of standard prices, in exchange for up-to-24-hour latency 90% off, but no streaming and no caching allowed Free processing, capped at 1,000 requests 25% off, with guaranteed sub-second latency

How should you match a batch result back to the request that produced it?

By the position/index in the results list By the custom_id on each request By the order requests were submitted By the timestamp on each result

Which batch result type is the only one that is billed?

expired canceled succeeded errored

A batch shares a large 8,000-token context and runs for several minutes. Which caching choice is recommended, and why?

No caching — caching is disabled inside batches The 1-hour TTL, because a batch can easily run longer than 5 minutes The 5-minute TTL, because it is always cheaper Caching only the output tokens

Batch processing attacks cost from a different angle than caching: it trades latency for a flat 50% discount. If a workload doesn't need answers right now, hand the requests to the platform, let it process them asynchronously, and pay half. The Message Batches API (POST /v1/messages/batches) takes a list of requests, each with a unique custom_id, and you poll for completion.

Key Points

Figure 4.3: Message Batches API lifecycle

stateDiagram-v2 [*] --> in_progress: create (up to 100k requests / 256 MB) in_progress --> ended: all requests complete in_progress --> ended: 24-hour window elapsed in_progress --> canceling: cancel requested canceling --> ended ended --> results: poll processing_status == "ended" results --> [*]: retained 29 days from creation note right of results Per-request result type: succeeded (billed) | errored | canceled | expired (not billed) Match by custom_id, never by position end note

Batch pricing (50% of standard)

ModelBatch Input $/MTokBatch Output $/MTok
Claude Fable 5$5.00$25.00
Claude Opus 4.8$2.50$12.50
Claude Sonnet 5 (intro)$1.00$5.00
Claude Sonnet 5 (from Sept 1 2026)$1.50$7.50
Claude Haiku 4.5$0.50$2.50

Our 100,000-classification job that cost $50 realtime becomes: input 100,000 × 400 × ($0.50/1M) = $20; output 100,000 × 20 × ($2.50/1M) = $5; total batch = $25 — exactly half. The only cost is that answers may take up to (but rarely) 24 hours instead of seconds. The defining constraint is the 24-hour processing window: results are guaranteed when all requests complete or after 24 hours, whichever comes first — a batch that doesn't finish in 24 hours expires, and expired requests are not billed.

Result types

Result typeMeaningBilled?
succeededCompleted; includes the message resultYes
erroredInvalid request or internal server errorNo
canceledCanceled before being sent to the modelNo
expiredHit the 24-hour limit before runningNo
Animation slot: batch lifecycle timeline — submit 100k requests, watch them return out-of-order and get reassembled by custom_id, with the running 50%-off bill ticking up only for succeeded results.
Post-Section Check

What is the pricing benefit of the Message Batches API, and its main cost?

50% of standard prices, in exchange for up-to-24-hour latency 90% off, but no streaming and no caching allowed Free processing, capped at 1,000 requests 25% off, with guaranteed sub-second latency

How should you match a batch result back to the request that produced it?

By the position/index in the results list By the custom_id on each request By the order requests were submitted By the timestamp on each result

Which batch result type is the only one that is billed?

expired canceled succeeded errored

A batch shares a large 8,000-token context and runs for several minutes. Which caching choice is recommended, and why?

No caching — caching is disabled inside batches The 1-hour TTL, because a batch can easily run longer than 5 minutes The 5-minute TTL, because it is always cheaper Caching only the output tokens

4. Optimization Patterns

Pre-Section Check

Why do caching, batching, and model choice compound rather than overlap?

They all reduce output tokens in the same way Each attacks a different cost axis: repeated cost, all cost, and per-token cost Only one can be active at a time, so you pick the biggest They only compound on the Haiku model

You fire N identical-prefix requests in parallel expecting cache reads. What actually happens?

All N read the cache and pay the read rate All N pay the full write price — none can read what the others are still writing Only the last request pays; the rest are free The API rejects concurrent identical prefixes

What is the single most cost-effective first move before applying any caching or batching?

Always upgrade to the most capable model Cut the tokens you don't need — the cheapest token is the one never sent Enable a 1-hour cache on every request Disable max_tokens so responses aren't truncated

In production, how do you catch a regression where a refactor silently broke caching?

Wait for the monthly invoice to spike Log the usage object and alert when cache_read_input_tokens trends toward zero Re-run tiktoken on every prompt Nothing — silent invalidators raise errors you'll see in logs

The three levers are most powerful in combination. Caching removes repeated cost, batching halves all cost, and model choice lowers the per-token cost — different axes, so they multiply. The engineering discipline is applying each where it fits: cache the shared rulebook (identical across requests), batch the whole run (nobody's waiting), and route by difficulty (don't pay Opus prices for a Haiku task).

Key Points

Stacking optimizations on a 10,000-document pipeline (8,000-token rulebook, Opus 4.8)

ConfigurationEffect on the shared rulebook
Naive: realtime, no caching, Opus 4.8Full input price × 10,000
+ Caching: cache the rulebookFirst request writes; the rest read at ~0.1×
+ Batching: run the whole set as one batchAnother 50% off input and output
+ Model choice: route simple docs to Haiku 4.55× cheaper input, 5× cheaper output than Opus

Figure 4.4: Choosing realtime vs. batch processing

flowchart TD Q1{"Is a human or user-facing system waiting on the response?"} Q1 -->|Yes| RT["Realtime Messages API (seconds; streaming supported)"] Q1 -->|No| Q2{"Can it tolerate up to 24h latency? (usually < 1 hour)"} Q2 -->|No| RT Q2 -->|Yes| BATCH["Message Batches API (50% of standard price)"] BATCH --> USES["Evals, bulk generation, moderation, data analysis"] RT --> USES2["Chat, autocomplete, voice, interactive apps"]

Realtime vs. batch at a glance

DimensionRealtime (Messages API)Batch (Message Batches API)
LatencySecondsUp to 24h (usually < 1 hour)
CostStandard50% of standard
Best forInteractive chat, voice, anything a human waits onEvals, moderation, bulk generation, data analysis
StreamingSupportedNot supported (stream: true rejected)
Rate limitsStandard Messages limitsSeparate; does not affect Messages quota
Animation slot: optimization-stacking bar chart — start from the naive Opus bill and watch each lever (caching, batching, model routing) shave the bar down, showing the compounding fraction of the original cost.
Post-Section Check

Why do caching, batching, and model choice compound rather than overlap?

They all reduce output tokens in the same way Each attacks a different cost axis: repeated cost, all cost, and per-token cost Only one can be active at a time, so you pick the biggest They only compound on the Haiku model

You fire N identical-prefix requests in parallel expecting cache reads. What actually happens?

All N read the cache and pay the read rate All N pay the full write price — none can read what the others are still writing Only the last request pays; the rest are free The API rejects concurrent identical prefixes

What is the single most cost-effective first move before applying any caching or batching?

Always upgrade to the most capable model Cut the tokens you don't need — the cheapest token is the one never sent Enable a 1-hour cache on every request Disable max_tokens so responses aren't truncated

In production, how do you catch a regression where a refactor silently broke caching?

Wait for the monthly invoice to spike Log the usage object and alert when cache_read_input_tokens trends toward zero Re-run tiktoken on every prompt Nothing — silent invalidators raise errors you'll see in logs

Your Progress

Answer Explanations