Gotcha one: the usage object you have to ask for
Non streamed responses carry usage by default. Streamed ones differ by provider:
- OpenAI: opt in with
stream_options: { include_usage: true }, which appends one final chunk holding the usage for the whole request. Without it, streamed calls report nothing, and any tracking that reads usage from responses records zero cost for your entire streamed surface. - Anthropic: usage streams by default, split across events:
message_startcarries input tokens,message_deltaevents carry cumulative output counts. Your handler reads the final delta for the full picture.
// OpenAI: capture usage on a stream
const stream = await openai.chat.completions.create({
model: 'gpt-5.4-mini',
messages,
stream: true,
stream_options: { include_usage: true }, // without this: no usage, ever
});
let usage;
for await (const chunk of stream) {
if (chunk.usage) usage = chunk.usage; // arrives in the final chunk
// ... forward content chunks to the client
}
// now log usage with userId/feature/plan (one POST to Weckr's /v1/log)Zero cost rows for a whole feature are the tell. If your per feature dashboard shows a streamed feature costing nothing, this is why, and the invoice will disagree with your dashboard by exactly that feature, the reconciliation gap pattern from the OpenAI Usage API guide.
Gotcha two: abandoned streams still bill
Cancellation stops future generation, not past generation: the input tokens processed and the output produced before the close are billed. For long generations with impatient users, that is real money spent on words nobody read. Three mitigations: propagate client aborts to the provider promptly so generation actually stops (do not let the server finish a response the client left), tune max_tokens so the worst case is not far beyond what users consume, and measure stream abandonment per feature, because a high rate is both a UX finding and a cost leak. Abandoned partial usage is still reported through the mechanisms above, so capture keeps working if your handler reads usage on abort paths too, not just on clean completion.
Streamed and unstreamed, one margin view
The end state to aim for: every call, streamed or not, lands in the same per user, per feature cost view with tokens captured correctly. With Weckr that means the SDK wrapper where you call providers directly, or one POST from your stream completion handler where you use framework streaming, the same pattern as Vercel AI SDK cost tracking’s onFinish. Cost is recomputed server side at current rates either way, so a capture bug cannot also become a pricing bug, and the per user caps from rate limiting AI endpoints per user apply to streamed traffic identically.
FAQ
Does streaming cost more than non streaming LLM calls?
No, the token prices are identical: streaming changes delivery, not billing. The cost differences are indirect. Streaming improves perceived latency so users interact more, abandoned generations still bill for the tokens produced before cancellation, and, the big one, sloppy usage capture on streams makes many apps record zero cost for their streamed traffic, which reads as savings and is actually blindness.
How do I get token usage when streaming with OpenAI?
You must opt in: set stream_options with include_usage true on the request, and OpenAI appends a final chunk containing the usage object for the whole request. Without that option, streamed responses report no usage at all, and any cost tracking that reads usage from the response silently records zeros for every streamed call.
How does Anthropic report usage on streamed responses?
By default, spread across events: the message_start event carries input token counts and message_delta events carry cumulative output token counts as generation proceeds. Read the final message_delta usage for the complete picture. Different mechanism from OpenAI, same requirement: your stream handler has to actually collect these fields, or cost tracking sees nothing.
What happens to cost when a user abandons a streamed response?
You pay for what was generated up to cancellation, the input tokens in full plus the output produced before the stream closed. Apps with long generations and impatient users accumulate real spend on responses nobody read. Worth measuring: an abandonment rate on streams, and a max_tokens tuned so the worst case generation is not far beyond what users actually consume.
How do I track streamed calls per user with Weckr?
Same as any call: capture the usage from the final chunk (OpenAI, with include_usage on) or the final message_delta (Anthropic), then log it with your userId, feature, and plan, either via the SDK wrapper or one POST to the log endpoint from your stream completion handler. Cost is recomputed server side at current rates, so streamed and non streamed traffic land in the same per user margin view.
Keep reading
Stream the tokens. Do not stream past the meter.
Streaming is table stakes UX, and it is also the easiest place for cost tracking to silently break. Wire the capture once, and Weckr keeps streamed traffic in the same per user margin view as everything else, free for 50,000 requests a month. See it on the live demo, or follow the integration docs.