When you call the Messages API with stream: true, the response arrives as a sequence of server-sent events rather than one JSON document. Token accounting arrives in two of those events, and the difference between them is the single most common source of wrong numbers in home-grown usage pipelines. This matters on every platform: Anthropic documents that the usage object is consistent across the Claude API, Claude Platform on AWS, Amazon Bedrock, Google Vertex AI (via streaming raw prediction), and Microsoft Foundry, so the semantics below travel with you.
Two usage reports per stream
message_start is the first event. It carries the message skeleton, including an initial usage snapshot — in Anthropic's documented example, {"input_tokens": 25, "output_tokens": 1}. Input tokens are already known at this point because the prompt has been processed. Output tokens are essentially a placeholder: generation has barely begun, so you'll see a value like 1. Anything that records message_start usage as the request's final cost undercounts output almost entirely.
message_delta is the final accounting event, arriving near the end of the stream. Its usage carries the updated totals — final output_tokens, plus, when applicable, cache fields and server_tool_use counts (web search and web fetch request tallies). This is the record your metering should keep.
The cumulative trap
Anthropic's streaming documentation carries an explicit warning: "The token counts shown in the usage field of the message_delta event are cumulative." The counts are running totals for the message, not increments since the last event. The failure mode is a pipeline that treats each delta's usage as a slice and sums the slices — producing totals that can be wildly inflated. The correct pattern is last-write-wins: overwrite your usage record with each delta's values, and whatever you hold when the stream ends is the truth.
from anthropic import AnthropicBedrockMantle
client = AnthropicBedrockMantle(aws_region="us-east-1")
final_usage = None
with client.messages.stream(
model="anthropic.claude-sonnet-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Summarize our Q2 report."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
final_usage = stream.get_final_message().usage # cumulative totals
print(final_usage.input_tokens, final_usage.output_tokens)
The official SDKs sidestep the trap for you: the stream helper's final-message accessor returns a fully assembled message whose usage already holds the end-of-stream totals. The trap bites teams that parse raw server-sent events themselves — common in gateway and proxy layers that meter traffic for many internal tenants.
+= next to output_tokens, it's a bug.What the early snapshot is still good for
The message_start usage isn't useless — it's just not final. Because input tokens are settled before generation begins, the snapshot gives you the request's input cost the moment the stream opens, including the cache counters. That's early enough to feed live budget displays or per-tenant spend estimates while the answer is still streaming. Just remember the accounting identity when you do: total input processed is input_tokens plus cache_creation_input_tokens plus cache_read_input_tokens — the three fields are disjoint, and dashboards that show only input_tokens will underrepresent heavily cached workloads. The final message_delta can also break cache writes down by TTL via usage.cache_creation (ephemeral_5m_input_tokens and ephemeral_1h_input_tokens), which matters because the two cache durations are priced differently — see cache reads versus writes.
Compaction changes what "top-level" means
One more wrinkle for teams using server-side compaction (the beta feature that summarizes older conversation content near the context limit). A compacted request can involve multiple internal iterations, and the response's usage.iterations array lists each compaction and message iteration separately. Critically, the top-level input_tokens and output_tokens do not include the compaction iterations — to compute total billed tokens you must sum across all iterations. A metering pipeline that reads only the top-level figures will undercount compacted requests. See the compaction deep dive for the mechanics.
Where to go next
For the full event vocabulary, read the streaming event types guide and the server-sent events primer. For what else lives in the usage object — service tiers and server-tool counters — see the billing fields beyond token counts.