AI Computer Institute
Expert-curated CS & AI curriculum aligned to CBSE standards. A bharath.ai initiative. About Us

Real-Time Data: WebSockets and Live Updates

📚 APIs & Data Engineering⏱️ 25 min read🎓 Grade 9
✍️ AI Computer Institute Editorial Team Updated: August 2026 CBSE-aligned · Peer-reviewed · 25 min read
Content curated by subject matter experts with IIT/NIT backgrounds. All chapters are fact-checked against official CBSE/NCERT syllabi.

The Problem: A Scoreboard That Refuses to Refresh Itself

Picture a cricket match app open on your phone during an India innings. A ball is bowled, Kohli flicks it through mid-wicket for four, and the number on your screen jumps from 114 to 118 within a second or two — without you touching a single button. Somewhere, a server "knew" the score had changed and pushed that change straight to your phone. How?

Your first instinct might be: "the app must be checking the score every second." That instinct is half right, and understanding exactly where it goes wrong — and what a better mechanism looks like — is the whole subject of this chapter. We are going to build, from the ground up, the two competing techniques web applications use to keep data fresh: repeatedly asking (called polling), and keeping a line permanently open so the server can speak whenever it wants (called a WebSocket). By the end, you will be able to read and reason about real WebSocket code, trace a live handshake, and calculate — with actual arithmetic — why one approach can be dozens of times more efficient than the other.

Recalling How the Web Normally Works

Before we can appreciate what is special about a WebSocket, we need to be precise about how an ordinary web request behaves, because a WebSocket is defined entirely in contrast to it. When your browser loads a normal webpage, or when an app calls a REST API, the pattern is always the same: the client sends one request, the server sends back one response, and then the connection is considered finished. Think of it like sending a postcard: you write your question, drop it in the box, and wait. The postman does not come back later, unprompted, to slip a new postcard through your door just because something changed at the other end. If you want fresh information, you have to write and send another postcard yourself.

This request-response pattern is called HTTP (HyperText Transfer Protocol), and it is deliberately stateless — the server does not keep the connection open or remember that you are "waiting" for anything after it answers you. This design is what makes ordinary websites simple, scalable, and reliable. But it creates an obvious gap: what happens when the client needs to know about something the moment it happens on the server, and the client has no way of knowing in advance when that will be?

The First Fix Anyone Tries: Polling

The most natural fix — and the one most beginner programmers reach for first — is to just keep asking. Every few seconds, the app quietly sends a fresh HTTP request: "has the score changed yet?" If yes, update the display. If no, throw the answer away and ask again shortly after. This technique is called polling, and it is exactly the postcard analogy taken to its logical extreme: instead of sending one postcard and waiting indefinitely, you send a new postcard every three seconds, forever, for as long as you care about the answer.

Here is what that looks like as real JavaScript running inside a browser tab:

// The polling approach: ask again and again
setInterval(function() {
  fetch("https://score.example.in/api/live-score")
    .then(function(response) { return response.json(); })
    .then(function(data) {
      document.getElementById("score").textContent = data.totalScore;
    });
}, 3000); // repeat every 3000 milliseconds = every 3 seconds

Trace through this carefully. setInterval is a built-in browser function that runs the function you give it once every 3000 milliseconds, forever, until something stops it. Each time it fires, fetch(...) opens a brand-new HTTP connection to the server, the server computes a fresh JSON response, sends it back, the connection closes, and the browser updates the on-screen score element. Three seconds later, the entire cycle repeats — a new TCP connection, a new HTTP request with a full set of headers, a new response, and a new close — regardless of whether the score changed even slightly in between.

Polling works. It is also simple enough that you could build it in an afternoon, using nothing but ordinary REST API skills. But notice the waste baked into its design: the vast majority of those requests will discover that nothing has changed. A cricket match spends far more time between balls than during them; a train's GPS position barely moves in three seconds; a stock price might sit still for a full minute. Every one of those "nothing changed" round trips still costs a full HTTP request and response, with all their overhead, for zero new information.

Doing the Arithmetic: How Wasteful Is Polling, Really?

Let's put real numbers on this instead of just asserting "it's wasteful." Suppose our cricket-score app polls every 3 seconds during a match, as in the code above.

First, how many requests does that produce in an hour?

Requests per hour = 3600 seconds ÷ 3 seconds per request
                   = 1200 requests

Now, every one of those 1200 requests carries HTTP headers on both sides — things like Host, User-Agent, Accept, an authentication token, and cookies on the way out; Content-Type, Content-Length, and caching directives on the way back. Real header sizes vary by app and server, but a reasonable typical estimate for a mobile API call is roughly 400 bytes of request headers and 200 bytes of response headers — about 600 bytes of pure overhead per round trip, before you even count the actual score data being carried. Let's multiply that out:

