◈   ⌘ api · Intermediate

OKX Spot Exchange API Documentation: A Trader's Guide

Master OKX spot exchange API documentation with real code examples, auth setup, and practical tips for automating crypto trading strategies.

Uncle Solieditor · voc · 29.03.2026 ·views 438
◈   Contents
  1. → OKX API Overview: REST vs WebSocket
  2. → Setting Up API Keys and Authentication
  3. → Fetching Spot Market Data
  4. → Placing and Managing Spot Orders
  5. → WebSocket Streaming for Real-Time Data
  6. → Frequently Asked Questions
  7. → Putting It All Together

OKX runs one of the deepest spot order books in crypto, and their API is the gateway to everything — real-time prices, order placement, account balances, and historical candles. If you're moving beyond manual clicking and building a trading bot or analytics system, understanding the OKX spot exchange API documentation is non-negotiable. This guide cuts through the official docs and shows you exactly how to authenticate, pull market data, and place orders — with working code you can run today.

OKX API Overview: REST vs WebSocket

OKX offers two primary interfaces: a REST API for request-response operations and a WebSocket API for real-time streaming. For spot trading automation, you'll use both. REST is your workhorse for placing and canceling orders, querying account state, and fetching historical OHLCV data. WebSocket handles the stuff that needs to be instant — live order book updates, trade streams, and your own order fills.

The base REST URL is https://www.okx.com and all spot market endpoints sit under /api/v5/. Compare this to Binance's /api/v3/ structure or Bybit's /v5/ — the patterns are similar enough that if you've worked with one, OKX will feel familiar. The key difference is OKX's unified account model, which means your spot and margin positions share the same account namespace.

Setting Up API Keys and Authentication

Before writing a single line of code, create your API key in the OKX web interface under Account > API. You'll get three credentials: API Key, Secret Key, and Passphrase. The passphrase is OKX-specific — Binance and Coinbase don't use one — so don't skip it. Store all three in environment variables, never hardcoded in your scripts.

OKX uses HMAC-SHA256 signatures for authenticated requests. The signature is built from a concatenation of timestamp, HTTP method, request path, and body. Here's a complete authentication setup in Python:

import hmac
import hashlib
import base64
import time
import os
import requests

API_KEY = os.environ['OKX_API_KEY']
SECRET_KEY = os.environ['OKX_SECRET_KEY']
PASSPHRASE = os.environ['OKX_PASSPHRASE']
BASE_URL = 'https://www.okx.com'

def sign(timestamp: str, method: str, path: str, body: str = '') -> str:
    message = timestamp + method.upper() + path + body
    mac = hmac.new(
        SECRET_KEY.encode('utf-8'),
        message.encode('utf-8'),
        digestmod=hashlib.sha256
    )
    return base64.b64encode(mac.digest()).decode()

def get_headers(method: str, path: str, body: str = '') -> dict:
    timestamp = str(time.time())
    return {
        'OK-ACCESS-KEY': API_KEY,
        'OK-ACCESS-SIGN': sign(timestamp, method, path, body),
        'OK-ACCESS-TIMESTAMP': timestamp,
        'OK-ACCESS-PASSPHRASE': PASSPHRASE,
        'Content-Type': 'application/json'
    }

# Test: fetch account balance
path = '/api/v5/account/balance'
response = requests.get(
    BASE_URL + path,
    headers=get_headers('GET', path)
)
print(response.json())
Always use IP whitelisting when creating OKX API keys for production bots. Without it, a leaked key can drain your account from anywhere in the world. Set withdraw permissions to OFF unless your bot specifically needs it.

Fetching Spot Market Data

Public market data endpoints require no authentication. This is where you pull ticker prices, order book depth, and historical candles. OKX spot instruments follow the format BTC-USDT, ETH-USDT — hyphenated pairs, always uppercase. This differs from Binance's BTCUSDT format, so keep that in mind if you're porting code between exchanges.

import requests

BASE_URL = 'https://www.okx.com'

def get_ticker(inst_id: str) -> dict:
    """Fetch best bid/ask and last price for a spot pair."""
    url = f'{BASE_URL}/api/v5/market/ticker'
    resp = requests.get(url, params={'instId': inst_id})
    resp.raise_for_status()
    data = resp.json()
    if data['code'] != '0':
        raise ValueError(f"OKX error: {data['msg']}")
    return data['data'][0]

