Guides / llm streaming cost tracking

Capturing token usage from a streaming response

Streaming responses report usage across two events rather than one body, and the numbers can differ within a single request. Reading the opening event alone under-counts, and summing the closing ones double-counts. Culpa, a local-first LLM cost, margin, and forecast ledger, records the final figures and prices each call from them.

Why this happens

A non-streaming call hands you one response body with one usage object, and capture is a solved problem. Server-sent events break that in two ways, both documented and both easy to get wrong. First, usage arrives split: an opening event carries an input count and a running output count, and a closing event carries the totals. Anthropic's docs warn in as many words that the counts on the closing events are cumulative, so code that adds them up across events reports more output than the model produced. Second, and worse because it looks like nothing is wrong, the opening figure can be stale by the end. Server-side tool use adds context mid-request, so the input count that arrived first no longer describes the request that ran. Both failures are silent. The stream completes, the user gets their answer, your ledger writes a number, and nothing anywhere reports an error. You find out at the invoice, which is the one place that disagreeing with you costs a month.

What this usually looks like

  • Your captured spend is lower than the provider invoice and nothing explains the gap.
  • Streaming endpoints report costs that the same prompt won't when run without streaming.
  • Your capture reads the first usage event it sees.
  • You add up usage across events rather than taking the last one.
  • Calls that used server-side tools look suspiciously cheap.

Free, no card, no account

Run the free Cost Leak Scan

It shows your most expensive conversation before you install anything.

Run the free Cost Leak ScanStart 14-day trial

Mistakes that cost the most

MistakeWhy it hurtsDo instead
Reading usage from the opening event and stopping.Server-side tool use can grow the input count during the request, so the first figure goes stale.Take the totals from the final event, and treat the opening one as a preview.
Summing usage across the closing events.Anthropic documents those counts as cumulative, so adding them reports output that never happened.Take the last value rather than the sum.
Assuming a completed stream means captured usage.The user gets their answer either way, so a capture bug produces no error anyone will see.Reconcile captured spend against the invoice monthly and treat any gap as a bug.
Dropping usage when a client disconnects mid-stream.The tokens were generated and billed, so discarding the record understates real spend.Record what arrived before the disconnect rather than throwing the call away.

Run this check tonight

  1. Find where your code reads usage from a stream and check which event it takes.
  2. Check whether it sums across events or takes the last value.
  3. Run one prompt streaming and non-streaming, and compare the cost you recorded.
  4. Compare a month of captured spend against the invoice and size the gap.

Two input counts, one request, from the provider's own example

Anthropic's streaming documentation, read 2026-08-05, includes a worked web-search response on claude-opus-5. Its message_start event reports input_tokens of 2,679. Its final message_delta event, on the same request, reports input_tokens of 10,682 and output_tokens of 510, because server-side tool use added context while the stream ran. Both token counts are published by Anthropic. The pricing is the price book's real claude-opus-5 rate of $5.00 and $25.00 per million, effective 2026-08-01.

reading message_start: 2,679 x $5.00/M + 510 x $25.00/M = $0.013395 + $0.012750 = $0.026145
reading the final message_delta: 10,682 x $5.00/M + 510 x $25.00/M = $0.053410 + $0.012750 = $0.066160
input tokens missed by stopping at the first event: 10,682 - 2,679 = 8,003
a miss of 74.9% of the real input, and the call costs 2.53 times what was recorded

None of this is a modelled edge case. It's the example Anthropic publishes to show what a stream looks like, and the two input counts in it differ by a factor of four. Any capture that reads the opening event and stops will under-report that call by 60%, quietly, forever.

Every number, with its confidence and source