Header overhead per hour ≈ 1200 requests × 600 bytes
                          ≈ 720,000 bytes
                          ≈ 703 KB per hour

That is roughly 700 KB of pure bookkeeping every hour, spent on requests where the answer is usually "nothing changed, ask again later." Now compare that to how often the score actually changes. A ball is bowled roughly every 25–30 seconds in a typical over of cricket, so let's estimate about 144 genuinely new scoreboard events in an hour (3600 ÷ 25 ≈ 144). If a system only sent data when something truly changed, it would need to transmit information about 144 times an hour — not 1200 times.

That gap between 1200 wasted round trips and 144 genuine events is precisely the inefficiency that a different design can eliminate. That different design is the WebSocket.

A Better Idea: Keep the Line Open

Go back to the postcard analogy, but change it. Instead of mailing a fresh postcard every few seconds, imagine you and your friend are on a phone call that neither of you hangs up. Now either of you can speak the instant you have something to say — no need to "ask" first, no envelope, no waiting for a fixed interval. That is the essential idea of a WebSocket: a single, long-lived connection between client and server that both sides can send messages over, at any moment, in either direction, without repeating the connection setup every time.

This property — messages flowing in both directions simultaneously over one open connection — is called full-duplex communication, and it is the key technical difference from ordinary HTTP. Ordinary HTTP is half-duplex at the application level: the client always speaks first, and the server can only reply to a request that was just made — it can never speak up on its own. A WebSocket removes that restriction entirely. Once the connection is open, the server can push a new score to you the instant it happens, with no request from you triggering it.

How a Plain HTTP Connection Becomes a WebSocket

Here is something that surprises many students: a WebSocket connection does not start out looking any different from a normal HTTP request. It begins as an ordinary HTTP request carrying a special instruction that asks the server to change the rules of the connection. This is called the WebSocket handshake, and it looks like this:

GET /live/IND-AUS HTTP/1.1
Host: score.example.in
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

The two lines Upgrade: websocket and Connection: Upgrade are the actual request: "if you support it, please convert this plain HTTP connection into a WebSocket connection and keep it open." If the server understands and agrees, it does not send back a normal 200 OK. Instead, it replies with a distinctive status code that means "I am changing how this connection behaves from here on":

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Status code 101 literally means "Switching Protocols" — the server is telling both ends of the connection to stop talking HTTP and start talking the WebSocket protocol instead, on this exact same underlying network connection. The Sec-WebSocket-Accept value is not encryption and not a password; it is computed by the server by combining the client's Sec-WebSocket-Key with a fixed string defined in the WebSocket standard, hashing the result, and encoding it. Its only job is to prove that the responder genuinely understands the WebSocket protocol, rather than being, say, a confused proxy server that just echoed the headers back. After this single handshake, no further HTTP requests are needed — the connection stays open, and small binary-framed messages flow over it directly in both directions until either side decides to close it.

Notice, too, that a WebSocket connection is identified by the scheme ws:// (unencrypted) or wss:// (encrypted, the WebSocket equivalent of https://) rather than http://. In production apps you should always use wss://, exactly as you would insist on https:// for a normal site — an unencrypted ws:// connection can be read or tampered with by anyone on the same network.

Talking to a WebSocket From JavaScript

None of the handshake mechanics above need to be written by hand — the browser's built-in WebSocket object performs the handshake for you the moment you construct it. Here is a complete, working client:

const socket = new WebSocket("wss://score.example.in/live/IND-AUS");

socket.onopen = function() {
  console.log("Connected! Waiting for ball-by-ball updates...");
};

socket.onmessage = function(event) {
  const update = JSON.parse(event.data);
  console.log(update.over + " -- " + update.batsman + ": " + update.runs + " runs");
  document.getElementById("score").textContent = update.totalScore;
};

socket.onclose = function() {
  console.log("Connection closed. Match may have ended.");
};

socket.onerror = function(error) {
  console.log("Something went wrong:", error);
};

Let's trace this precisely, because each handler fires at a different, specific moment:

  • new WebSocket(...) immediately starts the handshake in the background — it does not block your code while waiting.
  • onopen fires exactly once, the instant the server has replied with 101 Switching Protocols and the connection is ready to carry messages.
  • onmessage fires every single time the server sends a new message — and only then. There is no loop, no timer, and no request being made by the client at all; the client is simply reacting to messages the server chose to push.
  • onclose fires once, whenever the connection ends — whether because the server closed it, the network dropped, or your own code called socket.close().
  • onerror fires if something goes wrong at the protocol or network level.

Suppose the server pushes this exact message the moment Kohli hits that boundary:

{"over": "14.3", "batsman": "Kohli", "runs": 4, "totalScore": 118}

