# OKX WebSocket API Documentation: The Complete Guide for Real-Time Trading If you've ever tried to build a crypto trading bot and found yourself refreshing REST endpoints every second like a nervous intern hitting F5 on their browser — yeah, that's not sustainable. WebSocket connections are the answer, and OKX has one of the better-documented WebSocket APIs in the exchange space right now. This guide walks you through everything: the endpoints, channels, authentication flow, subscription patterns, rate limits, and practical code examples. Whether you're building your first trading bot or migrating from REST to WebSocket on an existing system, this is the reference you'll actually want to keep open. --- ## What Is the OKX WebSocket API, and Why Should You Care? The OKX WebSocket API provides persistent, low-latency, bidirectional connections between your application and OKX's servers. Unlike REST APIs — where you poll for data and wait for a response each time — WebSocket keeps the connection alive and pushes data to you the moment something changes. For anyone doing anything real-time (order book monitoring, tick-by-tick trading, algorithmic execution), REST polling is genuinely too slow and too expensive in terms of rate limit consumption. WebSocket solves both problems at once. OKX's WebSocket API is built on v5, which covers spot trading, perpetual and expiry futures, options, and portfolio margin accounts. The architecture splits into two primary connection types: - **Public channels** — market data, no login needed - **Private channels** — account data, orders, positions, requires authentication 👉 [Get started on OKX with 20% fee rebate](https://okx.com/join/CASH20) --- ## WebSocket Endpoints OKX runs two main WebSocket gateway URLs: | Type | URL | |------|-----| | **Public** | `wss://ws.okx.com:8443/ws/v5/public` | | **Private** | `wss://ws.okx.com:8443/ws/v5/private` | The public endpoint handles all market data: tickers, trades, order books, candlestick data, funding rates, open interest, and instrument metadata. No API key required — connect and subscribe immediately. The private endpoint covers everything account-related: balance updates, order fills, position changes, and algo order activity. This endpoint requires an authenticated login message sent immediately after connection. There's also a **business endpoint** (`wss://ws.okx.com:8443/ws/v5/business`) introduced in newer API versions for specific algo and grid trading channels. Check OKX's official API reference for the latest channel-to-endpoint mapping, since some channels migrated between endpoints in recent updates. --- ## Authentication: Connecting to Private Channels This is where most people get tripped up. OKX uses HMAC SHA256 signatures, and the parameters need to be assembled in a specific order or the login will silently reject. After establishing the WebSocket connection to the private endpoint, send a login operation as the first message: json { "op": "login", "args": [ { "apiKey": "YOUR_API_KEY", "passphrase": "YOUR_PASSPHRASE", "timestamp": "1700000000", "sign": "BASE64_ENCODED_HMAC_SHA256_SIGNATURE" } ] } **How to generate the signature:** 1. Construct the pre-sign string: `{timestamp}GET/users/self/verify` 2. Sign it using HMAC SHA256 with your API secret as the key 3. Base64-encode the result The timestamp must be a Unix timestamp in **seconds** (not milliseconds), as a string. Using milliseconds is one of the most common auth failures. **Python example:** python import hmac import hashlib import base64 import time def generate_signature(api_secret: str) -> tuple[str, str]: timestamp = str(int(time.time())) message = f"{timestamp}GET/users/self/verify" signature = base64.b64encode( hmac.new(api_secret.encode(), message.encode(), hashlib.sha256).digest() ).decode() return timestamp, signature Once the login message is accepted, OKX responds with: json { "event": "login", "code": "0", "msg": "" } Code `0` means success. Any other code means something went wrong — check your timestamp drift (it needs to be within 30 seconds of server time), signature construction, or passphrase. --- ## Subscribing to Channels All subscriptions and unsubscriptions follow the same pattern: json { "op": "subscribe", "args": [ { "channel": "CHANNEL_NAME", "instId": "BTC-USDT" } ] } You can batch multiple channel subscriptions in a single message — just add more objects to the `args` array. The total length of the payload can't exceed 64 KB. --- ## Public Channels: What's Available Here's a comprehensive breakdown of what OKX exposes on the public endpoint: ### Market Data Channels **Tickers** Channel: `tickers` Pushes best bid/ask prices, 24h volume, and last trade price. Updates on every price change. json { "op": "subscribe", "args": [{"channel": "tickers", "instId": "BTC-USDT"}] } **Trades** Channel: `trades` Tick-by-tick trade feed. Every single trade that executes on the order book gets pushed here. High volume on liquid pairs — make sure your consumer can keep up. **Candlesticks / K-Lines** Channels: `candle1m`, `candle5m`, `candle15m`, `candle30m`, `candle1H`, `candle4H`, `candle1D`, `candle1W` Real-time OHLCV data. Each new candle tick pushes the current incomplete candle state, then the completed candle on close. ### Order Book Channels OKX offers multiple order book depth levels — choosing the right one matters for performance: | Channel | Depth | Update Type | Use Case | |---------|-------|-------------|----------| | `books` | 400 levels | Snapshot + delta | Full book reconstruction | | `books5` | 5 levels | Full snapshot every push | Quick best-bid/ask spread | | `books50-l2-tbt` | 50 levels | Tick-by-tick | Mid-depth market making | | `books-l2-tbt` | 400 levels | Tick-by-tick | HFT / full depth tick-by-tick | | `bbo-tbt` | Best bid/offer only | Tick-by-tick | Execution-focused bots | For the `books` channel, the first message is always a full snapshot. Subsequent messages are deltas — you maintain a local order book and apply the updates. When a price level appears with quantity `0`, it means that level was removed. **Funding Rate** (perpetuals only) Channel: `funding-rate` Pushes every time the funding rate updates. Essential for carry trading strategies. **Open Interest** Channel: `open-interest` Tracks total open contracts. Useful for sentiment and volatility signals. **Instruments** Channel: `instruments` Pushes when trading pairs are added, modified, or removed from OKX. Good for systems that need to track available markets dynamically. --- ## Private Channels: Account and Order Data Private channels require authentication first (see the login section above). **Account Channel** Channel: `account` Pushes balance and equity updates for your entire account. Fires on any change — deposits, withdrawals, trade fills, funding payments. json { "op": "subscribe", "args": [{"channel": "account"}] } **Orders Channel** Channel: `orders` Real-time order lifecycle events: submitted, partially filled, fully filled, canceled, rejected. You can scope it to a specific instrument type: json { "op": "subscribe", "args": [{"channel": "orders", "instType": "SPOT"}] } Valid `instType` values: `SPOT`, `MARGIN`, `SWAP`, `FUTURES`, `OPTION` **Positions Channel** Channel: `positions` Pushes position changes as they happen — quantity, average entry price, unrealized PnL. Scope by instrument type same as orders. **Balance and Position Channel** Channel: `balance_and_position` A combined feed that pushes atomically when both your balance and positions update together (e.g., on a trade fill). Useful for systems where you need to maintain a consistent state snapshot. --- ## Connection Management and Rate Limits OKX enforces a few limits you need to design around: | Limit | Value | |-------|-------| | Max WebSocket connections per API key | 100 | | Subscribe/unsubscribe requests per hour per connection | 480 | | REST requests (general) | 3/second per IP | | Payload size per message | 64 KB max | | Connection timeout (no subscription or data) | 30 seconds | **Heartbeat handling:** OKX uses a ping/pong mechanism. If you haven't received a message for ~20 seconds, send a `ping` text frame. OKX responds with `pong`. If you miss two consecutive pongs, assume the connection is dead and reconnect. Most WebSocket libraries handle ping/pong at the protocol level automatically — but OKX also accepts the string `"ping"` at the application level. For production systems, implement both. **Reconnection logic:** Connections drop. Plan for it. A simple exponential backoff with jitter (start at 1s, max at 60s) handles most cases gracefully. On reconnect, replay your subscription list and re-authenticate for private channels. --- ## Code Example: Full Setup in Python Here's a working skeleton for subscribing to BTC-USDT ticker data: python import asyncio import json import websockets WS_PUBLIC = "wss://ws.okx.com:8443/ws/v5/public" async def main(): async with websockets.connect(WS_PUBLIC) as ws: # Subscribe to BTC-USDT ticker await ws.send(json.dumps({ "op": "subscribe", "args": [{"channel": "tickers", "instId": "BTC-USDT"}] })) async for message in ws: if message == "pong": continue data = json.loads(message) if data.get("event") == "subscribe": print(f"Subscribed: {data}") elif "data" in data: print(f"Tick: {data['data']}") asyncio.run(main()) For private channel access with authentication: python import asyncio import json import websockets import hmac import hashlib import base64 import time API_KEY = "your_api_key" API_SECRET = "your_api_secret" PASSPHRASE = "your_passphrase" WS_PRIVATE = "wss://ws.okx.com:8443/ws/v5/private" def get_login_params(): ts = str(int(time.time())) msg = f"{ts}GET/users/self/verify" sig = base64.b64encode( hmac.new(API_SECRET.encode(), msg.encode(), hashlib.sha256).digest() ).decode() return ts, sig async def main(): async with websockets.connect(WS_PRIVATE) as ws: ts, sig = get_login_params() await ws.send(json.dumps({ "op": "login", "args": [{"apiKey": API_KEY, "passphrase": PASSPHRASE, "timestamp": ts, "sign": sig}] })) login_resp = json.loads(await ws.recv()) if login_resp.get("code") != "0": raise RuntimeError(f"Login failed: {login_resp}") # Subscribe to live orders await ws.send(json.dumps({ "op": "subscribe", "args": [{"channel": "orders", "instType": "SPOT"}] })) async for message in ws: data = json.loads(message) print(data) asyncio.run(main()) --- ## OKX Fee Tiers: What You Pay to Trade Trading fees matter a lot when you're running a high-frequency system or even moderate algo activity. OKX uses a tiered fee structure based on your 30-day trading volume or total account assets — whichever qualifies you for the higher tier. | Tier | Asset Requirement | 30-Day Volume | Spot Maker | Spot Taker | Futures Maker | Futures Taker | |------|------------------|---------------|------------|------------|---------------|---------------| | Regular | < $100K | < $500K | 0.080% | 0.100% | 0.020% | 0.050% | | VIP 1 | $100K+ | $500K+ | 0.045% | 0.060% | 0.010% | 0.030% | | VIP 2 | $200K+ | $2M+ | 0.040% | 0.055% | 0.005% | 0.025% | | VIP 3 | $500K+ | $10M+ | 0.035% | 0.050% | 0.002% | 0.020% | | VIP 4 | $1M+ | $50M+ | 0.030% | 0.045% | 0.001% | 0.018% | | VIP 5 | $2M+ | $200M+ | 0.025% | 0.040% | 0.000% | 0.015% | | VIP 6 | $5M+ | $500M+ | 0.020% | 0.030% | -0.005% | 0.014% | | VIP 7 | $10M+ | $1B+ | 0.010% | 0.025% | -0.008% | 0.013% | | VIP 8 | Institutional | $2B+ | -0.005% | 0.015% | -0.010% | 0.013% | A few things worth noting here: **Negative maker fees at VIP 6+** — OKX actually pays you for placing limit orders that add liquidity at higher tiers. This is a genuine market-making rebate, not a gimmick. **OKB token holdings** also factor into fee reductions independently of volume. Holding OKX's native OKB token can bump you up a fee tier without needing to hit the trading volume threshold. **New users registering with a referral code** receive a permanent fee discount applied hourly. Using the referral link with invitation code CASH20 locks in a 20% commission rebate on trading fees — which compounds meaningfully over any real trading volume. 👉 [Register on OKX and claim your 20% fee rebate](https://okx.com/join/CASH20) --- ## Common Mistakes and How to Avoid Them **Timestamp in milliseconds instead of seconds for auth** The OKX signature requires seconds. Using `int(time.time() * 1000)` instead of `int(time.time())` will break authentication every time. **Not handling delta updates correctly for order books** The `books` channel sends an initial snapshot followed by deltas. Many people process every message as a full snapshot and end up with corrupted order books. Track the `checksum` field OKX provides to verify your local book state matches their expected state. **Subscribing to too many instruments on one connection** You have 100 connections per API key, but each connection has a payload limit. For systems watching many instruments, distribute subscriptions across multiple connections. **Not implementing reconnection logic** Connections will drop during maintenance windows, network hiccups, and OKX's occasional rolling restarts. A production system needs automatic reconnection with state restoration. **Ignoring the `instType` field** Many channels require `instType` to be set for private subscriptions. Omitting it can result in incomplete data or subscription failures depending on the channel. --- ## Getting API Keys on OKX To use the private WebSocket channels, you'll need an API key: 1. Log in to OKX (or create an account — 👉 [join here with code CASH20 for a fee rebate](https://okx.com/join/CASH20)) 2. Go to **Profile → API** in the top-right menu 3. Create a new API key, set a strong passphrase, and configure permissions (read, trade, or withdraw — only enable what you need) 4. Save your API key, secret, and passphrase securely — the secret is only shown once For trading bots, enable "Trade" permission. For monitoring-only systems, "Read" is sufficient and safer. --- ## Wrapping Up The OKX WebSocket API is genuinely solid: consistent message format, comprehensive channel coverage, good documentation, and predictable rate limits. The authentication flow is a bit more involved than some exchanges, but once it's working it's stable. Key takeaways: - Use `wss://ws.okx.com:8443/ws/v5/public` for market data, no auth required - Use `wss://ws.okx.com:8443/ws/v5/private` for account/order data, send login message first - Timestamps in seconds, HMAC SHA256 + Base64 for the signature - Handle reconnections gracefully — connections will drop - Choose the right order book channel for your latency needs - New users get a 20% fee rebate using invitation code CASH20 For algo traders especially, the fee structure is worth optimizing for — at regular tier fees of 0.10% taker on spot, a moderately active bot accumulates meaningful costs fast. Starting with a referral discount while you build up volume toward VIP tiers is just a sensible move. 👉 [Open your OKX account with invitation code CASH20](https://okx.com/join/CASH20)