Meta Description: Need real-time updates in your API? Compare WebSocket and Server-Sent Events (SSE) to choose the right protocol for your use case.
Keywords: websocket, server sent events, real-time api, sse vs websocket, push notifications, real-time communication
Word Count: ~2,200 words
Your API needs real-time updates. Users should see changes instantly without polling.
You have two main options: WebSocket and Server-Sent Events (SSE).
Both enable real-time communication, but they work differently and suit different use cases. Here's how to choose.
Quick Comparison
| Feature | WebSocket | Server-Sent Events |
|---|---|---|
| Direction | Bidirectional | Server to client only |
| Protocol | ws:// or wss:// | HTTP/HTTPS |
| Data Format | Binary or text | Text (typically JSON) |
| Browser Support | Excellent | Excellent |
| Reconnection | Manual | Automatic |
| Complexity | Higher | Lower |
| Use Case | Chat, gaming, collaboration | Notifications, feeds, monitoring |
Server-Sent Events: Simple Push
SSE is an HTTP-based protocol for server-to-client streaming. The server pushes updates over a long-lived HTTP connection.
How SSE Works
Client opens a connection:
const eventSource = new EventSource('https://api.petstoreapi.com/v1/pets/123/updates');
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Pet updated:', data);
};
eventSource.onerror = (error) => {
console.error('Connection error:', error);
};
Server sends events:
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
data: {"id":"123","status":"ADOPTED","updatedAt":"2026-03-13T10:30:00Z"}
data: {"id":"123","status":"AVAILABLE","updatedAt":"2026-03-13T11:00:00Z"}
Each event starts with data: followed by the payload.
SSE Event Format
Simple event:
data: {"message":"Pet status changed"}
Multi-line event:
data: {
data: "id": "123",
data: "status": "ADOPTED"
data: }
Named event:
event: statusChanged
data: {"id":"123","status":"ADOPTED"}
Client listens for named events:
eventSource.addEventListener('statusChanged', (event) => {
const data = JSON.parse(event.data);
console.log('Status changed:', data);
});
Event with ID (for reconnection):
id: 1234
data: {"id":"123","status":"ADOPTED"}
If the connection drops, the client sends the last event ID when reconnecting:
GET /v1/pets/123/updates
Last-Event-ID: 1234
The server resumes from that point.
SSE Server Implementation
Node.js with Express:
app.get('/v1/pets/:id/updates', (req, res) => {
const petId = req.params.id;
// Set SSE headers
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
// Send initial event
res.write(`data: ${JSON.stringify({ connected: true })}\n\n`);
// Subscribe to pet updates
const listener = (update) => {
if (update.petId === petId) {
res.write(`data: ${JSON.stringify(update)}\n\n`);
}
};
petUpdateEmitter.on('update', listener);
// Cleanup on disconnect
req.on('close', () => {
petUpdateEmitter.off('update', listener);
res.end();
});
});
Python with Flask:
from flask import Flask, Response
import json
import time
app = Flask(__name__)
@app.route('/v1/pets/<pet_id>/updates')
def pet_updates(pet_id):
def generate():
# Send initial event
yield f"data: {json.dumps({'connected': True})}\n\n"
# Send updates
while True:
update = get_pet_update(pet_id)
if update:
yield f"data: {json.dumps(update)}\n\n"
time.sleep(1)
return Response(generate(), mimetype='text/event-stream')
SSE Strengths
1. Simple to implement
SSE uses standard HTTP. No special protocol or library needed. The browser's EventSource API handles everything.
2. Automatic reconnection
If the connection drops, the browser automatically reconnects. You don't write reconnection logic.
3. Works with HTTP/2
SSE benefits from HTTP/2 multiplexing. Multiple SSE connections share one TCP connection.
4. Built-in event IDs
SSE has built-in support for resuming from the last received event. No custom logic needed.
5. Firewall-friendly
SSE uses standard HTTP/HTTPS. It works through corporate firewalls and proxies that block WebSocket.
SSE Weaknesses
1. Server to client only
SSE is one-way. Clients can't send messages over the SSE connection. They must use separate HTTP requests.
2. Text only
SSE sends text data. Binary data must be base64-encoded, which increases size.
3. Connection limits
Browsers limit concurrent HTTP connections per domain (typically 6). Each SSE connection counts toward this limit.
HTTP/2 helps, but the limit still exists.
4. No built-in compression
SSE doesn't compress messages automatically. You must compress data before sending.
When to Use SSE
Notifications: Push notifications, alerts, status updates Live feeds: News feeds, social media updates, activity streams Monitoring: Server metrics, log streaming, dashboard updates Progress tracking: File uploads, batch processing, long-running tasks
Use SSE when: - Updates flow from server to client - You don't need bidirectional communication - Simplicity matters - You want automatic reconnection
WebSocket: Bidirectional Communication
WebSocket provides full-duplex communication. Both client and server can send messages anytime.
How WebSocket Works
Client connects:
const ws = new WebSocket('wss://api.petstoreapi.com/v1/ws');
ws.onopen = () => {
console.log('Connected');
ws.send(JSON.stringify({ type: 'subscribe', petId: '123' }));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Received:', data);
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
ws.onclose = () => {
console.log('Disconnected');
};
Server handles connections:
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => {
console.log('Client connected');
ws.on('message', (message) => {
const data = JSON.parse(message);
if (data.type === 'subscribe') {
// Subscribe client to pet updates
subscribeToPet(ws, data.petId);
}
});
ws.on('close', () => {
console.log('Client disconnected');
});
});
function subscribeToPet(ws, petId) {
petUpdateEmitter.on('update', (update) => {
if (update.petId === petId) {
ws.send(JSON.stringify(update));
}
});
}
WebSocket Protocol
WebSocket starts with an HTTP upgrade request:
GET /v1/ws HTTP/1.1
Host: api.petstoreapi.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Server responds:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
After the handshake, the connection switches to WebSocket protocol. Both sides can send messages.
WebSocket Strengths
1. Bidirectional
Both client and server send messages anytime. No need for separate HTTP requests.
2. Low latency
WebSocket has minimal overhead. Messages arrive faster than HTTP requests.
3. Binary support
WebSocket handles binary data natively. No base64 encoding needed.
4. Efficient
After the initial handshake, WebSocket frames have minimal overhead (2-14 bytes per message).
5. No connection limits
WebSocket connections don't count toward browser HTTP connection limits.
WebSocket Weaknesses
1. More complex
WebSocket requires: - Handshake handling - Frame parsing - Ping/pong for keep-alive - Manual reconnection logic - State management
2. No automatic reconnection
If the connection drops, you must reconnect manually:
function connectWebSocket() {
const ws = new WebSocket('wss://api.petstoreapi.com/v1/ws');
ws.onclose = () => {
console.log('Disconnected, reconnecting...');
setTimeout(connectWebSocket, 1000);
};
return ws;
}
3. Firewall issues
Some corporate firewalls and proxies block WebSocket. You need fallback mechanisms.
4. Scaling challenges
WebSocket connections are stateful. Scaling requires: - Sticky sessions (route clients to the same server) - Or shared state (Redis, database) - Or message brokers (RabbitMQ, Kafka)
When to Use WebSocket
Chat applications: Real-time messaging, group chat Collaborative editing: Google Docs-style collaboration Gaming: Multiplayer games, real-time game state Trading platforms: Live price updates, order execution IoT: Device control, sensor data streaming
Use WebSocket when: - You need bidirectional communication - Low latency is critical - You're sending binary data - Clients send frequent updates
Combining Both
You don't have to choose one. Use both for different features.
Example: Pet adoption platform
SSE for notifications:
// Receive notifications
const notifications = new EventSource('/v1/notifications');
notifications.onmessage = (event) => {
showNotification(JSON.parse(event.data));
};
WebSocket for chat:
// Real-time chat with adoption coordinator
const chat = new WebSocket('wss://api.petstoreapi.com/v1/chat');
chat.onmessage = (event) => {
displayMessage(JSON.parse(event.data));
};
// Send messages
sendButton.onclick = () => {
chat.send(JSON.stringify({ message: messageInput.value }));
};
SSE handles one-way notifications. WebSocket handles bidirectional chat. Each protocol does what it does best.
Decision Framework
Choose SSE if: - Updates flow from server to client only - You want simplicity and automatic reconnection - You need to work through restrictive firewalls - You're building notifications, feeds, or monitoring
Choose WebSocket if: - You need bidirectional communication - Low latency is critical - You're sending binary data - You're building chat, gaming, or collaboration features
Use both if: - Different features have different requirements - You want the best tool for each job
Modern PetStore API Example
The Modern PetStore API supports both:
SSE for pet status updates:
GET /v1/pets/123/updates
Streams status changes, adoption updates, and medical records.
WebSocket for real-time chat:
wss://api.petstoreapi.com/v1/chat
Enables real-time communication between adopters and shelter staff.
Each protocol serves its purpose. The API is more flexible and performant as a result.
Key Takeaway: SSE is simpler for server-to-client updates. WebSocket is more powerful for bidirectional communication. Choose based on your requirements, not trends.
Real-time doesn't always mean WebSocket. Often, SSE is the better choice.