Inside onmessage, event.data holds that text exactly as shown above. JSON.parse(event.data) converts it into a genuine JavaScript object, so update.over is the string "14.3", update.batsman is "Kohli", update.runs is the number 4, and update.totalScore is 118. The console.log line then prints exactly:

14.3 -- Kohli: 4 runs

and the on-screen score element's text becomes 118 — all without your code ever calling fetch, and without any timer deciding when to check.

What the Server Side Looks Like: Broadcasting to Many Fans at Once

A WebSocket connection is between exactly one client and the server — but a live score is watched by thousands of people simultaneously. The server therefore has to keep track of every open connection and decide, in its own code, who receives each message. Here is the shape of that logic (using Node.js and the popular ws library, simplified for clarity):

const WebSocket = require('ws');
const server = new WebSocket.Server({ port: 8080 });

server.on('connection', function(clientSocket) {
  console.log('A new fan connected to the score feed');

  clientSocket.on('close', function() {
    console.log('A fan disconnected');
  });
});

function broadcastUpdate(update) {
  const message = JSON.stringify(update);
  server.clients.forEach(function(client) {
    if (client.readyState === WebSocket.OPEN) {
      client.send(message);
    }
  });
}

// Called by the match-tracking system whenever the score actually changes:
broadcastUpdate({ over: "14.3", batsman: "Kohli", runs: 4, totalScore: 118 });

Trace it: server.on('connection', ...) registers a callback that fires once for every new fan who opens a WebSocket to this server; each fan's socket is added automatically to server.clients, a live set the library maintains for you. When a ball is bowled and the score genuinely changes, some other part of the system calls broadcastUpdate exactly once, with the new data. Inside it, server.clients.forEach loops over every currently connected fan, checks client.readyState === WebSocket.OPEN (skipping anyone mid-disconnect), and calls client.send(message) on each remaining open socket — which is precisely what triggers that fan's onmessage handler on the browser side, all within the same fraction of a second.

This is worth stating explicitly, because it is a genuinely common point of confusion: sending a message over a WebSocket is not automatically a broadcast to everyone. A single socket.send() call only reaches the one connection it was called on. Reaching many clients — the way a live score reaches every phone watching it — requires server code that deliberately loops over every connection, exactly as shown above. The "broadcast" behaviour is something the application programmer builds, not something WebSockets give you for free.

Redoing the Arithmetic With WebSockets

Now let's calculate the WebSocket side of our earlier comparison, using the same match. Instead of firing 1200 requests an hour regardless of whether anything changed, the server sends a message only when the score genuinely changes — our earlier estimate of about 144 times an hour.

The JSON payload we traced above, {"over": "14.3", "batsman": "Kohli", "runs": 4, "totalScore": 118}, is 66 bytes of text. The WebSocket protocol wraps each message in a small binary frame — as little as 2 bytes of overhead for a server-to-client message under 126 bytes (client-to-server messages add 4 more bytes for a masking key the protocol requires, but that does not apply here since the server is the sender). So each pushed update costs roughly 66 + 2 = 68 bytes.

Data per hour ≈ 144 updates × 68 bytes
              ≈ 9,792 bytes
              ≈ 9.6 KB per hour

Add the one-time handshake cost (a few hundred bytes, paid once when the connection opens, not every three seconds) and the total stays under 10 KB for the entire hour of live match data. Compare that to the roughly 700 KB per hour of pure header overhead polling generated — and remember, that 700 KB didn't even include a single byte of actual score data, since it was purely the cost of asking "anything new?" 1200 times. Even accounting for reasonable variation in these estimates, the WebSocket approach moves well over an order of magnitude less data to deliver the same information, and it delivers each update within milliseconds of the real event instead of up to 3 seconds late.

Correcting a Common Misconception

A very natural but incorrect belief is: "WebSockets are just a faster version of HTTP." This is wrong in an important way. A WebSocket is not a speed upgrade to HTTP requests — it is a fundamentally different kind of connection with different rules. HTTP is stateless and request-driven: the server is not allowed to speak until spoken to, and every exchange is independent of the last. A WebSocket connection is stateful: the server actively remembers which clients are connected (as we saw with server.clients above) for as long as the connection lasts, and either side may speak whenever it wants. This has real consequences: a WebSocket server needs to manage memory and connection state for every client that stays connected — potentially for hours — which is a genuinely harder engineering problem than handling a stream of independent, stateless HTTP requests that come and go. This is precisely why WebSockets are reserved for situations that truly need push-based, low-latency updates, rather than being used as a universal replacement for REST APIs everywhere.

Where This Sits Among Other Techniques

