
How to Design Real-Time Market Data Subscription and Data Caching Mechanisms for Stock APIs?
Learn how to design a real-time stock API architecture using WebSocket subscriptions, Redis caching, stale-update protection, and separate storage paths for live quotes and historical market data.
When a stock application has only a few symbols and users, requesting the latest quote is relatively simple. The architecture becomes more complicated when the application needs continuous real-time stock data, multiple subscribed symbols, and many users requesting the same market information at the same time.
A practical stock API architecture should separate two jobs: getting market updates and serving the latest market state efficiently. WebSocket is well suited to the first job, while a cache such as Redis can handle the second.
Start With a Real-Time Market Data Subscription Layer
Polling a stock market API every few seconds can work for a small application, but it becomes inefficient when the number of symbols increases. Each request creates additional network traffic, while the application may still miss updates between polling intervals.
A persistent WebSocket connection provides a better model for continuously changing data. The server can push new market updates as they arrive instead of waiting for another client request.
A common architecture looks like this:
Market Data API → WebSocket Ingestion → Validation → Cache → Application
The important design decision is to avoid creating one upstream connection for every end user. Instead, an ingestion service can maintain the market-data connection, normalize incoming messages, and publish the latest state to other application services.
Subscribe to Real-Time Stock Data With WebSocket
AllTick provides WebSocket access for real-time market data, including stock quotes. For example, a subscription can identify a Hong Kong stock such as 700.HK.
A simplified subscription structure is:
import json
import websocket
WS_URL = "wss://quote.alltick.co/quote-stock-b-ws-api?token=YOUR_TOKEN"
defon_open(ws):
request = {
"cmd_id": 22002,
"seq_id": 1,
"trace": "stock-demo",
"data": {
"symbol_list": [
{"code": "700.HK"}
]
}
}
ws.send(json.dumps(request))
defon_message(ws, message):
data = json.loads(message)
print(data)
ws = websocket.WebSocketApp(
WS_URL,
on_open=on_open,
on_message=on_message
)
ws.run_forever()
The production version should add heartbeat handling, reconnect logic, logging, and message validation. A reconnect should not blindly overwrite the cache with the first message received after a connection is restored. The application should also distinguish between connection recovery and actual market-data freshness.
Cache the Latest Market State Instead of Every Tick
Real-time feeds can generate many updates, but a quotation page often needs only the latest price, volume, and timestamp.
Instead of sending every update directly to the database, keep the current state in a fast cache:
{
"symbol": "700.HK",
"price": 320.50,
"volume": 125000,
"timestamp": 1787041200000
}
Redis is a practical choice for this pattern. A key such as:
stock:quote:700.HK
allows application services to retrieve the latest state without repeatedly querying the upstream stock market data API.
This does not mean historical data should be discarded. A useful architecture normally separates the hot path from long-term storage:
| Data | Recommended storage | Purpose |
|---|---|---|
| Latest quote | Redis/cache | Fast application access |
| Tick stream | Stream/database | Processing and analysis |
| Historical K-line | Database/object storage | Backtesting and research |
Prevent Stale Updates From Overwriting New Data
One of the easiest problems to overlook is message ordering.
Suppose an application receives update A with timestamp 1000, followed by update B with timestamp 1010. If a delayed copy of A arrives later, an unconditional cache write could make the displayed price older than the actual latest update.
A simple safeguard is:
if new_tick["timestamp"] > cached_tick["timestamp"]:
update_cache(new_tick)
For a larger system, consider sequence numbers when available, message deduplication, reconnect recovery, and monitoring for gaps in the incoming stream.
Also define a policy for stale cache entries. A quote that has not changed for a while is not necessarily wrong—the market itself may be inactive. The application should distinguish between no market update and no data connection rather than treating both as missing data.
Keep Real-Time and Historical Data on Separate Paths
A real-time subscription should not be expected to solve every market-data requirement.
Live data can feed quotation pages, monitoring systems, and quantitative calculations. Historical K-lines and stored Tick data serve different jobs, such as backtesting and research.
This separation also makes scaling easier. The real-time path can optimize for low-latency delivery, while historical storage can optimize for retention, querying, and analytical workloads.
A Practical Architecture for a Stock API
For most applications, the workflow can be kept straightforward:

The subscription layer handles continuous real-time market data. The cache reduces repeated upstream requests. Validation protects against stale or malformed updates, while persistent storage preserves the data needed for later analysis.
If you are implementing the WebSocket layer, the next practical step is to verify the current subscription parameters and connection requirements in the AllTick API documentation. After that, you can test one symbol first, add reconnect and cache validation, and only then expand to more symbols and application users.
The same architecture can also combine WebSocket streaming with REST requests when an application needs both continuous updates and on-demand market-data queries.