Crypto Orderbook Depth API: A Trader's Complete Guide
A complete guide to using crypto orderbook depth APIs. Covers REST and WebSocket endpoints for Binance, Bybit, and OKX with real Python code examples and trading insights.
A complete guide to using crypto orderbook depth APIs. Covers REST and WebSocket endpoints for Binance, Bybit, and OKX with real Python code examples and trading insights.
The orderbook is the closest thing crypto trading has to reading minds. Every limit order sitting on Binance, Bybit, or OKX is a declared intention — and when you query the depth API, you're pulling back the curtain on exactly where the market is ready to absorb buys and sells. For algorithmic traders, this data is the foundation of everything from spread capture to large-order execution to predictive signal generation. The challenge isn't access — most major exchanges offer orderbook depth APIs for free — it's knowing what to pull, how to parse it, and what patterns actually matter.
A raw price ticker tells you the last trade price. The orderbook depth tells you what happens next. At any moment, the depth snapshot shows you all resting bid orders (buyers waiting) and ask orders (sellers waiting), organized by price level with their cumulative quantities. This gives you three immediately useful data points: the best bid-ask spread, the wall of liquidity nearest to the current price, and the imbalance between buying and selling pressure.
Depth specifically refers to how far from the mid-price you need to go to fill a given order size without significant slippage. A market with 50 BTC resting within 0.1% of mid-price has very different execution characteristics than one with 2 BTC in the same zone. Platforms like OKX and Binance both expose depth snapshots up to 5000 levels via their APIs, though for most trading strategies the top 20-50 levels contain 90% of the actionable information.
Depth data is a snapshot — it reflects resting orders at that instant. High-frequency market makers update quotes hundreds of times per second, so a REST snapshot older than 500ms may already be stale in fast markets. Use WebSocket streams for anything latency-sensitive.
Binance's REST depth endpoint requires no authentication for public market data, which makes it the easiest starting point. The endpoint returns bids and asks as arrays of [price, quantity] pairs, sorted from best to worst. The `limit` parameter controls depth: valid values are 5, 10, 20, 50, 100, 500, 1000, and 5000. Larger limits increase response size and latency, so start with 20-50 levels unless your strategy requires full book visibility.
import requests
def get_orderbook_depth(symbol="BTCUSDT", limit=20):
url = "https://api.binance.com/api/v3/depth"
params = {"symbol": symbol, "limit": limit}
try:
response = requests.get(url, params=params, timeout=5)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
print(f"HTTP error: {e.response.status_code} — {e.response.text}")
return None
except requests.exceptions.RequestException as e:
print(f"Connection error: {e}")
return None
data = get_orderbook_depth()
if data:
best_bid_price = float(data["bids"][0][0])
best_bid_qty = float(data["bids"][0][1])
best_ask_price = float(data["asks"][0][0])
best_ask_qty = float(data["asks"][0][1])
spread = best_ask_price - best_bid_price
spread_pct = spread / best_bid_price * 100
print(f"Best bid: {best_bid_price} x {best_bid_qty} BTC")
print(f"Best ask: {best_ask_price} x {best_ask_qty} BTC")
print(f"Spread: {spread:.2f} USDT ({spread_pct:.4f}%)")
print(f"Sequence: {data['lastUpdateId']}")
Notice the `lastUpdateId` field in the response. This sequence number is critical when you need to stitch REST snapshots together with WebSocket delta updates — you only apply WebSocket updates whose `u` (final update ID) is greater than your snapshot's `lastUpdateId`. Ignoring this will result in a corrupted book state that produces nonsensical signals.
Bybit's v5 API unified their spot, linear, and inverse markets under a single endpoint, which is a significant ergonomic improvement over their older v2/v3 fragmentation. The key difference from Binance: Bybit requires a `category` parameter (`spot`, `linear`, `inverse`, `option`) and uses `retCode: 0` for success rather than HTTP status codes alone. Always check `retCode` explicitly — Bybit can return HTTP 200 with an error payload.
import requests
def get_bybit_orderbook(symbol="BTCUSDT", category="spot", limit=25):
url = "https://api.bybit.com/v5/market/orderbook"
params = {
"category": category,
"symbol": symbol,
"limit": limit
}
response = requests.get(url, params=params, timeout=5)
data = response.json()
if data["retCode"] != 0:
raise ValueError(f"Bybit API error {data['retCode']}: {data['retMsg']}")
book = data["result"]
bids = book["b"] # [[price_str, qty_str], ...] best first
asks = book["a"] # [[price_str, qty_str], ...] best first
# Calculate cumulative bid-side liquidity within top 10 levels
top_bid_liquidity = sum(float(b[1]) for b in bids[:10])
top_ask_liquidity = sum(float(a[1]) for a in asks[:10])
imbalance = top_bid_liquidity / (top_bid_liquidity + top_ask_liquidity)
print(f"Top-10 bid liquidity: {top_bid_liquidity:.4f} BTC")
print(f"Top-10 ask liquidity: {top_ask_liquidity:.4f} BTC")
print(f"Order imbalance (bid %): {imbalance * 100:.1f}%")
return book
get_bybit_orderbook(symbol="BTCUSDT", category="linear")
Order imbalance — the ratio of bid volume to total volume in the top N levels — is one of the most widely studied short-term predictors of price direction. An imbalance consistently above 65% on the bid side in the top 10 Bybit linear futures levels has historically preceded upward price pressure within the next few seconds to minutes. This is the kind of signal that services like VoiceOfChain aggregate across exchanges to generate real-time trading alerts.
REST polling has a floor: even at one request per second, you're paying TCP handshake overhead on every call and you're almost certainly rate-limited to fewer calls than that at depth levels above 100. For any strategy that needs sub-second orderbook awareness — spread monitoring, iceberg detection, front-running prevention — WebSocket streaming is the only viable approach. Binance pushes diff depth updates every 100ms or 1000ms (selectable via stream name suffix), while OKX offers a snapshot-then-delta model over WebSocket that's excellent for maintaining a local copy of the full book.
import websocket
import json
import threading
local_book = {"bids": {}, "asks": {}}
def apply_depth_update(data):
# Remove price levels with qty=0, update others
for price, qty in data.get("b", []):
if float(qty) == 0:
local_book["bids"].pop(price, None)
else:
local_book["bids"][price] = float(qty)
for price, qty in data.get("a", []):
if float(qty) == 0:
local_book["asks"].pop(price, None)
else:
local_book["asks"][price] = float(qty)
def on_message(ws, message):
data = json.loads(message)
apply_depth_update(data)
sorted_bids = sorted(local_book["bids"].keys(), key=float, reverse=True)
sorted_asks = sorted(local_book["asks"].keys(), key=float)
if sorted_bids and sorted_asks:
best_bid = float(sorted_bids[0])
best_ask = float(sorted_asks[0])
spread_pct = (best_ask - best_bid) / best_bid * 100
print(f"Bid: {best_bid:.2f} | Ask: {best_ask:.2f} | Spread: {spread_pct:.4f}%")
def on_error(ws, error):
print(f"WebSocket error: {error}")
def on_close(ws, code, msg):
print("Stream closed")
# @depth updates every 100ms; @depth20 gives top-20 snapshot each tick
ws = websocket.WebSocketApp(
"wss://stream.binance.com:9443/ws/btcusdt@depth@100ms",
on_message=on_message,
on_error=on_error,
on_close=on_close
)
thread = threading.Thread(target=ws.run_forever)
thread.daemon = True
thread.start()
When maintaining a local book from WebSocket deltas, initialize it with a REST snapshot FIRST, then apply only delta messages where U (first update ID) <= lastUpdateId + 1 <= u (final update ID). Binance's documentation calls this the "How to manage a local order book" process — skip it and your local book will drift from reality within minutes.
Raw depth data is noise until you know what patterns to look for. These are the four signals that experienced algorithmic traders extract from orderbook depth on exchanges like Binance, Bybit, and Gate.io:
VoiceOfChain aggregates orderbook depth signals across multiple exchanges in real time, generating composite alerts when depth imbalance, spread anomalies, and price momentum align simultaneously. Rather than monitoring five exchange APIs independently, traders use platform signals to get notified when the setup actually forms.
| Exchange | Endpoint | Max Depth | WebSocket Update | Auth Required |
|---|---|---|---|---|
| Binance | /api/v3/depth | 5000 levels | 100ms / 1000ms | No (public) |
| Bybit | /v5/market/orderbook | 500 levels | 10ms (linear) | No (public) |
| OKX | /api/v5/market/books | 400 levels | Snapshot+delta | No (public) |
| Coinbase Adv. | /api/v3/brokerage/market/product_book | 250 levels | 50ms | No (public) |
| KuCoin | /api/v1/market/orderbook/level2 | 100 levels | Level2 diff | No (public) |
Orderbook depth APIs are one of the most information-dense and freely accessible data sources in crypto trading. Whether you're building a simple spread monitor, a slippage model for a larger trading desk, or feeding depth imbalance signals into a machine learning pipeline, the infrastructure is the same: a REST snapshot to initialize, WebSocket deltas to maintain the live state, and clean parsing logic that handles sequence gaps and zero-quantity level removals correctly. Start with Binance's public depth endpoint — it's the most documented and forgiving for beginners — then expand to Bybit and OKX as your strategy demands cross-exchange coverage. The traders who consistently extract edge from this data aren't doing anything exotic; they're just maintaining clean, low-latency book state while everyone else is working with stale snapshots.