WebSockets are not the only tool for this problem, and a well-rounded understanding includes knowing the alternatives:

  • Polling (what we started with): simple to build, works everywhere, but wastes bandwidth and adds delay equal to the polling interval. Fine when near-real-time is good enough — for example, checking whether a background report has finished generating.
  • Long polling: the client sends a request as usual, but the server deliberately holds it open without replying until there is actually something new to say, then responds and the client immediately opens a new one. It reduces wasted "nothing changed" replies compared to plain polling, but still cannot let the server speak first the way a WebSocket can.
  • Server-Sent Events (SSE): a lighter-weight alternative to WebSockets, built directly on top of a single long-lived HTTP response, where the server can keep pushing new events to the client. It is simpler than a WebSocket to set up, but it only flows one way — server to client — so it fits things like a live news ticker but not a two-way chat.
  • WebSockets: the right tool when you need true two-way, low-latency communication — live scores, live chat, multiplayer games, or live financial market data, where either side may need to send something at any instant.

A concrete, real Indian example of this last category: brokerage platforms such as Zerodha document a WebSocket-based streaming API (part of their Kite Connect platform) specifically because stock and derivative prices change many times per second during market hours, and a trading app that only polled every few seconds would show its users stale prices at exactly the moments accuracy matters most. Multiplayer mobile games and live chat features rely on the same full-duplex idea — a persistent, two-way connection — for the same underlying reason: the alternative, event, is unpredictable, and only a connection that either side can speak on at any moment can deliver it without a built-in delay.

Check Your Understanding

  1. A weather-alert app polls a server every 10 seconds, 24 hours a day, to check for a new cyclone warning that in practice gets issued about once a week. Using the same style of arithmetic used above, roughly how many "nothing new" requests does it send in a single day, and why might a WebSocket (or SSE) be a better design here?
  2. Why does a WebSocket handshake begin as an HTTP request with an Upgrade header instead of using some completely separate protocol from the very first byte?
  3. In the server broadcast code above, what would happen to fans currently connected if the line server.clients.forEach(...) were changed to send the update only to the single socket that most recently connected? Would every fan's screen still update correctly?
  4. Explain, in your own words, why "sending a message on a WebSocket" and "broadcasting a message to everyone connected" are not the same thing, using the readyState === WebSocket.OPEN check as part of your answer.

Quick answers to check yourself: (1) 10-second polling over 24 hours is 8640 requests, essentially all of which report no new warning — a WebSocket lets the server push the one alert that matters within moments of it being issued, at a tiny fraction of the data cost. (2) Reusing the existing HTTP request lets the WebSocket handshake pass through the same servers, proxies, firewalls, and ports (typically 80 and 443) that already handle normal web traffic, instead of requiring entirely new network infrastructure. (3) Only that one most-recent fan would see the update; everyone else's socket would simply never receive a message and their screen would stay frozen at the old score, showing that broadcasting is a deliberate loop, not automatic. (4) send() only pushes to the specific socket it is called on; reaching "everyone" requires the server to loop over its own list of live connections and call send() once per connection, skipping any whose readyState shows they are no longer open.

Diagram: Polling vs. WebSocket Over the Same Hour of a Match

Same hour of a cricket match, two designs Polling (every 3s, always a full round trip) Client Server request "no change" request (score just changed!) score finally delivered, up to 3s late ...1200 request/response pairs every hour... WebSocket (one open connection, push on real change) Client Server handshake (Upgrade request) 101 Switching Protocols connection stays open, no requests needed pushed the instant the ball is bowled next real change pushed, still no request only ~144 pushes/hour, each ~68 bytes

Summary

Ordinary HTTP is a request-response protocol: the client must always ask first, and the server can never speak up on its own. When an app needs frequently changing data — a live score, a stock price, a chat message — the naive fix is polling: asking again every few seconds, which wastes bandwidth on "nothing changed" replies and still delivers news late by up to the polling interval. A WebSocket solves this properly by starting as a single HTTP request (the handshake, marked by Upgrade: websocket and answered with 101 Switching Protocols) that converts an ordinary connection into a persistent, full-duplex channel: once open, either side can send a message at any instant, with only a couple of bytes of framing overhead per message. On the client, the WebSocket object's onopen, onmessage, onclose, and onerror handlers react to events as they happen; on the server, code must explicitly track every open connection and loop over it to broadcast, since sending is never automatically "to everyone." The tradeoff is real: WebSockets demand that a server hold state and memory for every connected client over time, which is why they are reserved for situations — live sport, trading platforms, chat, multiplayer games — where true, low-latency, two-way updates matter enough to justify that extra engineering cost, rather than being used everywhere REST already works fine.

← Data Pipelines and ETL: From Raw Data to InsightsIndia's Open Data Ecosystem: Building with Government APIs →

Found this useful? Share it!

📱 WhatsApp 🐦 Twitter 💼 LinkedIn