When you set "stream": true on a Messages request, Claude's response arrives as server-sent events (SSE): a single long-lived HTTP response whose body is a text stream of events, delivered as they are generated. SSE is much simpler than WebSockets — one direction, plain text over ordinary HTTP — which is exactly why it works through corporate proxies and standard load balancers without special configuration.
The wire format
An SSE body is line-oriented. Each event is a group of lines: an event: line naming the event type, one or more data: lines carrying the payload (for the Claude API, a JSON object), and a blank line terminating the event. A parser therefore does three things: read lines, accumulate data: content until a blank line, then JSON-decode and dispatch on the event name. If you hand-roll this, two classic bugs to avoid are assuming one network read equals one event (events fragment and coalesce across TCP packets) and forgetting that a single event's data can span multiple lines.
Claude's event vocabulary
A successful stream follows a fixed grammar: message_start (a Message object with empty content), then for each content block a content_block_start, one or more content_block_delta events, and a content_block_stop; then one or more message_delta events carrying top-level changes such as the stop reason and cumulative usage counts; finally message_stop. Delta payloads come in several types — text_delta for text, input_json_delta for tool inputs, thinking_delta and signature_delta for reasoning blocks — and each block carries an index tying it to its position in the final content array.
Two event types keep integrations honest. ping events may appear anywhere and exist to keep the connection warm; ignore them, but don't crash on them. error events deliver failures that occur after the HTTP 200 was already sent — for example {"type": "error", "error": {"type": "overloaded_error", ...}}, the in-stream equivalent of an HTTP 529. A robust client treats unknown event types as skippable rather than fatal, since the vocabulary grows over time (compaction blocks and fallback blocks already extend it).
Reconnection: what SSE promises and what applies here
The SSE standard includes a reconnection story: clients may auto-reconnect and send a Last-Event-ID header so the server can resume where it left off. That mechanism suits feeds like stock tickers. A model generation is different — Anthropic's streaming documentation does not describe resuming an interrupted generation mid-stream, so the safe engineering assumption is that a dropped stream means re-issuing the request from scratch. Design for that: make delivery idempotent on your side (discard the partial response, or de-duplicate downstream), and lean on prompt caching so the retry re-reads the expensive prefix at a tenth of the price instead of paying full input cost twice.
Connection-level failures and 429/5xx responses are retryable, and the official SDKs retry them automatically with exponential backoff, honoring any retry-after header. Streaming is also the documented answer to long generations: for requests that would run more than about 10 minutes, use streaming (or batches) — the SDKs validate that non-streaming requests won't exceed a 10-minute timeout and set TCP keep-alive to hold long connections open.
Library choices
The strong default in every language is Anthropic's official SDK, which handles SSE parsing, event typing, accumulation, and retries. In Python that looks like:
from anthropic import AnthropicVertex
client = AnthropicVertex(project_id="my-project", region="global")
with client.messages.stream(model="claude-opus-4-8", max_tokens=1024,
messages=[{"role": "user", "content": "Explain SSE briefly."}]) as s:
for text in s.text_stream:
print(text, end="", flush=True)
If you can't use an SDK — an unsupported language, or a gateway in between — any generic SSE client library plus a JSON parser suffices, provided it exposes event names and tolerates unknown types. For browsers, note that the native EventSource API only issues GET requests, while the Messages API requires POST, so browser streaming typically goes through a fetch-based SSE reader or, better, your own backend proxy that keeps credentials off the client.
error events even after a 200, and treat a broken stream as a fresh retry — with caching making that retry cheap.Where to go next
The tool-use layer on top of these events is covered in Streaming With Tool Use, and the full event catalog in Streaming Event Types. For user-experience and timeout design, see Streaming UX and Timeouts.