The anatomy of a usage block
TL;DR. Every API response carries a usage object that is the ground truth of what you were
billed, and most people misread its most important field. input_tokens is not your prompt size; it
is only the uncached remainder. The full prompt is input_tokens + cache_creation_input_tokens + cache_read_input_tokens, each priced at a different rate (1x, 1.25x or 2x, 0.1x), plus
output_tokens at five times the input rate. This chapter dissects every field, prices a six-turn
agent session block by block with a runnable lab, and shows the same fields inside a real Claude
Code transcript, so a usage block stops being a mystery number and becomes something you can predict
before the response arrives.
Contents
- Where the usage block lives
- The four core fields, and the identity
- The lab: a six-turn session, priced block by block
- The rest of the fields, from a real block
- count_tokens: the pre-flight instrument
- Usage under streaming
- Further reading
- Takeaways
Chapter 2 established what a token costs and
Chapter 6 established why cached tokens cost a tenth as much.
This chapter is where those prices meet the wire: the usage object the API attaches to every
single response. It is the receipt for one request, the input to every cost dashboard, and the
number that /usage, ccusage, and every observability tool ultimately aggregates. If you can
read one usage block precisely, everything in the measurement chapters that follow is just
summing them.
Where the usage block lives
Every call to the Messages API returns a Message object, and every Message carries usage:
# Illustrative: requires the anthropic SDK and an API key.
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
system=[{"type": "text", "text": BIG_SYSTEM_PROMPT,
"cache_control": {"type": "ephemeral"}}],
messages=[{"role": "user", "content": "Summarize the design doc."}],
)
print(response.usage)
The same object shows up in more places than the SDK. It is written verbatim into Claude Code's transcript files for every assistant turn (Chapter 25 mines those), it arrives inside streaming events, and batch results carry one per request. Anywhere tokens were billed, this block is the record.
The four core fields, and the identity
| Field | What it counts | Price (opus-4-8) |
|---|---|---|
input_tokens | Prompt tokens processed at the full input rate: everything not covered by the cache | $5.00 / Mtok (1x) |
cache_creation_input_tokens | Prompt tokens written into the prompt cache this request | 1.25x (5-minute TTL) or 2x (1-hour TTL) |
cache_read_input_tokens | Prompt tokens served from the cache | 0.1x |
output_tokens | Tokens the model wrote, including any thinking | $25.00 / Mtok (5x) |
The identity that makes the input side make sense:
$$\text{full prompt size} = \text{input} + \text{cache_creation} + \text{cache_read}$$
Every prompt token lands in exactly one of the three buckets. Which bucket depends entirely on the
cache state and the breakpoint placement from Chapter 6: tokens
covered by a warm cache entry are cache_read, tokens newly stored are cache_creation, and
whatever sits after the last breakpoint (or missed the cache) is plain input.
Don't be confused.
input_tokensdoes not mean "how big my prompt was". An agent two hours into a session can showinput_tokens: 118on a 200,000-token prompt, because 199,882 of those tokens were served from cache. If you monitor prompt growth, alert on the sum of all three fields. If you monitor cost, weight each field by its own rate. Readinginput_tokensalone gives you a number that is simultaneously too small to be the prompt and too crude to be the bill.
The cost of one block is therefore a four-term formula, not a two-term one:
$$\text{cost} = \text{in} \cdot r + \text{write}{5m} \cdot 1.25r + \text{write}{1h} \cdot 2r + \text{read} \cdot 0.1r + \text{out} \cdot 5r$$
where $r$ is the model's per-token input rate, and the write terms come from the cache_creation
sub-object described below.
The lab: a six-turn session, priced block by block
The lab rebuilds this accounting for a small agent session: a 14,000-token stable prefix, six turns, a cache breakpoint on the latest turn (the standard multi-turn pattern from Chapter 6). It emits one usage block per turn and prices each with the formula above.
"""The anatomy of a usage block, from scratch.
Every response from the Anthropic API carries a `usage` object that is the
ground truth of what you were billed. This lab rebuilds that accounting for a
small multi-turn agent session, turn by turn, so each field stops being a
mystery number and becomes something you can predict before the response
arrives.
The four core fields, and the one identity that makes them make sense:
input_tokens tokens processed at the FULL input rate
cache_creation_input_tokens tokens WRITTEN to the prompt cache (premium)
cache_read_input_tokens tokens READ from the prompt cache (~0.1x)
output_tokens tokens the model wrote (the 5x-priced half)
full prompt size = input_tokens + cache_creation_input_tokens
+ cache_read_input_tokens
That identity is the single most misread thing in the API. `input_tokens` is
NOT "how big my prompt was"; it is only the uncached remainder. An agent that
has been running for an hour can show input_tokens=118 on a 200,000-token
prompt, because the other 199,882 were served from cache.
Newer responses also break the cache write down by TTL:
cache_creation.ephemeral_5m_input_tokens written with the 5-minute TTL (1.25x)
cache_creation.ephemeral_1h_input_tokens written with the 1-hour TTL (2x)
Run with the standard library only.
"""
import json
# Real Anthropic prices, per token (claude-opus-4-8: $5 in / $25 out per 1M).
IN_RATE = 5.00 / 1_000_000
OUT_RATE = 25.00 / 1_000_000
READ_RATE = IN_RATE * 0.10 # a cache hit costs ~0.1x the input rate
WRITE_5M_RATE = IN_RATE * 1.25 # writing with the default 5-minute TTL
WRITE_1H_RATE = IN_RATE * 2.00 # writing with the 1-hour TTL
def cost_of(usage):
"""Price one usage block, the same way the bill does."""
w5 = usage["cache_creation"]["ephemeral_5m_input_tokens"]
w1 = usage["cache_creation"]["ephemeral_1h_input_tokens"]
return (
usage["input_tokens"] * IN_RATE
+ w5 * WRITE_5M_RATE
+ w1 * WRITE_1H_RATE
+ usage["cache_read_input_tokens"] * READ_RATE
+ usage["output_tokens"] * OUT_RATE
)
# ----------------------------------------------------------------------------
# The session we will account for: a 6-turn agent loop.
#
# The prompt each turn is three parts, in render order:
# PREFIX tools + system prompt + CLAUDE.md (stable, 14,000 tokens)
# HISTORY everything said and read so far (grows every turn)
# NEW this turn's user message or tool result
#
# The harness puts a cache breakpoint on the latest turn, the standard
# multi-turn pattern. So on each request:
# - the PREFIX and all PRIOR history are found in the cache -> cache_read
# - the tokens appended since the last request are written -> cache_creation
# - anything after the last breakpoint is plain -> input_tokens
# We use the 5m TTL here; the coding-agent harnesses use 1h (same math, 2x
# write premium instead of 1.25x).
# ----------------------------------------------------------------------------
PREFIX = 14_000
TURNS = [
# (what happened, new prompt tokens appended this turn, output tokens)
("user asks for the feature", 220, 350),
("tool result: read main.py", 4_100, 420),
("tool result: read tests", 3_500, 380),
("tool result: ran test suite", 2_800, 510),
("tool result: applied the edit", 900, 460),
("user: 'also update the docs'", 60, 300),
]
def simulate(cached=True):
"""Return the list of per-turn usage blocks for the session."""
blocks = []
history = 0 # prompt tokens accumulated in the conversation
cached_upto = 0 # how much of the prompt is already in the cache
for _, new_tokens, out_tokens in TURNS:
prompt = PREFIX + history + new_tokens
if cached:
read = cached_upto # everything cached so far
write = prompt - cached_upto # the newly appended part
plain = 0
cached_upto = prompt # breakpoint moves to the end
else:
read, write, plain = 0, 0, prompt # no caching: all full price
blocks.append({
"input_tokens": plain,
"cache_creation_input_tokens": write,
"cache_read_input_tokens": read,
"output_tokens": out_tokens,
"cache_creation": {
"ephemeral_5m_input_tokens": write,
"ephemeral_1h_input_tokens": 0,
},
})
# The model's output becomes part of the next turn's prompt too.
history += new_tokens + out_tokens
return blocks
def main():
print("=== One agent session, six usage blocks ===")
print(f"Stable prefix {PREFIX:,} tok; breakpoint on the latest turn; 5m TTL.\n")
header = (f"{'turn':<34}{'input':>7}{'write':>8}{'read':>9}"
f"{'out':>6}{'prompt':>9}{'cost':>9}")
print(header)
print("-" * len(header))
blocks = simulate(cached=True)
total = 0.0
for (label, _, _), u in zip(TURNS, blocks):
prompt = (u["input_tokens"] + u["cache_creation_input_tokens"]
+ u["cache_read_input_tokens"])
c = cost_of(u)
total += c
print(f"{label:<34}{u['input_tokens']:>7,}"
f"{u['cache_creation_input_tokens']:>8,}"
f"{u['cache_read_input_tokens']:>9,}"
f"{u['output_tokens']:>6,}{prompt:>9,}{c:>9.4f}")
print("-" * len(header))
print(f"{'session total':<34}{'':>7}{'':>8}{'':>9}{'':>6}{'':>9}{total:>9.4f}\n")
print("Read the table columns like an auditor:")
print(" - 'prompt' (the real prompt size) GROWS every turn; 'input' stays 0")
print(" because the breakpoint pattern leaves nothing after the marker.")
print(" - 'read' is last turn's 'read' + 'write': the cache accretes.")
print(" - The expensive column is 'out': it is 5x per token, and unlike")
print(" the prompt it is paid at full price every single turn.\n")
uncached_total = sum(cost_of(u) for u in simulate(cached=False))
print("=== The same session with caching off ===")
print(f" with caching: ${total:.4f}")
print(f" without caching: ${uncached_total:.4f}"
f" ({uncached_total / total:.1f}x more)\n")
# The identity, verified on the last turn's block.
u = blocks[-1]
print("=== The identity, checked on the final turn ===")
print(json.dumps(u, indent=2))
lhs = (u["input_tokens"] + u["cache_creation_input_tokens"]
+ u["cache_read_input_tokens"])
print(f"\n input + write + read = {lhs:,} tokens (the FULL prompt)")
print(" Never read input_tokens alone as 'prompt size'. Sum all three.")
if __name__ == "__main__":
main()
Running it:
=== One agent session, six usage blocks ===
Stable prefix 14,000 tok; breakpoint on the latest turn; 5m TTL.
turn input write read out prompt cost
----------------------------------------------------------------------------------
user asks for the feature 0 14,220 0 350 14,220 0.0976
tool result: read main.py 0 4,450 14,220 420 18,670 0.0454
tool result: read tests 0 3,920 18,670 380 22,590 0.0433
tool result: ran test suite 0 3,180 22,590 510 25,770 0.0439
tool result: applied the edit 0 1,410 25,770 460 27,180 0.0332
user: 'also update the docs' 0 520 27,180 300 27,700 0.0243
----------------------------------------------------------------------------------
session total 0.2878
Read the table columns like an auditor:
- 'prompt' (the real prompt size) GROWS every turn; 'input' stays 0
because the breakpoint pattern leaves nothing after the marker.
- 'read' is last turn's 'read' + 'write': the cache accretes.
- The expensive column is 'out': it is 5x per token, and unlike
the prompt it is paid at full price every single turn.
=== The same session with caching off ===
with caching: $0.2878
without caching: $0.7412 (2.6x more)
=== The identity, checked on the final turn ===
{
"input_tokens": 0,
"cache_creation_input_tokens": 520,
"cache_read_input_tokens": 27180,
"output_tokens": 300,
"cache_creation": {
"ephemeral_5m_input_tokens": 520,
"ephemeral_1h_input_tokens": 0
}
}
input + write + read = 27,700 tokens (the FULL prompt)
Never read input_tokens alone as 'prompt size'. Sum all three.
Three things in that table are worth staring at, because they recur in every real session you will ever audit:
- The write column is the delta, the read column is the history. Each turn writes only what
was appended since the last request (the previous turn's output plus the new tool result), and
reads everything before it.
readon turn $n$ equalsread + writeon turn $n-1$: the cache accretes, and the usage block shows you the accretion directly. input_tokenscan legitimately be zero. With a breakpoint on the latest turn, nothing sits after the marker, so the plain-rate bucket is empty. Zero is not an error and not "free"; the money moved into the write and read columns.- Per turn, output dominates the cost even though it is 100x smaller than the prompt. On the final turn, 27,700 prompt tokens cost about $0.017 (mostly at 0.1x) while 300 output tokens cost $0.0075. That is Chapter 2's 5x asymmetry compounded by Chapter 6's 0.1x reads: on a warm cache, a prompt token is effectively fifty times cheaper than an output token.
The rest of the fields, from a real block
The four core fields are the accounting; the rest of the block is the metadata that explains the
accounting. Here is an unedited usage object from an assistant turn in this machine's own Claude
Code transcript (how to find these files is Chapter 25's subject):
"usage": {
"input_tokens": 6199,
"cache_creation_input_tokens": 6052,
"cache_read_input_tokens": 15853,
"output_tokens": 318,
"server_tool_use": { "web_search_requests": 0, "web_fetch_requests": 0 },
"service_tier": "standard",
"cache_creation": {
"ephemeral_1h_input_tokens": 6052,
"ephemeral_5m_input_tokens": 0
},
"inference_geo": "not_available",
"iterations": [ { "type": "message", "input_tokens": 6199, ... } ],
"speed": "standard"
}
Field by field:
| Field | What it tells you |
|---|---|
cache_creation | The write, split by TTL. This block shows 6,052 tokens written with the 1-hour TTL and none with the 5-minute one: Claude Code caches on the 1-hour tier, paying 2x on writes to keep the entry alive through pauses in your work. The two sub-fields sum to cache_creation_input_tokens. |
server_tool_use | Counts of server-side tool invocations (web search, web fetch) this request, which carry their own per-use pricing on top of tokens. |
service_tier | Which capacity tier served the request (standard, priority, or batch). Batch runs at half price; the tier changes the multiplier on everything above. |
speed | Whether fast mode served the request (standard here). Fast mode is premium-priced, so this field is load-bearing for cost math. |
iterations | One entry per model attempt inside the call, each with its own four-field breakdown. With server-side fallbacks, a declined attempt and the rescue each appear here; the per-attempt list is the billing source of truth when they differ. |
inference_geo | Where inference ran, when data-residency routing is in use. |
The lesson of the TTL split deserves its own sentence: you can read a harness's caching strategy straight out of its usage blocks. The 1h/5m sub-fields told us, without any documentation, that Claude Code buys the doubled write premium for hour-long durability. Chapter 24 measures exactly when that trade wins.
count_tokens: the pre-flight instrument
The usage block is the receipt after the fact. Its pre-flight twin is count_tokens
(Chapter 2 introduced it): the same request shape, no generation, free,
and it counts what the request will actually bill: the system prompt, the tool schemas, and the
message framing are all included, so the number matches the wire rather than your raw text.
# Illustrative: requires the anthropic SDK and an API key.
n = client.messages.count_tokens(
model="claude-opus-4-8",
system=SYSTEM_PROMPT,
tools=TOOLS,
messages=[{"role": "user", "content": QUESTION}],
).input_tokens
The two instruments answer different questions and are worth pairing deliberately:
- Before the call,
count_tokenstells you the full prompt size, so you can enforce a budget, pick a model, or refuse a request that will not fit. - After the call,
usagetells you how that prompt was billed: how much of it hit the cache, what the write premium was, what the output cost. - The difference between them is your cache diagnosis. If
count_tokenssays 40,000 and the usage block showscache_read_input_tokens: 0on the second identical request, a silent invalidator is rewriting your prefix. That test costs nothing and catches the most expensive class of caching bug there is.
Remember.
count_tokenscounts the request;usagebills the response. Neither includes the other's job:count_tokenscannot know the cache state (it never splits into the three buckets), andusagearrives too late to stop an oversized prompt. Pre-flight with one, reconcile with the other.
Usage under streaming
Streaming responses report usage in two installments, and dashboards that read only one of them
under-count. The message_start event carries the input-side fields (they are known as soon as
the prompt is processed, before any output exists), while the final message_delta event carries
the authoritative output_tokens once generation ends:
event: message_start
data: {"type":"message_start","message":{..., "usage":{"input_tokens":6199,
"cache_creation_input_tokens":6052,"cache_read_input_tokens":15853,
"output_tokens":3}}}
... content_block_delta events ...
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},
"usage":{"output_tokens":318}}
The output_tokens in message_start is a placeholder for the few tokens generated so far; the
one in message_delta is the bill. The SDK helpers (get_final_message() in Python,
finalMessage() in TypeScript) merge the two for you, which is one more reason to use them
instead of accumulating events by hand. If you do meter by hand, take input-side fields from
message_start and output from the last message_delta, never the reverse.
Further reading
- The Anthropic Messages API reference (
platform.claude.com/docs), for the authoritative usage field list, and the prompt-caching page for the write-multiplier and TTL details the fields reflect. - The Anthropic token-counting documentation, for what
count_tokensincludes (system, tools, message structure) and its rate limits. - Chapter 2 for the price table and the 5x output asymmetry, and Chapter 6 for why the three input buckets exist at all.
- Chapter 25, where these same blocks, thousands of them, are mined out of Claude Code's transcripts into a full ledger.
Takeaways
- Every response carries
usage, the billing ground truth. The full prompt isinput + cache_creation + cache_read;input_tokensalone is only the uncached remainder and is routinely near zero in healthy agent sessions. - Each field has its own rate: 1x, 1.25x or 2x (by TTL, split out in
cache_creation), 0.1x, and 5x for output. Cost math that ignores the multipliers is wrong in both directions. - In the lab session, the write column is each turn's delta and the read column is the accreted history; caching cut the session cost 2.6x, and per turn the 5x output tokens outweighed a prompt 100x their size.
- The metadata fields explain the bill: the TTL split exposes the caching strategy (Claude Code
uses the 1-hour tier),
service_tierandspeedchange the multipliers, anditerationsitemizes multi-attempt calls. - Pair the instruments:
count_tokensbefore the call for size and budget,usageafter it for cache health and cost. Identical prefix twice withcache_readstill zero means a silent invalidator. - Streaming splits usage across
message_start(input side) and the finalmessage_delta(output side); the SDK's final-message helpers merge them correctly.
👉 You can now read a single receipt. The next chapter turns to the machinery that decides which bucket your tokens land in: a from-scratch model of the prompt cache, and the experiments that show exactly how prompt shape makes or silently breaks the hit.