Streaming matters for anything interactive: users see the first words in well under the time a full response takes, and long generations cannot hit client timeouts. Enable it with "stream": true and the Messages API responds with server-sent events (SSE) — a plain-text protocol where each event has a type and a JSON payload. Streaming is core API behavior, generally available on all four third-party platforms. Here is what actually comes down the wire.
The event sequence
Every streamed response follows the same skeleton:
| Event | What it carries |
|---|---|
message_start | A Message object with an empty content array — the frame you will fill in |
content_block_start | Opens one content block (text, tool_use, thinking) with its index |
content_block_delta | One or more incremental updates to that block |
content_block_stop | Closes the block at that index |
message_delta | Top-level changes to the Message — stop reason, cumulative usage |
message_stop | End of stream |
The start/delta/stop trio repeats per content block, and each block carries an index matching its final position in the content array — so assembly is just "append deltas to the block at this index." Two more event types can appear anywhere: ping events (keep-alives you can ignore) and error events, such as an overloaded_error mid-stream. That last one is important: because the HTTP status was already 200 when streaming began, failures arrive as events, not status codes. Your handler needs an error branch even after a successful connection.
The delta types
Inside content_block_delta, the payload's type tells you what kind of block is growing. text_delta carries visible text. thinking_delta carries reasoning text when thinking display is on. signature_delta arrives just before a thinking block closes and carries the cryptographic signature you must pass back unchanged in later turns. And input_json_delta streams a tool call's arguments as partial_json string fragments — accumulate them and parse only after content_block_stop, because the fragments are not valid JSON on their own. Current models emit roughly one complete key/value pair at a time, so gaps between these events are normal, and fine-grained streaming of parameter values can be enabled per tool with eager_input_streaming.
thinking.display: "omitted" (the default on the newest models), no thinking_delta events are sent at all — the thinking block opens, receives a single signature_delta, and closes. Don't treat the silence as a stall.Assembling a response in practice
from anthropic import AnthropicVertex
client = AnthropicVertex(project_id="my-project", region="global")
stream = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
stream=True,
messages=[{"role": "user", "content": "Draft a weekly status update."}],
)
for event in stream:
if event.type == "content_block_delta" and event.delta.type == "text_delta":
print(event.delta.text, end="", flush=True)
elif event.type == "message_delta":
usage = event.usage # cumulative token counts
Token counts in message_delta usage are cumulative, not incremental — read the last one you received as the total, and treat it as the billing ground truth for the response. The official SDKs also offer higher-level stream helpers that do the assembly for you; the raw loop above is what those helpers are doing underneath.
Odds and ends worth knowing
If your request uses server-side fallback between models, a fallback content block appears at each model boundary as a start/stop pair with no deltas in between. If you use server-side compaction, the summary arrives as a normal content block: one content_block_start, a single delta containing the whole summary, then stop. And for requests likely to run past ten minutes, streaming is not just nice to have — official guidance is to stream (or use batches) rather than hold a non-streaming connection open that long.
Where to go next
See streaming with tools for the tool-use loop under SSE, the Messages API anatomy for the non-streaming object you are reassembling, and the feature matrix for platform coverage.