Every product brief that mentions “live updates”, “real-time dashboard” or “instant notifications” eventually lands on the same engineering question: how does data actually get from the server to the browser or the app without the user refreshing the page. There are three real answers – WebSockets, Server-Sent Events (SSE) and polling – and teams often reach for the most powerful one by default. That is usually the wrong instinct. The right choice depends on who needs to push data, how often, and what your infrastructure can realistically support.
What the three options actually do
Polling is the client asking “anything new?” on a timer. It is the simplest option, works through every proxy and firewall ever built, and scales the way any ordinary HTTP request scales. The cost is latency (you only find out about a change on the next poll) and wasted requests when nothing has changed.
Server-Sent Events is a single long-lived HTTP connection where the server streams events to the client as they happen. It is one-directional – server to client only – built on plain HTTP, and browsers reconnect automatically if the connection drops. It is a genuinely underused tool for the amount of engineering effort it saves.
WebSockets open a full-duplex connection: both sides can send messages at any time, with low overhead per message. That makes them the right fit when the client also needs to push data back continuously, not just receive it. It is also the option with the most infrastructure to think about.
The question that actually decides it
Strip away the framework opinions and the decision comes down to one thing: does the client need to send data as frequently as it receives it?
- If only the server pushes – notifications, live scores, a token stream from an AI model, a dashboard that updates when new data lands – SSE covers it with far less to operate than a WebSocket.
- If both sides need to push continuously and with low latency – chat, collaborative editing, multiplayer state, signalling for a video call – WebSockets are the right tool, because forcing that traffic through request/response HTTP adds overhead and complexity you would otherwise avoid.
- If updates are infrequent, or a delay of seconds to a minute is genuinely fine, polling is not a compromise – it is the correct answer, and adding a persistent connection would be solving a problem you do not have.
- Streaming AI-generated text token by token is a one-directional push from server to client – a natural fit for SSE, and simpler to operate than a WebSocket for the same result.
- Live transcription of an ongoing conversation involves continuous audio or partial-result data moving in something closer to both directions, and low latency matters to the experience – the kind of feature where a WebSocket earns its operational cost.
- Video calling needs its own dedicated real-time media protocol (WebRTC) for the audio and video itself, with WebSockets or SSE typically used alongside it for signalling and chat, not for the media stream.
- A fleet dashboard showing device telemetry or alert status is server-to-client and tolerant of a small delay – SSE, or even polling every few seconds, is usually sufficient and considerably cheaper to run at fleet scale than a persistent WebSocket per device.
- A rota or leave-approval tool where a manager’s decision needs to appear for the employee moments later is a good example of a feature that does not need sub-second delivery – polling on a short interval, or SSE if you already have the infrastructure, is enough.
- Does the client need to send data as often as it receives it? If not, you do not need a WebSocket.
- Is a delay of a few seconds actually a problem for this specific feature, or does it just feel more modern to remove it?
- Who is going to own reconnection, backoff and message ordering on the client, and has that been budgeted as ongoing work rather than a one-off task?
- Does your hosting platform support long-lived connections properly, or will you be fighting idle timeouts and connection limits from day one?
- What happens to shared state when you run more than one server instance – do you already have, or are you willing to add, a pub/sub layer?
- Could the corporate network of your actual users block or interfere with the connection type you are about to build on?
Most products that ask for “real-time everything” actually contain a mix of these once you look feature by feature. Treating the whole application as one transport decision is where the overengineering starts.
What each option costs you operationally
The functional differences get most of the attention in comparison articles. The operational differences are what actually bite a small engineering team six months in.
Scaling and shared state
Polling is stateless – any server behind a load balancer can answer any request, so horizontal scaling needs no special thought. SSE and WebSockets both hold a connection open on a specific server process, which means the moment you run more than one instance you need either sticky sessions or a pub/sub layer (Redis, or similar) so that a message published from one instance reaches a client connected to another. This is not a reason to avoid them, but it is infrastructure that has to be built, tested and monitored, not infrastructure you get for free.
Proxies, load balancers and serverless platforms
WebSockets need explicit support from everything in the request path – reverse proxies, load balancers, API gateways – and some serverless platforms either do not support long-lived WebSocket connections at all or charge for the connection duration rather than the work done. SSE, because it rides on plain HTTP, tends to pass through the same infrastructure with far fewer surprises, though it can still be closed early by a proxy with an aggressive idle timeout, which is worth checking before you commit to it. Corporate networks and older enterprise firewalls are also more likely to block or interfere with WebSocket upgrades than with either HTTP alternative – relevant if your buyer’s own IT policy is part of your target environment.
Client-side complexity
SSE reconnects automatically and delivers ordered events with an event ID you can use to resume a stream after a drop – the browser handles most of this natively. WebSockets give you none of that: reconnection, backoff, resuming from where the connection left off and de-duplicating messages are all your responsibility to build. That is a real and recurring cost, not a one-off, because it has to work correctly on flaky mobile connections, not just in a demo on office Wi-Fi.
Where this shows up in practice
A few categories of feature make the trade-offs concrete:
The trap worth naming
WebSockets get chosen by default more often than the requirements justify, usually because “real-time” in a brief gets read as “needs a persistent bidirectional connection” without checking whether the client side of that connection ever actually sends anything. The result is a small team carrying connection-scaling infrastructure, reconnection logic and sticky-session configuration for a feature that only ever pushed data one way. Six months later that infrastructure is still being maintained, and nobody remembers why it was needed in the first place. The fix is cheap: before the architecture gets decided, list the features that need live updates and, for each one, write down whether the client pushes as well as receives, and how much delay is actually acceptable.
A checklist before you commit
Answer those honestly per feature, rather than once for the whole product, and the choice between WebSockets, SSE and polling stops being a philosophical one and becomes a fairly mechanical decision – which is exactly what it should be.