← Back to AI News

Stop Waiting for the Full Response: Stream Claude Replies Token-by-Token With messages.stream()
Chris Harper
2 min read
Jul 17, 2026 · 04:05 UTC
AI
Workflow
Claude Code
Best Practices
Replace messages.create() with messages.stream() — users see the first token in under 100ms instead of waiting 3–5 seconds for a full reply, with almost no code change.
Every call to messages.create() blocks until Claude finishes generating. For a 300-word reply, that's 3–5 seconds of silence before your UI shows anything. Streaming prints each token as it's generated — users see the response start immediately.
The two-line change
import anthropic
client = anthropic.Anthropic()
# Before — blocks until the full response is ready
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
messages=[{"role": "user", "content": "Explain gradient descent."}],
)
print(response.content[0].text)
# After — streams tokens as they arrive
with client.messages.stream(
model="claude-opus-4-8",
max_tokens=1024,
messages=[{"role": "user", "content": "Explain gradient descent."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
stream.text_stream is a generator that yields each text delta as Claude produces it. The with block closes the connection when done.
Store the complete message after streaming
with client.messages.stream(
model="claude-opus-4-8",
max_tokens=1024,
messages=[{"role": "user", "content": "Explain gradient descent."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
# After the loop exits, the full message is available
final = stream.get_final_message()
# Same shape as messages.create() — use for storage, conversation history, etc.
print(f"\nTokens used: {final.usage.input_tokens} in, {final.usage.output_tokens} out")
Async version (FastAPI, WebSockets, SSE)
async def stream_to_client(prompt: str):
client = anthropic.AsyncAnthropic()
async with client.messages.stream(
model="claude-opus-4-8",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
) as stream:
async for text in stream.text_stream:
yield text # forward to your SSE or WebSocket handler
Streaming with tool calls
Use raw stream events when you need to handle tool calls alongside text:
with client.messages.stream(
model="claude-opus-4-8",
max_tokens=1024,
tools=[{"name": "get_weather", "description": "...", "input_schema": {...}}],
messages=[{"role": "user", "content": "What's the weather in NYC?"}],
) as stream:
for event in stream:
if event.type == "content_block_start" and event.content_block.type == "tool_use":
print(f"\n[Tool call: {event.content_block.name}]")
elif event.type == "content_block_delta" and event.delta.type == "text_delta":
print(event.delta.text, end="", flush=True)
When streaming matters
| Use case | Streaming? |
|---|---|
| User-facing chat UI | Yes — first token under 100ms |
| Background batch job | No — create() is simpler |
| Agent inner loop | Optional — use for live progress |
| CI pipeline | No — final result is all that matters |
Sources: Streaming messages — Claude Platform Docs · Anthropic Python SDK