Claude Streaming Usage Metadata Missing — Token Count Fix (2026) | AI Error Hub
Anthropic Claude Streaming Severity: Medium Client-side

Claude Streaming Usage Metadata Missing or Wrong

You need token counts for billing/analytics but the streaming response arrives without usage data — or the counts don't match what you expect. Usage is split across message_start (input tokens) and message_delta (output tokens), NOT sent per-chunk.

Error surface: Client-side accounting Category: Streaming Location: Two separate events Last tested: Nov 15, 2026
Quick Fix (TL;DR)

Input tokens arrive in message_start.message.usage. Output tokens arrive in message_delta.usage.output_tokens (near end of stream). Cache metrics (cache_creation_input_tokens, cache_read_input_tokens) are in message_start. Track both events to get the full picture.

The Symptoms You're Seeing

Common issues
usage.output_tokens is 0 or missing after stream ends
  → You looked at message_start; output count is in message_delta

Billing dashboard shows higher tokens than my logs
  → You forgot to include cache_read_input_tokens

Streaming response.usage doesn't match non-streaming response.usage
  → Only if you're accumulating incorrectly; totals should match

Only got input_tokens, no output_tokens
  → Stream was interrupted before message_delta arrived

cache_creation_input_tokens is None
  → Prompt caching not used on this request (this is fine)

Where Usage Data Lives in the Stream

SSE events with usage
// FIRST event — input tokens known upfront event: message_start data: { "type": "message_start", "message": { "id": "msg_01...", "usage": { "input_tokens": 1523, // prompt tokens "cache_creation_input_tokens": 0, // caching writes "cache_read_input_tokens": 0, // caching hits "output_tokens": 1 // initial (updated later) } } } // ... many content_block_delta events ... // NEAR END — final output token count event: message_delta data: { "type": "message_delta", "delta": { "stop_reason": "end_turn" }, "usage": { "output_tokens": 847 // FINAL output tokens } } event: message_stop data: { "type": "message_stop" }

What Actually Causes This Error

45%
Only reading message_start for usageoutput_tokens in message_start is a placeholder (1). Real value is in message_delta.
25%
Ignoring cache token fieldscache_read_input_tokens are billed at reduced rate but still count. Missing them = wrong cost estimate.
15%
Stream interrupted before message_deltaConnection drop = no final usage. Cost still accrues on Anthropic's side.
10%
Not using SDK's final_message()Manual parsers often forget to accumulate the two usage sources.
5%
Confusion with server_tool_use tokensTool result tokens billed separately for computer use / server-side tools.

Fixes That Work (Tested Nov 2026)

1SDK Approach (Recommended)

Python · SDK aggregates usage automatically
with client.messages.stream( model="claude-sonnet-4-5", max_tokens=1024, messages=[{"role": "user", "content": prompt}] ) as stream: for text in stream.text_stream: print(text, end="") # Final message has complete usage final = stream.get_final_message() usage = final.usage print(f"\n--- Usage ---") print(f"Input: {usage.input_tokens}") print(f"Output: {usage.output_tokens}") print(f"Cache write: {usage.cache_creation_input_tokens}") print(f"Cache read: {usage.cache_read_input_tokens}") # Cost calculation for Claude Sonnet 4.5 input_cost = usage.input_tokens * 3 / 1_000_000 output_cost = usage.output_tokens * 15 / 1_000_000 cache_write_cost = usage.cache_creation_input_tokens * 3.75 / 1_000_000 cache_read_cost = usage.cache_read_input_tokens * 0.30 / 1_000_000 total_cost = input_cost + output_cost + cache_write_cost + cache_read_cost print(f"Total: ${total_cost:.6f}")

2Manual Usage Tracking (Custom Parser)

Python · manual usage aggregation
usage = { "input_tokens": 0, "output_tokens": 0, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, } for event_type, data in stream_claude(messages): if event_type == "message_start": # Input tokens and cache metrics live here u = data["message"]["usage"] usage["input_tokens"] = u.get("input_tokens", 0) usage["cache_creation_input_tokens"] = u.get( "cache_creation_input_tokens", 0) usage["cache_read_input_tokens"] = u.get( "cache_read_input_tokens", 0) elif event_type == "message_delta": # Final output tokens land here (near end of stream) usage["output_tokens"] = data["usage"]["output_tokens"] elif event_type == "content_block_delta": # Text chunks — usage NOT sent per chunk if data["delta"].get("type") == "text_delta": yield data["delta"]["text"] log_usage(usage)

3Reconcile With Anthropic Billing Dashboard

Billing formula (Claude Sonnet 4.5, Nov 2026)

Input: $3.00 per 1M tokens
Output: $15.00 per 1M tokens
Cache write: $3.75 per 1M tokens (5-min ephemeral cache)
Cache read: $0.30 per 1M tokens (10% of input price)
All four token types are billable. If your logs miss any category, dashboard totals won't match. Server-side tool use (computer use, web search) has separate billing lines.

Python · reconciliation helper
def calculate_cost(usage, model="claude-sonnet-4-5"): prices = { "claude-sonnet-4-5": {"in": 3.0, "out": 15.0, "cw": 3.75, "cr": 0.30}, "claude-opus-4-6": {"in": 15.0, "out": 75.0, "cw": 18.75, "cr": 1.50}, "claude-haiku-4-5": {"in": 0.80, "out": 4.0, "cw": 1.0, "cr": 0.08}, } p = prices[model] return ( usage.input_tokens * p["in"] + usage.output_tokens * p["out"] + usage.cache_creation_input_tokens * p["cw"] + usage.cache_read_input_tokens * p["cr"] ) / 1_000_000

Preventing This Error Going Forward

  • Log usage after every stream. Store all four token categories.
  • Use SDK's get_final_message(). Handles aggregation for you.
  • Include cache tokens in cost dashboards. Ignoring them = wrong forecasts.
  • Emit usage on interrupted streams too. Whatever you have from message_start is still billed.
  • Reconcile monthly with Anthropic Console. Discrepancy > 5% suggests parsing bug.
AR
Tested by Ahmed R.
Researcher · AI Error Hub
Last verified: Nov 15, 2026 · SDK: anthropic-sdk-python 0.42

Frequently Asked Questions

It's a placeholder — Anthropic accounts for the first output token to be generated. The real count is finalized in message_delta once generation completes. Never trust message_start.usage.output_tokens as final.

No. To estimate live cost while streaming, count text_delta events or use tiktoken-style estimation. The authoritative count arrives only in message_delta at the end.

For server-side tools (web_search, code_execution, computer use), Anthropic reports additional token fields like server_tool_use.web_search_requests or server_tool_use.execution_time_ms. These are billed at published rates for each tool. Include them in cost tracking if you use these tools.

Yes. Cache reads bill at 10% of the input rate. So cache_read_input_tokens × input_price × 0.10. The API doesn't charge the full input rate for cached content — you just need to include it in your cost math.

Yes, whatever tokens were consumed are billed. Refusals may generate a short output (billed), and input tokens are always billed even on 400 errors after processing began.