FigureWhat it meansConfidenceSource
8,003 input tokensdifference between the two input counts in Anthropic's own published streaming exampleprovider-reportedAnthropic's streaming documentation at platform.claude.com/docs/en/build-with-claude/streaming, read 2026-08-05. Its worked web-search example on claude-opus-5 publishes input_tokens of 2,679 in the message_start event and 10,682 in the final message_delta event of the same request. Both figures are Anthropic's, quoted as published. The difference of 8,003 is 74.9% of 10,682.
$0.026145 against $0.066160cost of that one published call, read from the first usage event and from the lastcalculatedApplying the price book's real claude-opus-5 rates of $5.00 and $25.00 per million, effective 2026-08-01, to Anthropic's own published token counts: 2,679 input and 510 output gives $0.013395 + $0.012750 = $0.026145, while 10,682 input and the same 510 output gives $0.053410 + $0.012750 = $0.066160, a factor of 2.53. Nothing here is modelled. Both token counts and both rates are published.

What a generic answer can’t know

Nothing here is hidden by a provider, since Anthropic documents all of it plainly. The pivot is where the capture runs. Anything reading usage from a stream has to sit inside your request path, handling your SSE events, in your code, which is exactly where a tool watching from outside can't reach. Culpa captures at the call site, takes the final usage rather than the first or the sum, prices each call from a versioned price book in exact decimal, and keeps the ledger on infrastructure you control. It also fails open, so a capture problem never becomes an availability problem. The check that proves it stays the boring one worth doing anyway: reconcile a month of captured spend against the invoice and see whether the two agree.

Questions founders ask next

Where does token usage appear in a streaming response?

In two places. The opening message_start event carries an input count and a starting output count, and the closing message_delta event carries the totals. Anthropic's docs warn that the counts on message_delta are cumulative, so take the last value rather than adding them up.

Why would the input token count change during one request?

Server-side tool use adds context mid-request. In Anthropic's own published web-search example the count goes from 2,679 at message_start to 10,682 at the final message_delta, so the opening figure describes the request as it began rather than as it ran.

Does streaming cost more than a normal call?

No, the rates are the same. What changes is how easy the usage is to capture correctly, and a capture bug looks exactly like a discount until the invoice arrives.

What happens if the user closes the tab mid-stream?

The tokens generated up to that point were still produced and still billed, so discarding the record understates your real spend. Record what arrived before the disconnect. Aborted streams are also worth counting on their own, because a high abort rate is money spent on output nobody read.

On your infrastructure

Culpa runs on your infrastructure. Your prompts and responses never leave it. Culpa counts calls to run your plan, and it fails open, so if it ever breaks your app keeps running.


How Culpa works

Find the culprit. Not just the total.

Your dashboard shows what you spent. It stops short of who spent it. Culpa shows the conversation, the user and the feature behind it.

Your prompts stay local.

Culpa runs on your own infrastructure. What you send to a model reaches us at no point.

Every dollar has a name.

Follow any charge to the conversation, the user, the feature and the customer behind it.

See the bill before it lands.

Cost your next feature before you ship it. You get the likely bill and the worst case, at best, median, p90 and p99.

Three steps to your first answer.

1

Change one base URL.

Or drop in the Python or TypeScript library.

2

Find your most expensive conversation.

In the first session, not the first week.

3

Cost your next feature before you ship it.

Base URLhttp://localhost:4545/v1Your traffic keeps flowing if Culpa ever stops.

Why the bill went up

Example dashboard

Calls traced

418,209

across 3 projects

Spend this week

$378.41

+ $182 vs last week

Failed calls

312

74% retried, and you paid for all of them

+ $182 this week traced to one culprit

Spend over 14 days

$0$20$40$60$8024262830020406
user_384report_generatorconv_91fprompt_v1894,220 tokens3 retries$6.81

Most expensive users

user_384$38.42
user_119$21.07
user_562$14.90
user_204$8.30
user_871$5.10

Next week forecast

Best$180
Median$240
p90$310
p99$395

Graded against reality. Accuracy shown as results land.

Keep reading


Sources: Anthropic, streaming messages, Anthropic pricing. Last reviewed 2026-08-05, rates effective 2026-08-01. Plain text version.