TL;DR
Use Server-Sent Events (SSE) for one-way server-to-client updates like notifications and live feeds. Use WebSocket for bidirectional communication like chat and gaming. SSE is simpler and works over HTTP. WebSocket is more complex but supports two-way messaging. Modern PetstoreAPI implements both for different real-time use cases.
Introduction
You need real-time updates in your API. A pet’s status changes from “available” to “adopted”—clients need to know immediately. Do you use WebSocket or Server-Sent Events (SSE)?
Most developers default to WebSocket because it’s “more powerful.” But SSE is often the better choice. It’s simpler, works over standard HTTP, and handles reconnection automatically. WebSocket adds complexity you might not need.
Modern PetstoreAPI implements both protocols. SSE for pet status updates and order notifications. WebSocket for live auction bidding and real-time chat. Each protocol serves different use cases.
In this guide, you’ll learn the differences between SSE and WebSocket, see real examples from Modern PetstoreAPI, and discover when to use each protocol.
What Is Server-Sent Events (SSE)?
SSE is an HTTP-based protocol for streaming events from server to client.
How SSE Works
The client opens a connection and receives events as they happen:
const eventSource = new EventSource('https://petstoreapi.com/v1/pets/notifications');
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Pet update:', data);
};
eventSource.addEventListener('adoption', (event) => {
const data = JSON.parse(event.data);
console.log('Pet adopted:', data.petId);
});
Server sends events:
GET /v1/pets/notifications
Accept: text/event-stream
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
event: adoption
data: {"petId":"019b4132","userId":"user-456"}
event: status-change
data: {"petId":"019b4127","status":"AVAILABLE"}
SSE Features
1. One-way communication
Server pushes to client. Client can’t send messages back through the SSE connection (but can use regular HTTP requests).
2. Built on HTTP
Uses standard HTTP. Works through proxies, firewalls, and CDNs.
3. Automatic reconnection
If the connection drops, the browser automatically reconnects.
4. Event IDs for resume
Server can send event IDs. Client resumes from last received event:
id: 123
event: adoption
data: {"petId":"019b4132"}
id: 124
event: status-change
data: {"petId":"019b4127"}
If disconnected, client sends Last-Event-ID: 124 header to resume.
5. Simple protocol
Text-based format. Easy to debug with curl:
curl -N -H "Accept: text/event-stream" \
https://petstoreapi.com/v1/pets/notifications
SSE Limitations
1. One-way only
Client can’t send messages through SSE. Need separate HTTP requests for client-to-server communication.
2. Text-only
SSE sends text. Binary data must be base64-encoded.
3. Browser connection limits
Browsers limit SSE connections per domain (typically 6). Not an issue for most apps.
4. No built-in compression
HTTP compression works, but no protocol-level compression like WebSocket.
What Is WebSocket?
WebSocket is a full-duplex bidirectional protocol over a persistent connection.
How WebSocket Works
Client and server can both send messages anytime:
const ws = new WebSocket('wss://petstoreapi.com/auctions/019b4132');
// Send message to server
ws.send(JSON.stringify({
type: 'bid',
amount: 500
}));
// Receive messages from server
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Auction update:', data);
};
ws.onclose = () => {
console.log('Connection closed');
// Manual reconnection logic needed
};
Server can send anytime:
{"type":"bid","userId":"user-456","amount":550}
{"type":"outbid","newAmount":550}
Client can send anytime:
{"type":"bid","amount":600}
{"type":"watch","petId":"019b4132"}
WebSocket Features
1. Bidirectional
Both client and server can send messages anytime. True two-way communication.
2. Low latency
Persistent connection. No HTTP overhead per message. Ideal for gaming, chat, live collaboration.
3. Binary support
Can send binary data directly. No base64 encoding needed.
4. Custom protocol
Uses ws:// or wss:// (secure). Not HTTP after initial handshake.
5. Frame-based
Messages are framed. Can send partial messages and reassemble.
WebSocket Limitations
1. Complex setup
Requires WebSocket server. More complex than HTTP endpoints.
2. Manual reconnection
No automatic reconnection. You must implement retry logic.
3. Proxy issues
Some corporate proxies block WebSocket. HTTP proxies don’t understand ws://.
4. Stateful
Server must track connections. Harder to scale than stateless HTTP.
5. No HTTP features
Can’t use HTTP caching, status codes, or standard headers after handshake.
Side-by-Side Comparison
| Feature | SSE | WebSocket |
|---|---|---|
| Direction | Server → Client | Bidirectional |
| Protocol | HTTP | Custom (ws://) |
| Reconnection | Automatic | Manual |
| Browser Support | All modern | All modern |
| Proxy-friendly | Yes | Sometimes |
| Complexity | Simple | Complex |
| Binary Data | No (text only) | Yes |
| Latency | Low | Very low |
| Scalability | High (stateless) | Medium (stateful) |
| Use Case | Notifications, feeds | Chat, gaming, collaboration |
How Modern PetstoreAPI Uses Both
Modern PetstoreAPI implements both SSE and WebSocket for different scenarios.
SSE for Pet Updates
Endpoint: GET /v1/pets/notifications
const events = new EventSource(
'https://petstoreapi.com/v1/pets/notifications?userId=user-456'
);
events.addEventListener('adoption', (e) => {
const data = JSON.parse(e.data);
showNotification(`${data.petName} was adopted!`);
});
events.addEventListener('status-change', (e) => {
const data = JSON.parse(e.data);
updatePetStatus(data.petId, data.status);
});
Server implementation:
app.get('/v1/pets/notifications', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const userId = req.query.userId;
// Subscribe to pet updates
const subscription = petUpdates.subscribe(userId, (event) => {
res.write(`event: ${event.type}\n`);
res.write(`data: ${JSON.stringify(event.data)}\n\n`);
});
req.on('close', () => {
subscription.unsubscribe();
});
});
Use cases:
- Pet status changes (available → adopted)
- Order notifications (placed, shipped, delivered)
- Inventory updates
- Price changes
WebSocket for Live Auctions
Endpoint: wss://petstoreapi.com/auctions/{auctionId}
const ws = new WebSocket('wss://petstoreapi.com/auctions/019b4132');
// Place bid
function placeBid(amount) {
ws.send(JSON.stringify({
type: 'bid',
amount
}));
}
// Receive updates
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
switch (msg.type) {
case 'bid':
updateCurrentBid(msg.amount, msg.userId);
break;
case 'outbid':
showOutbidNotification(msg.newAmount);
break;
case 'auction-end':
showAuctionResult(msg.winner);
break;
}
};
Server implementation:
wss.on('connection', (ws, req) => {
const auctionId = req.params.auctionId;
const auction = auctions.get(auctionId);
ws.on('message', (data) => {
const msg = JSON.parse(data);
if (msg.type === 'bid') {
const result = auction.placeBid(msg.userId, msg.amount);
// Broadcast to all participants
auction.participants.forEach(participant => {
participant.send(JSON.stringify({
type: 'bid',
userId: msg.userId,
amount: msg.amount
}));
});
}
});
});
Use cases:
- Live auction bidding
- Real-time chat with support
- Collaborative pet care planning
- Live inventory updates during sales
Testing Real-Time APIs with Apidog
Apidog supports testing both SSE and WebSocket APIs.
Testing SSE
1. Create SSE request:
GET https://petstoreapi.com/v1/pets/notifications
Accept: text/event-stream
2. Validate events:
- Check event types
- Validate JSON payloads
- Test reconnection behavior
- Verify event IDs
3. Test scenarios:
- Connection drops
- Server restarts
- Event ordering
- Resume from last event
Testing WebSocket
1. Create WebSocket connection:
wss://petstoreapi.com/auctions/019b4132
2. Send test messages:
{"type":"bid","amount":500}
{"type":"watch","petId":"019b4132"}
3. Validate responses:
- Check message formats
- Test bidirectional flow
- Verify connection handling
- Test error scenarios
4. Test scenarios:
- Multiple concurrent connections
- Message ordering
- Connection timeouts
- Reconnection logic
When to Use Each
Use SSE When:
- One-way updates - Server pushes to client, client doesn’t need to send back
- Simple setup - Want to use standard HTTP infrastructure
- Automatic reconnection - Don’t want to implement retry logic
- Proxy-friendly - Need to work through corporate firewalls
- Notifications - Status updates, alerts, live feeds
Examples:
- Pet adoption notifications
- Order status updates
- Inventory changes
- Price alerts
- News feeds
Use WebSocket When:
- Bidirectional - Both client and server send messages frequently
- Low latency critical - Gaming, real-time collaboration
- Binary data - Sending images, audio, video
- Custom protocol - Need full control over message format
- High message frequency - Hundreds of messages per second
Examples:
- Live auction bidding
- Real-time chat
- Multiplayer games
- Collaborative editing
- Live video streaming
Don’t Use WebSocket Just Because:
❌ “It’s more advanced” - Complexity without benefit
❌ “Everyone uses it” - SSE is often simpler
❌ “It’s faster” - SSE is fast enough for most use cases
❌ “It’s bidirectional” - Do you actually need bidirectional?
Conclusion
SSE and WebSocket both enable real-time communication, but they’re designed for different scenarios. SSE excels at one-way server-to-client updates with automatic reconnection and HTTP compatibility. WebSocket shines for bidirectional, low-latency communication.
Modern PetstoreAPI shows how to use both protocols effectively. SSE for notifications and status updates. WebSocket for live auctions and chat. Choose based on your use case, not which protocol seems “better.”
Test your real-time APIs with Apidog to ensure both SSE and WebSocket implementations work correctly across different scenarios.
FAQ
Can SSE work through corporate firewalls?
Yes. SSE uses standard HTTP, so it works through HTTP proxies and firewalls. WebSocket uses a custom protocol that some proxies block.
Is WebSocket faster than SSE?
WebSocket has slightly lower latency (no HTTP overhead per message), but for most applications, the difference is negligible. SSE is fast enough for notifications, feeds, and status updates.
How do you handle SSE reconnection?
The browser handles reconnection automatically. Send event IDs from the server, and the client will resume from the last received event using the Last-Event-ID header.
Can you use SSE with mobile apps?
Yes. iOS and Android support SSE through native HTTP clients or libraries. SSE works anywhere HTTP works.
What’s the maximum SSE connection time?
No hard limit. SSE connections can stay open indefinitely. Some proxies or load balancers may have timeouts (typically 30-60 seconds), but the browser will automatically reconnect.
Can WebSocket send binary data?
Yes. WebSocket supports both text and binary frames. You can send images, audio, or any binary data without base64 encoding.
How many SSE connections can a browser have?
Browsers limit SSE connections per domain (typically 6). This is rarely an issue—most apps only need 1-2 SSE connections.
Do you need a special server for SSE?
No. Any HTTP server can handle SSE. Just set the correct headers (Content-Type: text/event-stream) and keep the connection open.



