Streaming Best Practices
Streaming is unreliable by nature - long-lived connections drop, proxies buffer output, and clients disconnect mid-response.
Search across all documentation pages
Streaming is unreliable by nature - long-lived connections drop, proxies buffer output, and clients disconnect mid-response.
This page collects the practices that keep a streaming integration solid once it leaves a demo and reaches production traffic.
try/except that covers the entire iteration, not just the call that opens it. anthropic.APIStatusError and its subclasses (like RateLimitError, APIConnectionError) can surface partway through consuming the stream, not only when it's first opened.APIConnectionError and 5xx-class APIStatusErrors; do not retry 4xx errors like a malformed request, which will fail identically every time.index. A response can contain multiple blocks (text, tool_use, thinking) interleaved by index; a single global buffer corrupts state the moment more than one block streams.input_json_delta fragments individually. Accumulate the full string for a block and parse once, at content_block_stop - see Handling Partial JSON During Streamed Tool Calls.text_delta as an opaque fragment, never a token or word boundary. Sentence-detection, profanity filtering, or markdown parsing logic should run on the accumulated string, not on individual deltas.get_final_message() only after the stream has been fully iterated. Calling it early can return an incomplete Message object.message_stop from a stream that just went silent. If your loop exits without ever seeing message_stop, treat that as a failure condition and log it distinctly from a successful completion.error event type explicitly, not just exceptions. A mid-stream error event (e.g. from an overloaded model) is a normal part of the event vocabulary, not always raised as a Python exception depending on SDK version - check both paths.stop_reason values in code paths that assume completion. If your code expects "end_turn" but gets "max_tokens", the response was truncated - don't silently treat it as complete.input_schema is still a malformed call your execution code needs to reject cleanly.usage from message_delta, not an estimate. Token counts are only exact once the API reports them - counting characters or words client-side is not a reliable proxy for billing or rate-limit accounting.stop_reason distribution in production metrics. A rising rate of "max_tokens" stops usually means your max_tokens cap is too low for real traffic, not that responses are naturally getting longer.error events or failed reconnects, not just on outright request failures. Streaming failures can hide inside a "successful" HTTP 200 that never reaches message_stop.Wrapping the entire streaming iteration - not just the initial call - in error handling. Most production streaming bugs come from an error surfacing mid-stream that the code never expected to catch there.
No. The Messages API streaming endpoint has no resume capability - a "reconnect" is really re-sending the full request and starting a fresh stream.
A successful stream's loop exits after observing message_stop. If your loop exits (or the connection closes) without ever seeing message_stop, treat it as a failure, log it, and consider a retry.
No. Retry transient errors like APIConnectionError or 5xx APIStatusErrors with backoff. Don't retry 4xx errors (like an invalid request) - they'll fail identically every time.
It works, but at scale it's wasteful. Batching UI updates over a short interval (25-50ms) instead of per-delta reduces render cost with no perceptible impact on user experience.
Nothing stops automatically on the Claude side unless your backend detects the client disconnect and closes its own stream - otherwise you keep paying for output tokens nobody will see.
Be cautious about idempotency: if the tool already executed and had a side effect before the drop, blindly retrying the whole request could re-trigger that side effect. Guard side-effecting tools with idempotency keys or dedup logic.
Your max_tokens cap is too low for the responses your traffic is actually generating - it's a signal to raise the cap or investigate why responses are longer than expected, not something to silently ignore.
The retry decision logic (which errors to retry, backoff strategy, caps) is the same. The difference is only that a streaming failure can occur after the request already appeared to succeed (mid-stream), so detection requires watching the whole iteration, not just the initial response.
Yes - the partial content up to the failure point is valuable for debugging and, in some UIs, can be shown to the user with a "response was interrupted" notice rather than discarded entirely.
Stack versions: Written against the Claude model lineup current as of ~June 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (the default), and Claude Haiku 4.5 - and the official
anthropicPython SDK (latest 0.x release). Model names, SDK versions, and pricing move quickly - verify current specifics at platform.claude.com/docs before relying on them.
Reviewed by Chris St. John·Last updated Jul 19, 2026