def get_candles(inst_id: str, bar: str = '1H', limit: int = 100) -> list:
    """Fetch OHLCV candles. bar options: 1m, 5m, 15m, 1H, 4H, 1D"""
    url = f'{BASE_URL}/api/v5/market/candles'
    resp = requests.get(url, params={
        'instId': inst_id,
        'bar': bar,
        'limit': limit
    })
    resp.raise_for_status()
    data = resp.json()
    # Each candle: [ts, open, high, low, close, vol, volCcy, volCcyQuote, confirm]
    return data['data']

# Usage
ticker = get_ticker('BTC-USDT')
print(f"BTC last price: {ticker['last']} | Bid: {ticker['bidPx']} | Ask: {ticker['askPx']}")

candles = get_candles('ETH-USDT', bar='1H', limit=24)
print(f"Fetched {len(candles)} hourly candles for ETH-USDT")
print(f"Latest close: {candles[0][4]}")

One thing that trips people up: OKX returns candles in reverse chronological order by default — newest first. If you're feeding data into a library like pandas-ta or passing it to a signal engine like VoiceOfChain for backtesting, reverse the list first. The volume columns are also split: vol is base asset volume, volCcyQuote is the USDT-denominated volume most people actually want for liquidity assessment.

Placing and Managing Spot Orders

Order placement is where authentication matters. OKX's POST /api/v5/trade/order endpoint handles all order types: limit, market, post-only, IOC, and FOK. For spot trading, set tdMode to cash. If you're on a unified account and want cross-margin behavior, use cross instead — but cash is the right default for pure spot automation.

import json
import requests
# Assumes get_headers() from the auth setup above

def place_limit_order(
    inst_id: str,
    side: str,        # 'buy' or 'sell'
    price: str,
    size: str,        # quantity in base currency
    client_oid: str = ''
) -> dict:
    path = '/api/v5/trade/order'
    body = {
        'instId': inst_id,
        'tdMode': 'cash',
        'side': side,
        'ordType': 'limit',
        'px': price,
        'sz': size,
    }
    if client_oid:
        body['clOrdId'] = client_oid  # your own reference ID

    body_str = json.dumps(body)
    response = requests.post(
        BASE_URL + path,
        headers=get_headers('POST', path, body_str),
        data=body_str
    )
    result = response.json()
    if result['code'] != '0':
        raise ValueError(f"Order failed: {result['data'][0]['sMsg']}")
    return result['data'][0]

def cancel_order(inst_id: str, ord_id: str) -> dict:
    path = '/api/v5/trade/cancel-order'
    body = json.dumps({'instId': inst_id, 'ordId': ord_id})
    response = requests.post(
        BASE_URL + path,
        headers=get_headers('POST', path, body),
        data=body
    )
    return response.json()

# Place a limit buy for 0.01 BTC at $60,000
order = place_limit_order(
    inst_id='BTC-USDT',
    side='buy',
    price='60000',
    size='0.01',
    client_oid='my_bot_order_001'
)
print(f"Order placed: {order['ordId']}")

# Cancel it
cancel_result = cancel_order('BTC-USDT', order['ordId'])
print(f"Cancel status: {cancel_result['data'][0]['sCode']}")
Always pass a clOrdId (client order ID) when placing orders. If your network request times out mid-flight, you can query /api/v5/trade/order?clOrdId=your_id to check if the order landed — without risking a duplicate submission.

WebSocket Streaming for Real-Time Data

Polling REST endpoints for price updates is wasteful and slow. For any strategy that reacts to market moves — whether you're running a market maker on OKX, watching signals from VoiceOfChain, or monitoring a Bybit position hedge — WebSocket is the right tool. OKX's WebSocket API uses a subscribe/unsubscribe message pattern over a persistent connection.

import asyncio
import json
import websockets

