
Real-time web features require either WebSockets or Server-Sent Events (SSE). Both push data from server to client, but they work differently. In 2026, SSE has gained popularity for AI streaming while WebSockets remain essential for bidirectional communication. This guide explains both with code and helps you choose.
๐ Table of Contents
WebSockets: Full Duplex
WebSockets create a persistent TCP connection. Both client and server can send messages at any time โ true full-duplex. Used for: chat apps, collaborative tools, live gaming, trading platforms.
# FastAPI WebSocket server
from fastapi import FastAPI, WebSocket
app = FastAPI()
connected = []
@app.websocket('/ws')
async def websocket_endpoint(ws: WebSocket):
await ws.accept()
connected.append(ws)
try:
while True:
data = await ws.receive_text()
# Broadcast to all clients
for client in connected:
await client.send_text(f'Message: {data}')
except:
connected.remove(ws)
// Browser WebSocket client
const ws = new WebSocket('wss://example.com/ws');
ws.onopen = () => console.log('Connected');
ws.onmessage = (e) => console.log('Received:', e.data);
ws.onerror = (e) => console.error('Error:', e);
ws.onclose = () => console.log('Disconnected');
// Send message
ws.send('Hello server!');
Server-Sent Events (SSE): One-Way Push
SSE uses a regular HTTP connection where the server streams events. Client cannot send data back (use separate HTTP requests for that). Auto-reconnects on disconnect. Works over HTTP/2 multiplexing. Used for: live feeds, AI response streaming, notifications, progress bars.
# FastAPI SSE server
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import asyncio
app = FastAPI()
@app.get('/stream')
async def stream():
async def event_generator():
for i in range(10):
yield f'data: Message {i}\n\n'
await asyncio.sleep(1)
return StreamingResponse(
event_generator(),
media_type='text/event-stream'
)
// Browser SSE client
const evtSource = new EventSource('/stream');
evtSource.onmessage = (e) => {
console.log('Received:', e.data);
};
evtSource.onerror = (e) => {
console.error('Error:', e);
// EventSource auto-reconnects after error
};
// Close when done
evtSource.close();
AI Streaming with SSE (2026 Pattern)
LLM APIs (Claude, OpenAI) stream responses via SSE. Here is the pattern to forward Claude streaming to the browser.
import anthropic
from fastapi.responses import StreamingResponse
client = anthropic.Anthropic()
@app.post('/ask')
async def ask(question: str):
async def generate():
with client.messages.stream(
model='claude-sonnet-4-5',
max_tokens=1024,
messages=[{'role': 'user', 'content': question}]
) as stream:
for text in stream.text_stream:
yield f'data: {text}\n\n'
yield 'data: [DONE]\n\n'
return StreamingResponse(generate(), media_type='text/event-stream')
Comparison
- WebSockets: Bidirectional, persistent TCP, binary or text, manual reconnect
- SSE: Server-to-client only, HTTP, text only, auto-reconnect, simpler
- WebSockets overhead: Heavier setup (upgrade handshake, state management)
- SSE overhead: Minimal โ works with any HTTP server, CDN-friendly
When to Use Which
- WebSockets: Chat, multiplayer games, collaborative editing (Google Docs-style)
- SSE: AI response streaming, live feeds, notifications, progress updates
- Neither: Polling every 30s+ is simpler and often sufficient for low-frequency updates
Conclusion
In 2026, SSE is the right choice for most real-time features โ simpler, works over HTTP/2, CDN-friendly, and perfect for AI streaming. Choose WebSockets only when you need true bidirectional communication. Start with SSE and upgrade to WebSockets if you hit its limits.
๐ You might also like
๐ Share this article




โ๏ธ Leave a Comment