OKX · API Advanced
Streaming OKX Live Market Data with WebSocket: Why Advanced Strategies Drop REST Polling
Run a strategy long enough and you'll probably hit this awkward spot: the logic is clearly fine, but live fills always lag half a beat. Stare at the code and the problem usually isn't the strategy itself—it's how you're getting market data: still polling over REST, asking "what's the price now" over and over. That kind of polling has built-in latency, and crank the frequency up and it trips rate limits. To make a strategy react faster, you usually want to switch market-data intake from polling to WebSocket push.
In this piece we (the MeowQuant desk) make WebSocket on OKX clear: how it differs from REST polling, why advanced strategies use it, what channels OKX has, how to write push handlers with ccxt.pro's watch_ticker and watch_orders, how to handle reconnect and heartbeat, and—importantly—who it's for. The conclusion up front: this is advanced material, and beginners' simple strategies are fine on REST, so don't go to WS early just to "look professional."
REST polling vs WebSocket push
The difference is clear with one everyday analogy. REST polling is like phoning someone every few seconds to ask "any news?"—most of the time the answer is "no," so the call was wasted. WebSocket is like having an open line to them that never hangs up: the moment they have news they just say it, and you ask nothing.
In technical terms:
| Dimension | REST polling (fetch_) | WebSocket push (watch_) |
|---|---|---|
| How you get data | You actively send requests over and over | One long-lived connection; the exchange pushes |
| Latency | Bound by polling interval, inherently behind | Pushed the moment data changes, low latency |
| Request cost | High-frequency polling easily trips rate limits | One connection, barely consumes request quota |
| Your own orders/account | Have to query repeatedly to know fill status | Private channels push order fills and account changes in real time |
| Implementation complexity | Synchronous, intuitive, easy to debug | Async, must handle reconnect and heartbeat |
| Best for | Beginners, low-frequency strategies | Advanced, timing-sensitive strategies |
In short: REST is simple but slow, WebSocket is fast but takes more code. Which you pick depends on how much your strategy cares about "fast."
Why a strategy uses WS
Distilling the comparison above into three points where a strategy benefits:
- Low latency, faster reactions. The price gets pushed to you the moment it changes, so the strategy can react earlier. For strategies riding the chop or grabbing spreads, that bit of latency difference can be the line between profit and not.
- Fewer requests, no rate limits. WS is one long-lived connection and doesn't fire requests repeatedly like polling, so it naturally doesn't trip rate limits. When you want to watch many pairs, this advantage is especially clear.
- Know your own state in real time. Private channels actively push your order fills, position changes, and balance changes, so you don't keep running
fetch_open_ordersto query. The strategy adjusts more promptly off this, and saves endpoint calls too.
But note the "benefit" has a precondition: your strategy has to actually be timing-sensitive. If you run a low-frequency grid that checks once an hour, that bit of WS latency advantage means nothing to you, and you've just imported complexity for nothing.
OKX WS channels
OKX's WebSocket (API v5) channels split roughly into two kinds; understand this division and the code that follows goes smoothly:
Public channels. They push public market data, anyone can subscribe, no authentication needed. Common ones:
- tickers: latest trade price, best bid/ask, 24-hour change, etc.
- candles: candle updates across timeframes.
- books: order-book bid/ask depth.
- trades: every individual trade happening in the market.
Private channels. They push data tied to your own account, and subscribing needs API Key authentication (still the apiKey, secret, Passphrase trio). Common ones:
- orders: your order status changes and fill reports.
- positions: changes to your futures positions.
- account: balance, margin, and other changes.
One line to remember: public channels for the market, private channels for yourself. You can subscribe to public channels only for a market-driven strategy, or subscribe to private channels at the same time to track your own order results in real time.
Streaming market data with ccxt.pro (watch_ticker)
The easiest way to connect WS is still through ccxt—its WebSocket extension is called ccxt.pro, now merged into the main ccxt package. Regular ccxt goes over REST with the fetch_ family; ccxt.pro goes over WS with the watch_ family. Because it's a long-lived connection underneath, the code has to be written async.
The block below subscribes to BTC/USDT market data and prints on every price update:
import asyncio
import ccxt.pro as ccxtpro # WS version, the watch_ family
async def main():
okx = ccxtpro.okx() # public market data needs no auth
symbol = 'BTC/USDT'
try:
while True:
ticker = await okx.watch_ticker(symbol) # returns only on new data
print(ticker['symbol'], ticker['last'])
finally:
await okx.close() # close the long-lived connection on exit
asyncio.run(main())
The key difference from REST is in await okx.watch_ticker(...): you're not actively pulling, you're "hanging there waiting for a push," and it returns only on new market data. Put it in a while True loop and it becomes a continuous stream of market data. Note the await okx.close() at the end—a long-lived connection should be closed actively when you're done, don't leave it dangling.
Connecting the private order channel (watch_orders)
Beyond public market data, an advanced move is watching your own orders at the same time. watch_orders pushes to you the moment your order status changes (partial fill, full fill, cancel), far more promptly than running fetch_open_orders repeatedly. It's a private channel, so you carry the credentials:
import asyncio
import ccxt.pro as ccxtpro
async def main():
okx = ccxtpro.okx({
'apiKey': 'YOUR_apiKey',
'secret': 'YOUR_secret',
'password': 'YOUR_Passphrase', # private channels need auth
})
try:
while True:
orders = await okx.watch_orders() # pushed whenever your orders change
for o in orders:
print(o['symbol'], o['side'], o['status'], o.get('filled'))
finally:
await okx.close()
asyncio.run(main())
With this, your strategy gets the report the instant an order fills, and can immediately place the next order or adjust the position off it, instead of waiting for the next polling round to discover "oh, that one filled." This is key for grid and market-making strategies that need to relay orders fast. The private channel still uses your live API Key (key creation in the API quant intro), with only "Read" and "Trade" ticked, never "Withdraw."
OK30001 for a fee discount that applies to API orders too. Sign up for OKX and open the API here →
Reconnect and heartbeat
The biggest real-world problem with a long-lived connection is: it will drop. A network stutter, a routine exchange restart, and the connection is gone. How steady your WS strategy is depends largely on how you handle drops. Two things are mandatory:
Heartbeat / keep-alive. The WebSocket protocol relies on periodic ping/pong to confirm the connection is still alive. If there's no activity for a while, the exchange treats the connection as dead and cuts it. Fortunately ccxt.pro generally handles keep-alive internally for you; you mainly need to make sure the program doesn't block for a long time and stall the message send/receive.
Reconnect + re-subscribe. A dropped connection has to reconnect automatically, and after reconnecting you must re-subscribe the channels and re-query the current real state (e.g. re-pull current open orders and positions), because you can't receive pushes that happened during the drop. The common pattern is wrapping the watch_ call in a try/except, catching connection-class exceptions and reconnecting and re-subscribing:
import asyncio
import ccxt.pro as ccxtpro
async def main():
okx = ccxtpro.okx()
symbol = 'BTC/USDT'
while True:
try:
ticker = await okx.watch_ticker(symbol)
print(ticker['last'])
except ccxtpro.NetworkError as e:
print('Connection issue, reconnecting and re-subscribing shortly:', e)
await asyncio.sleep(2) # back off before reconnecting, don't hammer
# after reconnecting, re-query current positions/open orders as needed
except Exception as e:
print('Other exception:', e)
await asyncio.sleep(2)
asyncio.run(main())
The point here isn't how pretty the code is, it's that principle: after a drop, don't assume the state hasn't changed—re-query before continuing. This is the same thing we cover in quant risk control as "check state first, then act after reconnecting"—the market may have moved while you lost contact, and blindly carrying on invites trouble.
Tested: latency comparison
fetch_ticker every 3 seconds, the other took pushes with ccxt.pro's watch_ticker. The result was plain to see—the polling side's price updates jumped clearly "notch by notch," with up to nearly 3 seconds between two updates (because it asks once every 3 seconds); the WS side refreshed almost continuously, pushing the moment the market moved, and felt noticeably faster. We didn't try to nail it down to the millisecond (and a demo environment doesn't represent live networking), but "polling lags push" was obvious at a glance. For low-frequency strategies that bit of difference doesn't matter, but for a chop-riding or spread-grabbing strategy, those few seconds are a real disadvantage. This comparison also confirmed for us: strategies that should be on WS, get them on it early; the ones that don't need it, REST is actually the more carefree choice.
Who it's for
Back to the question most worth settling: do you need WebSocket. Our yardstick is simple—
Cases where we suggest REST first: you're a beginner; the strategy is low-frequency (checks every few minutes or even hours); the logic is simple and not latency-sensitive. REST is synchronous, intuitive, and easy to debug, and for these cases it's plenty, with none of the async-and-reconnect gotchas, so you can put your energy into the strategy itself.
Cases worth going to WS: the strategy is latency-sensitive (chop-riding, market-making, spread arbitrage); you're watching many pairs at once and polling starts tripping rate limits; you need real-time fill reports to relay orders. In these cases, the real-time responsiveness and efficiency WS brings are worth the extra async and reconnect code.
One line to sum up: don't go to WS early just to seem professional. Get the strategy running and smooth on REST first; once you genuinely hit the ceiling of polling—it's slow, or it's tripping rate limits—then upgrade to WebSocket. That order is the least effort and the least likely to trip you up on technical complexity before you've even figured out the strategy. Whichever you use, verify new code on the demo account first.
FAQ
What's the difference between REST polling and WebSocket push?
REST polling is you actively asking the exchange over and over "what's the price now," sending a request and waiting for a response each time—that adds latency and easily trips rate limits. WebSocket establishes one long-lived connection, and the exchange pushes new data to you the moment it has it, so you don't keep asking. For strategies that need to react to the market fast, push beats polling on both real-time responsiveness and efficiency.
Why should a strategy use WebSocket?
Three reasons: one, low latency—the price gets pushed the instant it changes, so you react faster; two, fewer requests—no high-frequency polling, so you naturally don't trip rate limits; three, private channels deliver your own order fills and account changes in real time, so you don't keep querying. For grid, market-making, and high-frequency strategies sensitive to timing, WS is practically standard. But for simple beginner strategies, REST polling is usually enough.
What channels does OKX WebSocket have?
Roughly two kinds. Public channels push public market data, like the ticker, candles, order-book depth, and trades, and anyone can subscribe without authentication. Private channels push data tied to your own account, like order updates, position changes, and account balance, and need API Key authentication to subscribe. Public for the market, private for yourself.
Is ccxt.pro the same thing as regular ccxt?
ccxt.pro is ccxt's WebSocket extension, wrapping each exchange's WS into a unified interface too. Regular ccxt goes over REST (the fetch_ family); ccxt.pro goes over WS (the watch_ family, like watch_ticker and watch_orders). ccxt.pro is now merged into the main ccxt package, so once ccxt is installed you just use the async watch_ methods. Underneath it's a long-lived connection, so the code has to be written as an async loop.
What do I do when the WebSocket connection drops?
Long-lived connections inevitably drop—network stutters, exchange restarts, all possible. Two things to do: one, heartbeat/keep-alive—periodically send a ping or rely on the library's keep-alive so the connection isn't judged dead; two, reconnect—catch the connection exception, reconnect automatically, and after reconnecting re-subscribe the channels and re-query the current real state. With ccxt.pro's watch_ methods, putting them in a try/except loop and reconnecting and re-subscribing on drop is the common pattern.
Should a beginner use REST or WebSocket first?
Use REST first. REST is synchronous, intuitive, and easy to debug, and it's plenty for a beginner's simple strategy (like a low-frequency grid or a scheduled check). WebSocket introduces async, long-lived connections, and reconnect handling—added complexity. Once your strategy really is latency-sensitive, or polling starts tripping rate limits, it's not too late to upgrade to WS. Don't go to WS early just to "look professional."
WebSocket is a good tool, but it's meant for people who "already know why they need it." If you're still building the basics, get the API quant intro running first and fit your risk control—those matter far more than switching to WS. Once your strategy genuinely can't go any faster, come back to this piece.
Strategy needs more speed? Get your account and API ready first
WebSocket, private channels, real-time order pushes—all of it starts with a properly-permissioned API Key. A new account registered with the invite code gets a fee discount, and orders placed via the API get it too—lay the basics first, then talk advanced.
Crypto asset prices swing violently, and futures and leverage can lead to a total loss of capital. Quant and automated trading don't guarantee profit—use only funds you can afford to lose.