async def stream_orderbook(inst_id: str):
    uri = 'wss://ws.okx.com:8443/ws/v5/public'
    subscribe_msg = {
        'op': 'subscribe',
        'args': [{
            'channel': 'books5',   # top 5 bids/asks
            'instId': inst_id
        }]
    }

    async with websockets.connect(uri, ping_interval=20) as ws:
        await ws.send(json.dumps(subscribe_msg))
        print(f'Subscribed to {inst_id} order book')

        async for raw_msg in ws:
            msg = json.loads(raw_msg)
            if msg.get('event') == 'subscribe':
                continue  # confirmation, skip

            data = msg.get('data', [])
            if not data:
                continue

            book = data[0]
            best_bid = book['bids'][0][0] if book['bids'] else 'N/A'
            best_ask = book['asks'][0][0] if book['asks'] else 'N/A'
            print(f"{inst_id} | Bid: {best_bid} | Ask: {best_ask}")

# Run it
asyncio.run(stream_orderbook('BTC-USDT'))

For private WebSocket channels (order fills, position updates), you need to send an authentication message immediately after connecting — before subscribing to any private channel. The auth message uses the same HMAC-SHA256 signature scheme as REST, with the path set to /users/self/verify and an empty body. OKX's official documentation covers this in the WebSocket Authentication section, and it's worth reading carefully because the timestamp format must be a Unix timestamp in seconds as a string, not milliseconds.

OKX WebSocket Public Channels for Spot Trading
ChannelDataUpdate Frequency
tickersLast price, 24h vol, bid/ask100ms
books5Top 5 bids and asks100ms
booksFull order book (400 levels)On change
tradesIndividual trade executionsReal-time
candle1m1-minute OHLCV candles500ms

Frequently Asked Questions

Does OKX have a sandbox or testnet for API testing?
Yes. OKX provides a demo trading environment accessible at https://www.okx.com/demo-trading. You create separate API keys for the demo environment, and the REST base URL changes to https://www.okx.com with a special demo flag in the header: x-simulated-trading: 1. This lets you test order placement and WebSocket streams without risking real funds.
What are OKX API rate limits for spot trading?
Market data endpoints (tickers, candles, order book) allow 20 requests per 2 seconds per IP. Authenticated trade endpoints like order placement allow 60 requests per 2 seconds per user ID. If you exceed limits, OKX returns a 429 error with a Retry-After header. Using WebSocket instead of polling REST reduces rate limit pressure significantly.
Why is my OKX API signature returning a 50111 error?
Error 50111 means signature mismatch. The most common causes are: timestamp drift (your system clock is more than 30 seconds off UTC), using the wrong string encoding for the secret key, or including a trailing slash in the path when OKX doesn't expect one. Sync your system clock with an NTP server and double-check that your concatenation order is timestamp + method + path + body.
Can I use the OKX API to trade all spot pairs or only major ones?
All listed spot pairs on OKX are accessible via the API, including altcoins and newer listings. Use GET /api/v5/public/instruments?instType=SPOT to fetch the full list of available instruments along with their minimum order sizes, tick sizes, and lot sizes. This endpoint is public and requires no authentication.
How does OKX spot API compare to Binance or Bybit for bot development?
All three are solid, but they differ in details. Binance has the most community libraries and StackOverflow coverage. Bybit's v5 API is very clean and well-documented. OKX's unified account model is an advantage if you're hedging across spot and derivatives in the same bot — you don't need to manage separate account balances. For pure spot automation, all three are comparable in reliability.

Putting It All Together

The OKX spot exchange API documentation is genuinely well-structured once you know where to look — the authentication pattern is consistent across all endpoints, the error codes are descriptive, and the WebSocket channels cover everything you'd need for a production system. Start with the public market data endpoints to validate your setup, add authentication for account queries, then layer in order management once you're confident in the signature logic.

For traders who want to combine algorithmic execution with intelligent signal generation, pairing your OKX API bot with VoiceOfChain gives you real-time on-chain signals — whale movements, exchange inflows, smart money positioning — that you can act on programmatically. The signal triggers from VoiceOfChain, your bot executes on OKX within milliseconds. That's the setup that closes the gap between spotting an opportunity and actually capturing it. Whether you're building on OKX, Bitget, Gate.io, or KuCoin, the principle is the same: faster, more informed execution wins.

◈   more on this topic
◉ basics Mastering the ccxt library documentation for crypto traders ⌂ exchanges Mastering the Binance CCXT Library for Crypto Traders ⌬ bots Best Crypto Trading Bots 2025: Profitable AI-Powered Strategies