Gold Center had a problem I've seen a few times with financial platforms: their data was stale before anyone had finished reading it. Every 30 seconds, the system pinged an exchange API, got a price, updated the dashboard. For a news site that's fine. For gold traders making buy and sell decisions, you may as well be reading yesterday's paper.
The brief was simple: get it under a second. No specific number, just "fast." What that actually required was three separate changes, and none of them were obvious until I'd spent some time understanding how the existing system was breaking.
WebSockets instead of polling
REST polling is what almost everyone builds first because it's easy to understand — you send a request, you get data. The problem is there's a hard floor on how fast you can go. At some point you're hammering the server with requests and still can't get below a few seconds.
I replaced it with a Node.js service that holds persistent WebSocket connections to all five exchanges. When a price changes, the exchange pushes it to us. We don't ask, we receive. That alone brought latency under 100ms for most updates.
The annoying part: every exchange uses different field names for the same data. "bid", "ask", "buy_price", "sell_price" — all meaning the same things, formatted differently. I wrote a normalisation layer that translates all five feeds into one consistent schema. The frontend doesn't know or care which exchange the data came from.
Redis as a safety net
Exchanges drop connections. It happens. Without a fallback, a dead WebSocket means traders stare at frozen prices and have no idea if the market is quiet or if something's broken.
I added Redis to store the last known good price for every symbol. When a feed goes offline, clients get the cached value with a stale: true flag and a timestamp. Traders can see the data is old and decide how to handle it — which is infinitely better than guessing.
Redis also handles rate limiting from the exchanges. Some cap how often they send updates, so the aggregator buffers ticks and flushes them at a steady cadence instead of letting bursts pile up and kill the connections.
The frontend was also slow
Even with 85ms server updates, the dashboard felt laggy. The reason: React re-rendering the entire price table on every tick. When you have 50 price rows updating multiple times per second, that's a lot of reconciliation work for no reason.
I moved to cell-level updates — each price cell subscribes to its own stream and updates through a ref, bypassing React's renderer entirely for the actual number. It's the kind of thing you only do when you've profiled it and confirmed it's the actual bottleneck. In this case, it was.
The result looks like a real trading terminal instead of a web page trying to be one.
Where it ended up
30 seconds down to 85ms average. 500+ active traders daily. 99.9% uptime over eight months. The Redis fallback has triggered four times during exchange outages — each time, traders got a clear stale-data warning in under two seconds instead of a blank screen and a support ticket.
The thing I'd do differently: I'd add the Redis fallback before the WebSocket layer, not after. We built the happy path first and the safety net second. It worked, but building resilience into the design from the start is always cheaper than bolting it on.
If you're building a real-time data platform and want similar results, read the full case study or get in touch — this is the kind of real-time backend work I do regularly.