Streaming
Streaming lets you receive Claude's response as it's generated. Perfect for chat UIs where you want to show text as it arrives.
Basic streaming
use Anthropic\Laravel\Facades\Anthropic;
$stream = Anthropic::messages()->createStreamed([
'model' => 'claude-sonnet-4-6',
'max_tokens' => 1024,
'messages' => [
['role' => 'user', 'content' => 'Tell me a short story.'],
],
]);
foreach ($stream as $response) {
if ($response->type === 'content_block_delta'
&& $response->delta->type === 'text_delta') {
echo $response->delta->text;
}
}
Each iteration yields an event object. You'll see message_start, content_block_start, a series of content_block_delta events with text chunks, content_block_stop, and message_delta.
Streaming to the browser with Laravel
Use a streamed response to push text chunks directly to the browser:
use Illuminate\Http\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;
Route::post('/chat', function () {
$stream = Anthropic::messages()->createStreamed([
'model' => 'claude-sonnet-4-6',
'max_tokens' => 1024,
'messages' => request('messages'),
]);
return new StreamedResponse(function () use ($stream) {
foreach ($stream as $response) {
if ($response->type === 'content_block_delta'
&& $response->delta->type === 'text_delta') {
echo "data: " . json_encode(['text' => $response->delta->text]) . "\n\n";
ob_flush();
flush();
}
}
}, 200, [
'Content-Type' => 'text/event-stream',
'Cache-Control' => 'no-cache',
'X-Accel-Buffering' => 'no',
]);
});
On the frontend, consume it with EventSource or fetch with a reader.
Streaming with Broadcasting
For apps with real-time UIs (Livewire, Inertia, Echo), push each chunk as a broadcast event:
class AskClaudeJob implements ShouldQueue
{
public function handle(): void
{
$stream = Anthropic::messages()->createStreamed([
'model' => 'claude-sonnet-4-6',
'max_tokens' => 4096,
'messages' => $this->messages,
]);
$fullText = '';
foreach ($stream as $response) {
if ($response->type === 'content_block_delta'
&& $response->delta->type === 'text_delta') {
$fullText .= $response->delta->text;
ChunkReceived::dispatch($this->conversationId, $response->delta->text);
}
}
$this->message->update(['content' => $fullText, 'status' => 'finished']);
}
}
Stopping a stream mid-generation
If a user cancels, you can break out of the loop. A common pattern is to check a cache flag on each iteration:
use Illuminate\Support\Facades\Cache;
foreach ($stream as $response) {
if (Cache::pull("stop-stream-{$conversationId}")) {
break;
}
if ($response->type === 'content_block_delta'
&& $response->delta->type === 'text_delta') {
echo $response->delta->text;
}
}
Event types
| Event | When it fires | What it contains |
|---|---|---|
message_start |
Once, at the start | Message envelope (id, model, role) and initial usage |
content_block_start |
Start of each content block | Block type and index |
content_block_delta |
Multiple times per block | Incremental content (text, JSON fragments, thinking) |
content_block_stop |
End of each content block | Block index |
message_delta |
Once, near the end | Stop reason and final usage |
The client handles message_stop, ping, and mid-stream errors internally. An error mid-stream throws an ErrorException (see Error Handling).
For the full event sequence, streaming with tool use, streaming with thinking, and the complete delta type list, see the Streaming page in the PHP docs or the Streaming guide on the Anthropic docs.