How Does Zerodha Show You Live Stock Prices? I Learned and Designed It From Scratch.
N I R A N J A N Developing in Web3 and AI
On this page
The Question That Started It
Polling — The Naive Approach
WebSockets — Stay Connected, Get Pushed
Two Data Flows Nobody Talks About
Pub/Sub — One Price, A Million Users
Redis Pub/Sub vs Kafka — Choosing the Right Tool
Time Series Database — Because History Matters
The Full Picture
The Takeaway
I opened Zerodha one day and just stared at the price ticker.
2847.50... 2847.75... 2848.00...
Every second. No refresh. No button. Just... live.
I'd built WebSocket apps before. Used Kafka in production. But I'd never sat down and thought about how all of it fits together behind a live stock price feed.
So I designed it. Here's everything I figured out.
The Question That Started It
What makes a stock ticker fundamentally different from loading a Twitter feed?
Twitter — you open the app, it fetches your feed, done. One request, one response. If you want new tweets, you pull to refresh.
A stock ticker — prices update every second automatically. No refresh. No pull. The page just knows.
Who's initiating those updates? Your browser or the server?
The server. And that changes everything about how you architect it.
Polling — The Naive Approach
The obvious solution: your browser makes an API call every second.
Browser → GET /price/RELIANCE → Server
Browser → GET /price/RELIANCE → Server (1 second later)
Browser → GET /price/RELIANCE → Server (1 second later)
...forever
This is called polling. It works. And it's terrible at scale.
Here's why. Zerodha has 10 million active users. Each browser polling every second = 10 million API requests per second. Just for one stock. There are thousands of stocks being watched simultaneously.
Most of those requests come back with the same price anyway. Nothing changed. You just hammered your server for no reason.
There's a smarter way.
WebSockets — Stay Connected, Get Pushed
Instead of your browser repeatedly asking "any updates?" — what if you just stayed on the line?
WebSockets establish a persistent, two-way connection between browser and server. One handshake to connect, then the connection stays open. The server pushes data the moment something changes — no request needed.
Polling:
Browser → "any update?" → Server (every second, forever)
WebSocket:
Browser ←————— connected —————→ Server
↑
Server pushes ONLY when price changes
The difference at scale is enormous. Instead of 10 million requests per second, you have 10 million open connections — and the server only sends data when a price actually changes.
Faster. Cheaper. Cleaner.
Two Data Flows Nobody Talks About
Here's the part that took me a second to realize.
Zerodha doesn't make up stock prices. They come from NSE — the National Stock Exchange, where actual trades happen.
Every buy, every sell, every price change on NSE gets broadcast to brokers — Zerodha, Groww, Angel One — in real time via a market data feed. Think of it like a live radio broadcast. NSE transmits, every subscribed broker receives.
So there are two completely separate data flows:
NSE → Zerodha servers (market data feed — one-way firehose)
Zerodha servers → You (WebSocket — persistent, two-way)
NSE doesn't know you exist. It just broadcasts everything to everyone subscribed. Zerodha's job is to receive that firehose, process it, and route the right prices to the right users.
That routing problem is where it gets interesting.
Pub/Sub — One Price, A Million Users
Zerodha has 10 million users. Each watching different stocks.
You can't open a separate WebSocket connection per stock per user — that's hundreds of millions of connections. You'd need a data center the size of a city.
The solution: Pub/Sub (Publish/Subscribe).
Think of it like YouTube channels. You don't get a separate video upload pipeline for each subscriber. One video gets uploaded (published), millions of people subscribe to the channel and receive it.
In Zerodha's case:
One topic per stock — "RELIANCE", "INFY", "TCS"
Server publishes price updates to the relevant topic
Users watching that stock subscribe to that topic
Price changes? Everyone subscribed gets it instantly via their WebSocket
NSE data feed
↓
Zerodha servers
↓
Pub/Sub topics (one per stock)
RELIANCE topic → all users watching RELIANCE
INFY topic → all users watching INFY
TCS topic → all users watching TCS
↓
WebSocket → user's browser
Clean routing. No wasted data. Each user only gets what they're watching.
The tool? Redis Pub/Sub — the same Redis you already know from caching and rate limiting. Built-in, fast, minimal setup.
Redis Pub/Sub vs Kafka — Choosing the Right Tool
I've used Kafka in production — built a pipeline in my Nautilus project that consumed ad auction events, transformed them, and produced to a Node.js service for SEAL encryption and Walrus storage.
So naturally I wondered: why not use Kafka here instead of Redis Pub/Sub?
The answer comes down to one thing: persistence.
Redis Pub/Sub — fire and forget. If a subscriber is offline when a message is published, that message is gone. No storage, no replay.
Kafka — messages persist in topics. If a consumer goes offline, messages wait. When it comes back, it picks up exactly where it left off.
For a live stock price ticker, Redis Pub/Sub is actually the right choice. Here's why:
If your phone dies for 5 minutes and you reopen Zerodha — you don't need the price from 5 minutes ago. You need the price right now. The missed updates are irrelevant.
Speed matters more than persistence for live prices. Redis Pub/Sub wins.
Kafka would be overkill here — heavier infrastructure, more complexity, for a guarantee you don't actually need.
Time Series Database — Because History Matters
But what about that mini price chart when you open a stock?
RELIANCE last 5 mins:
2847.50 → 2848.00 → 2849.25 → 2847.75 → 2848.50
Redis Pub/Sub threw those prices away the moment they were delivered. So where does the chart data come from?
A separate storage layer — and this is where the data shape matters.
Every price record looks like this:
stock: RELIANCE
timestamp: 09:15:01
price: 2847.50
Timestamp + value. Always appended, never updated. Always queried by time range — "give me the last 5 minutes" or "show me today's full chart."
A regular SQL database can handle this but it's not built for it. There's a database purpose-built for exactly this pattern — storing and querying massive amounts of time-stamped data efficiently.
It's called a Time Series Database (TSDB).
Popular choices:
InfluxDB — most popular for financial and IoT data
TimescaleDB — PostgreSQL under the hood, optimized for time series
Apache Druid — used at Uber, Netflix scale
So your system has two storage layers serving two different needs:
Redis Pub/Sub → live prices → instant delivery via WebSocket
Time Series DB → historical prices → charts, replays, analysis
The Full Picture
Here's the complete stock price ticker system end to end:
NSE broadcasts live trade data (market data feed)
↓
Zerodha servers receive the firehose
↓
Publish to Redis Pub/Sub topics (one per stock)
↓ ↓
Users subscribed via Write to Time Series DB
WebSocket get live prices (InfluxDB / TimescaleDB)
↓ ↓
Browser updates instantly Historical charts on demand
Real-time delivery. Historical storage. Two separate concerns, two separate tools.
The Takeaway
A live stock ticker looks like a simple number updating on a screen. Underneath it's a lesson in:
WebSockets over polling — persistent connections, server-pushed updates
Two data flows — market data feed from exchange, WebSocket to user
Pub/Sub routing — one topic per stock, subscribers get only what they watch
Right tool for the job — Redis Pub/Sub for real-time, Kafka for persistence
Time Series DB — purpose-built for timestamp + value data at scale
The pattern keeps repeating across every system I've designed: identify the data flow, decouple the components, pick the right storage for the access pattern.
Next up — Payment System. Idempotency, distributed transactions, double-spend prevention. The most complex one yet.
Follow me on X @nirxnjxn7 where I build stuff, break stuff, and write about both.
More posts like this at nirxnjxn-tech.hashnode.dev
