TL;DR
Use Server-Sent Events (SSE) for one-way server-to-client updates such as notifications, status changes, and live feeds. Use WebSocket for bidirectional communication such as chat, gaming, and live bidding. SSE is simpler and runs over HTTP, while WebSocket provides lower-latency two-way messaging at the cost of additional complexity. Modern PetstoreAPI uses both protocols for different real-time scenarios.
Introduction
Suppose a pet changes from available to adopted. Clients need to receive that update immediately. Should you use WebSocket or Server-Sent Events (SSE)?
WebSocket is often the default because it supports more communication patterns. However, SSE is usually the better option when the server only needs to push updates. It uses standard HTTP, works with existing HTTP infrastructure, and includes browser-managed reconnection.
Modern PetstoreAPI uses both protocols:
- SSE for pet status updates and order notifications
- WebSocket for live auction bidding and real-time chat
The right choice depends on how data flows through your application.
If you are building or testing real-time APIs, Apidog supports both SSE and WebSocket testing. You can test event streams, validate message formats, and simulate reconnection scenarios.
This guide compares SSE and WebSocket, demonstrates both implementations with Modern PetstoreAPI examples, and explains when to use each protocol.
What Is Server-Sent Events (SSE)?
SSE is an HTTP-based protocol for streaming events from a server to a client.
How SSE Works
The client opens a long-lived HTTP connection and listens for events:
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);
});
The server responds with a text/event-stream:
GET /v1/pets/notifications HTTP/1.1
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"}
Each event ends with a blank line. The event field identifies the event type, and the data field contains the payload.
SSE Features
1. One-way communication
The server pushes messages to the client. The client cannot send messages through the SSE connection, but it can use regular HTTP requests for client-to-server actions.
2. Built on HTTP
SSE uses standard HTTP and can work with HTTP proxies, firewalls, and CDNs.
3. Automatic reconnection
When the connection drops, the browser automatically attempts to reconnect.
4. Event IDs for resuming
The server can assign IDs to events:
id: 123
event: adoption
data: {"petId":"019b4132"}
id: 124
event: status-change
data: {"petId":"019b4127"}
After disconnecting, the client sends the last received ID in the Last-Event-ID header so the server can resume the stream.
5. Simple text-based protocol
You can inspect an SSE stream directly with curl:
curl -N -H "Accept: text/event-stream" \
https://petstoreapi.com/v1/pets/notifications
SSE Limitations
- One-way only: Client-to-server communication requires separate HTTP requests.
- Text-only: Binary data must be encoded, such as with base64.
- Browser connection limits: Browsers typically limit SSE connections per domain to about six.
- No protocol-level compression: HTTP compression can still be used, but SSE does not provide a WebSocket-style compression mechanism.
What Is WebSocket?
WebSocket is a full-duplex protocol that keeps a persistent connection open for communication in both directions.
How WebSocket Works
The client and server can send messages at any time:
const ws = new WebSocket(
'wss://petstoreapi.com/auctions/019b4132'
);
ws.onopen = () => {
ws.send(JSON.stringify({
type: 'bid',
amount: 500
}));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Auction update:', data);
};
ws.onclose = () => {
console.log('Connection closed');
// Add manual reconnection logic when needed.
};
The server can send messages such as:
{"type":"bid","userId":"user-456","amount":550}
{"type":"outbid","newAmount":550}
The client can send messages such as:
{"type":"bid","amount":600}
{"type":"watch","petId":"019b4132"}
WebSocket Features
1. Bidirectional communication
Both the client and server can send messages whenever necessary.
2. Low latency
After the connection is established, messages do not require a new HTTP request. This makes WebSocket suitable for gaming, chat, and live collaboration.
3. Binary data support
WebSocket can send binary frames directly, so images, audio, and video do not need base64 encoding.
4. Custom protocol
Connections use ws:// or secure wss://. After the initial handshake, communication uses the WebSocket protocol.
5. Frame-based messages
WebSocket frames allow messages to be transmitted and reassembled independently.
WebSocket Limitations
- More complex setup: You need a WebSocket server and connection lifecycle management.
- Manual reconnection: Applications must implement retry and reconnection logic.
- Proxy compatibility issues: Some corporate proxies block or do not fully support WebSocket traffic.
- Stateful connections: The server must track active connections, which can make scaling more involved.
- Limited HTTP semantics after the handshake: You cannot rely on HTTP caching, status codes, or standard headers for each message.
SSE vs. WebSocket: Side-by-Side Comparison
| Feature | SSE | WebSocket |
|---|---|---|
| Direction | Server → client | Bidirectional |
| Protocol | HTTP | WebSocket (ws:// or wss://) |
| Reconnection | Automatic in browsers | Manual |
| Browser support | All modern browsers | All modern browsers |
| Proxy-friendly | Yes | Sometimes |
| Complexity | Simple | Complex |
| Binary data | No, text only | Yes |
| Latency | Low | Very low |
| Scalability | High for many stateless HTTP patterns | Medium because connections are stateful |
| Typical use cases | Notifications and feeds | Chat, gaming, and collaboration |
How Modern PetstoreAPI Uses Both
Modern PetstoreAPI chooses the protocol based on the communication pattern.
Use SSE for Pet Updates
Endpoint:
GET https://petstoreapi.com/v1/pets/notifications
A client can subscribe to updates for a specific user:
const events = new EventSource(
'https://petstoreapi.com/v1/pets/notifications?userId=user-456'
);
events.addEventListener('adoption', (event) => {
const data = JSON.parse(event.data);
showNotification(`${data.petName} was adopted!`);
});
events.addEventListener('status-change', (event) => {
const data = JSON.parse(event.data);
updatePetStatus(data.petId, data.status);
});
A Node.js server can create the stream like this:
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;
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();
});
});
SSE is a good fit for:
- Pet status changes, such as
available→adopted - Order notifications, such as placed, shipped, and delivered
- Inventory updates
- Price changes
Use WebSocket for Live Auctions
Endpoint:
wss://petstoreapi.com/auctions/{auctionId}
The client can place bids and process updates over the same connection:
const ws = new WebSocket(
'wss://petstoreapi.com/auctions/019b4132'
);
function placeBid(amount) {
ws.send(JSON.stringify({
type: 'bid',
amount
}));
}
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
switch (message.type) {
case 'bid':
updateCurrentBid(message.amount, message.userId);
break;
case 'outbid':
showOutbidNotification(message.newAmount);
break;
case 'auction-end':
showAuctionResult(message.winner);
break;
}
};
A server can process bids and broadcast them to all auction participants:
wss.on('connection', (ws, req) => {
const auctionId = req.params.auctionId;
const auction = auctions.get(auctionId);
ws.on('message', (data) => {
const message = JSON.parse(data);
if (message.type === 'bid') {
auction.placeBid(message.userId, message.amount);
auction.participants.forEach((participant) => {
participant.send(JSON.stringify({
type: 'bid',
userId: message.userId,
amount: message.amount
}));
});
}
});
});
WebSocket is a good fit for:
- Live auction bidding
- Real-time support chat
- Collaborative pet care planning
- Live inventory updates during sales
Testing Real-Time APIs with Apidog
Apidog supports testing both SSE and WebSocket APIs.
Test an SSE Endpoint
- Create an SSE request:
GET https://petstoreapi.com/v1/pets/notifications
Accept: text/event-stream
- Validate the event stream:
- Check event types.
- Validate JSON payloads.
- Verify event IDs.
- Confirm that events arrive in the expected order.
- Test failure and recovery scenarios:
- Drop the connection.
- Restart the server.
- Resume from the last event ID.
- Verify reconnection behavior.
Test a WebSocket Connection
- Create a connection:
wss://petstoreapi.com/auctions/019b4132
- Send test messages:
{"type":"bid","amount":500}
{"type":"watch","petId":"019b4132"}
- Validate responses:
- Check message formats.
- Test client-to-server and server-to-client flows.
- Verify connection handling.
- Test invalid messages and error responses.
- Test operational scenarios:
- Multiple concurrent connections
- Message ordering
- Connection timeouts
- Reconnection logic
When to Use SSE
Choose SSE when:
- The server is the primary sender.
- You want to use standard HTTP infrastructure.
- Browser-managed reconnection is useful.
- The connection must work through HTTP proxies and firewalls.
- You are delivering notifications, status updates, or feeds.
Examples include:
- Pet adoption notifications
- Order status updates
- Inventory changes
- Price alerts
- News feeds
When to Use WebSocket
Choose WebSocket when:
- Both client and server send messages frequently.
- Low latency is critical.
- The application needs binary frames.
- You need a custom message protocol.
- The application handles a high message frequency, such as hundreds of messages per second.
Examples include:
- Live auction bidding
- Real-time chat
- Multiplayer games
- Collaborative editing
- Live video streaming
Do Not Choose WebSocket Just Because It Seems More Advanced
Avoid adding WebSocket complexity when your application only needs server-to-client updates:
- “It’s more advanced.” Extra complexity does not provide value if you do not need bidirectional messaging.
- “Everyone uses it.” SSE may be a better fit for simple event streams.
- “It’s faster.” SSE is fast enough for many notification and status-update scenarios.
- “It’s bidirectional.” First confirm that the client actually needs to send messages through the persistent connection.
Conclusion
SSE and WebSocket both support real-time communication, but they solve different problems.
SSE is a practical choice for one-way server-to-client updates because it is simple, HTTP-compatible, and supports automatic browser reconnection. WebSocket is better for bidirectional, low-latency communication such as live auctions, chat, and gaming.
Modern PetstoreAPI uses SSE for notifications and status updates, and WebSocket for live auctions and chat. Choose the protocol based on your communication pattern—not on which protocol appears more powerful.
Test your real-time APIs with Apidog to verify event streams, message formats, reconnection behavior, and failure scenarios.
FAQ
Can SSE work through corporate firewalls?
Yes. SSE uses standard HTTP, so it generally works through HTTP proxies and firewalls. WebSocket uses a custom protocol that some proxies block.
Is WebSocket faster than SSE?
WebSocket can have slightly lower latency because messages do not include HTTP overhead after the handshake. For many applications, however, the difference is negligible, and SSE is fast enough for notifications, feeds, and status updates.
How do you handle SSE reconnection?
Browsers handle reconnection automatically. The server should send event IDs so the client can reconnect with the Last-Event-ID header and resume from the last received event.
Can you use SSE with mobile apps?
Yes. iOS and Android applications can consume SSE through native HTTP clients or libraries. SSE works anywhere HTTP works.
What is the maximum SSE connection time?
There is no protocol-defined maximum. An SSE connection can remain open indefinitely, although proxies or load balancers may impose timeouts, often around 30–60 seconds. The browser can reconnect when the connection closes.
Can WebSocket send binary data?
Yes. WebSocket supports both text and binary frames, allowing applications to send images, audio, or other binary data without base64 encoding.
How many SSE connections can a browser have?
Browsers typically limit SSE connections per domain to about six. This is rarely a problem because most applications only need one or two SSE connections.
Do you need a special server for SSE?
No. Any HTTP server can handle SSE. Set Content-Type: text/event-stream, keep the response open, and write events using the SSE format.
Top comments (0)