subscribeSubscribe to a real-time channel
Send
{ "op": "subscribe", "channel": "orderbook", "symbol": "BTC"}Receive
{ "type": "subscribed", "channel": "orderbook", "symbol": "BTC"}One socket for live subscriptions, replay, and continuity signals.
Connect once, keep the heartbeat alive, then issue subscribe or replay commands.
Connect to wss://api.0xarchive.io/ws with your API key.
const ws = new WebSocket( "wss://api.0xarchive.io/ws?apiKey=0xa_your_api_key");
ws.onopen = () => { console.log("Connected to 0xArchive WebSocket");};
ws.onmessage = (event) => { const data = JSON.parse(event.data); console.log("Received:", data);};import asyncioimport websocketsimport json
async def connect(): uri = "wss://api.0xarchive.io/ws?apiKey=0xa_your_api_key" async with websockets.connect(uri) as ws: print("Connected to 0xArchive WebSocket") async for message in ws: data = json.loads(message) print("Received:", data)
asyncio.run(connect())The server sends ping frames every 30 seconds. Clients that do not answer within 60 seconds are disconnected.
// Send a ping to keep connection alivews.send(JSON.stringify({ op: "ping" }));
// Server responds with:// {"type": "pong"}The same socket handles subscribe, unsubscribe, replay, and keep-alive commands.
Real-time Subscriptions
subscribeSubscribe to a real-time channel
Send
{ "op": "subscribe", "channel": "orderbook", "symbol": "BTC"}Receive
{ "type": "subscribed", "channel": "orderbook", "symbol": "BTC"}unsubscribeUnsubscribe from a channel
Send
{ "op": "unsubscribe", "channel": "orderbook", "symbol": "BTC"}Receive
{ "type": "unsubscribed", "channel": "orderbook", "symbol": "BTC"}Historical Replay
replayStart historical replay. Use "channel" for single or "channels" (array) for multi-channel synchronized replay. All channels must be from the same exchange. Use interval for candles (1m-1w). Use granularity for lighter_orderbook.
Send
{ "op": "replay", "channel": "trades", "symbol": "BTC", "start": 1704067200000, "end": 1704153600000, "speed": 10}Receive
{ "type": "replay_started", "replay_id": "rpl_abc123", "channel": "trades", "symbol": "BTC", "speed": 10}replay.pausePause current replay
Send
{ "op": "replay.pause"}Receive
{ "type": "replay_paused", "replay_id": "rpl_abc123"}replay.resumeResume paused replay
Send
{ "op": "replay.resume"}Receive
{ "type": "replay_resumed", "replay_id": "rpl_abc123"}replay.seekSeek to specific timestamp
Send
{ "op": "replay.seek", "timestamp": 1704100000000}Receive
{ "type": "replay_seeked", "replay_id": "rpl_abc123", "timestamp": 1704100000000}replay.stopStop current replay
Send
{ "op": "replay.stop"}Receive
{ "type": "replay_stopped", "replay_id": "rpl_abc123"}Utility
pingSend a ping to keep connection alive (server responds with pong)
Send
{ "op": "ping"}Receive
{ "type": "pong", "timestamp": "2026-01-01T12:00:00Z"}Channel inventory by venue, plus live vs replay availability and depth detail.
Historical data from April 2023. Symbols: BTC, ETH, SOL, etc.
Message sample
{ "type": "market_data", "channel": "orderbook", "symbol": "BTC", "timestamp": "2026-01-01T12:00:00Z", "data": { "bids": [ { "px": "42150.50", "sz": "2.5", "n": 5 } ], "asks": [ { "px": "42151.00", "sz": "1.8", "n": 3 } ], "mid_price": "42150.75" }}Message sample
{ "type": "market_data", "channel": "trades", "symbol": "BTC", "timestamp": "2026-01-01T12:00:00Z", "data": [ { "side": "B", "price": "42150.50", "size": "0.25", "trade_id": 12345678 } ]}Message sample
{ "type": "market_data", "channel": "candles", "symbol": "BTC", "timestamp": "2026-01-01T12:00:00Z", "interval": "1h", "data": { "open": "42100.00", "high": "42200.00", "low": "42050.00", "close": "42180.00", "volume": "125.5" }}Message sample
{ "type": "market_data", "channel": "open_interest", "symbol": "BTC", "timestamp": "2026-01-01T12:00:00Z", "data": { "open_interest": "12500.5", "mark_price": "42150.75" }}Message sample
{ "type": "market_data", "channel": "funding", "symbol": "BTC", "timestamp": "2026-01-01T12:00:00Z", "data": { "funding_rate": "0.0001", "premium": "0.00005" }}Message sample
{ "type": "market_data", "channel": "liquidations", "symbol": "BTC", "timestamp": "2026-01-01T12:00:00Z", "data": [ { "side": "A", "price": "42150.50", "size": "1.5", "trade_id": 12345678, "is_liquidation": true, "direction": "Close Long", "closed_pnl": -118.4 } ]}Message sample
{ "type": "market_data", "channel": "ticker", "symbol": "BTC", "timestamp": "2026-01-01T12:00:00Z", "data": { "price": "42150.75", "volume_24h": "1250000000" }}Message sample
{ "type": "market_data", "channel": "all_tickers", "symbol": "BTC", "timestamp": "2026-01-01T12:00:00Z", "data": [ { "symbol": "BTC", "price": "42150.75", "volume_24h": "1250000000" } ]}Message sample
{ "type": "market_data", "channel": "l4_diffs", "symbol": "BTC", "timestamp": "2026-01-01T12:00:00Z", "data": [ { "order_id": "0xabc", "user_address": "0x1234...abcd", "side": "B", "price": "42150.50", "size_delta": "0.5" } ]}Message sample
{ "type": "market_data", "channel": "l4_orders", "symbol": "BTC", "timestamp": "2026-01-01T12:00:00Z", "data": [ { "order_id": "0xabc", "user_address": "0x1234...abcd", "status": "filled", "side": "B", "price": "42150.50", "size": "1.2" } ]}Builder-deployed perpetuals on Hyperliquid. Historical trades, candles, orderbook, OI/funding, and liquidations begin in February 2026. Symbols: km:US500, hyna:BTC, etc.
Message sample
{ "type": "market_data", "channel": "hip3_orderbook", "symbol": "km:US500", "timestamp": "2026-01-01T12:00:00Z", "data": { "bids": [ { "px": "42150.50", "sz": "2.5", "n": 5 } ], "asks": [ { "px": "42151.00", "sz": "1.8", "n": 3 } ], "mid_price": "42150.75" }}Message sample
{ "type": "market_data", "channel": "hip3_trades", "symbol": "km:US500", "timestamp": "2026-01-01T12:00:00Z", "data": [ { "side": "B", "price": "42150.50", "size": "0.25", "trade_id": 12345678 } ]}Message sample
{ "type": "market_data", "channel": "hip3_candles", "symbol": "km:US500", "timestamp": "2026-01-01T12:00:00Z", "interval": "1h", "data": { "open": "42100.00", "high": "42200.00", "low": "42050.00", "close": "42180.00", "volume": "125.5" }}Message sample
{ "type": "market_data", "channel": "hip3_open_interest", "symbol": "km:US500", "timestamp": "2026-01-01T12:00:00Z", "data": { "open_interest": "12500.5", "mark_price": "42150.75" }}Message sample
{ "type": "market_data", "channel": "hip3_funding", "symbol": "km:US500", "timestamp": "2026-01-01T12:00:00Z", "data": { "funding_rate": "0.0001", "premium": "0.00005" }}Message sample
{ "type": "market_data", "channel": "hip3_liquidations", "symbol": "km:US500", "timestamp": "2026-01-01T12:00:00Z", "data": [ { "side": "A", "price": "42150.50", "size": "1.5", "trade_id": 12345678, "is_liquidation": true, "direction": "Close Long", "closed_pnl": -118.4 } ]}Message sample
{ "type": "market_data", "channel": "hip3_l4_diffs", "symbol": "km:US500", "timestamp": "2026-01-01T12:00:00Z", "data": [ { "order_id": "0xabc", "user_address": "0x1234...abcd", "side": "B", "price": "42150.50", "size_delta": "0.5" } ]}Message sample
{ "type": "market_data", "channel": "hip3_l4_orders", "symbol": "km:US500", "timestamp": "2026-01-01T12:00:00Z", "data": [ { "order_id": "0xabc", "user_address": "0x1234...abcd", "status": "filled", "side": "B", "price": "42150.50", "size": "1.2" } ]}Hyperliquid spot pairs. 294 pairs live from May 2026. Symbols use dash form (PURR-USDC, HYPE-USDC, HFUN-USDC); the bare token (e.g. HFUN) is also accepted. No funding, OI, or liquidations (perp-only constructs).
Message sample
{ "type": "market_data", "channel": "spot_orderbook", "symbol": "BTC", "timestamp": "2026-01-01T12:00:00Z", "data": { "bids": [ { "px": "42150.50", "sz": "2.5", "n": 5 } ], "asks": [ { "px": "42151.00", "sz": "1.8", "n": 3 } ], "mid_price": "42150.75" }}Message sample
{ "type": "market_data", "channel": "spot_trades", "symbol": "BTC", "timestamp": "2026-01-01T12:00:00Z", "data": [ { "side": "B", "price": "42150.50", "size": "0.25", "trade_id": 12345678 } ]}Message sample
{ "type": "market_data", "channel": "spot_l4_diffs", "symbol": "BTC", "timestamp": "2026-01-01T12:00:00Z", "data": [ { "order_id": "0xabc", "user_address": "0x1234...abcd", "side": "B", "price": "42150.50", "size_delta": "0.5" } ]}Message sample
{ "type": "market_data", "channel": "spot_l4_orders", "symbol": "BTC", "timestamp": "2026-01-01T12:00:00Z", "data": [ { "order_id": "0xabc", "user_address": "0x1234...abcd", "status": "filled", "side": "B", "price": "42150.50", "size": "1.2" } ]}Message sample
{ "type": "market_data", "channel": "spot_twap", "symbol": "BTC", "timestamp": "2026-01-01T12:00:00Z", "data": { "status": "ok" }}Binary outcome markets on Hyperliquid L1. Per-side coins are #-prefixed (e.g. #0 for Yes, #1 for No). No funding, no liquidations, no candles by design (build candles client-side from hip4_trades). Live data from May 2026.
Message sample
{ "type": "market_data", "channel": "hip4_orderbook", "symbol": "#0", "timestamp": "2026-01-01T12:00:00Z", "data": { "bids": [ { "px": "42150.50", "sz": "2.5", "n": 5 } ], "asks": [ { "px": "42151.00", "sz": "1.8", "n": 3 } ], "mid_price": "42150.75" }}Message sample
{ "type": "market_data", "channel": "hip4_trades", "symbol": "#0", "timestamp": "2026-01-01T12:00:00Z", "data": [ { "side": "B", "price": "42150.50", "size": "0.25", "trade_id": 12345678 } ]}Message sample
{ "type": "market_data", "channel": "hip4_open_interest", "symbol": "#0", "timestamp": "2026-01-01T12:00:00Z", "data": { "open_interest": "12500.5", "mark_price": "42150.75" }}Message sample
{ "type": "market_data", "channel": "hip4_l4_diffs", "symbol": "#0", "timestamp": "2026-01-01T12:00:00Z", "data": [ { "order_id": "0xabc", "user_address": "0x1234...abcd", "side": "B", "price": "42150.50", "size_delta": "0.5" } ]}Message sample
{ "type": "market_data", "channel": "hip4_l4_orders", "symbol": "#0", "timestamp": "2026-01-01T12:00:00Z", "data": [ { "order_id": "0xabc", "user_address": "0x1234...abcd", "status": "filled", "side": "B", "price": "42150.50", "size": "1.2" } ]}Data from August 2025 for fills, candles, open interest, and funding; January 2026 for L2 orderbook; March 2026 for L3 orderbook. Symbols: BTC, ETH, SOL, etc.
Message sample
{ "type": "market_data", "channel": "lighter_orderbook", "symbol": "BTC", "timestamp": "2026-01-01T12:00:00Z", "data": { "bids": [ { "px": "42150.50", "sz": "2.5", "n": 5 } ], "asks": [ { "px": "42151.00", "sz": "1.8", "n": 3 } ], "mid_price": "42150.75" }}Message sample
{ "type": "market_data", "channel": "lighter_trades", "symbol": "BTC", "timestamp": "2026-01-01T12:00:00Z", "data": [ { "side": "B", "price": "42150.50", "size": "0.25", "trade_id": 12345678 } ]}Message sample
{ "type": "market_data", "channel": "lighter_candles", "symbol": "BTC", "timestamp": "2026-01-01T12:00:00Z", "interval": "1h", "data": { "open": "42100.00", "high": "42200.00", "low": "42050.00", "close": "42180.00", "volume": "125.5" }}Message sample
{ "type": "market_data", "channel": "lighter_open_interest", "symbol": "BTC", "timestamp": "2026-01-01T12:00:00Z", "data": { "open_interest": "12500.5", "mark_price": "42150.75" }}Message sample
{ "type": "market_data", "channel": "lighter_funding", "symbol": "BTC", "timestamp": "2026-01-01T12:00:00Z", "data": { "funding_rate": "0.0001", "premium": "0.00005" }}Message sample
{ "type": "market_data", "channel": "lighter_l3_orderbook", "symbol": "BTC", "timestamp": "2026-01-01T12:00:00Z", "data": { "bids": [ { "px": "42150.50", "sz": "2.5", "n": 5 } ], "asks": [ { "px": "42151.00", "sz": "1.8", "n": 3 } ], "mid_price": "42150.75" }}Subscribe to proxied venue streams.
// Subscribe to real-time order book updatesws.send(JSON.stringify({ op: "subscribe", channel: "orderbook", symbol: "BTC"}));
// Subscribe to real-time tradesws.send(JSON.stringify({ op: "subscribe", channel: "trades", symbol: "ETH"}));
// Subscribe to L4 orderbook diffs (Pro+, real-time only)// Messages are batched by block and include user wallet addressesws.send(JSON.stringify({ op: "subscribe", channel: "l4_diffs", symbol: "BTC"}));
// Subscribe to L4 order lifecycle events (Pro+, real-time only)// Streams order state changes (new, partial fill, filled, cancelled)ws.send(JSON.stringify({ op: "subscribe", channel: "l4_orders", symbol: "ETH"}));
// Subscribe to HIP-3 L4 channelsws.send(JSON.stringify({ op: "subscribe", channel: "hip3_l4_diffs", symbol: "km:US500"}));
ws.send(JSON.stringify({ op: "subscribe", channel: "hip3_l4_orders", symbol: "km:US500"}));
// Subscribe to HIP-4 outcome-market channels// HIP-4 coins are #-prefixed: #0 = Yes side, #1 = No sidews.send(JSON.stringify({ op: "subscribe", channel: "hip4_orderbook", symbol: "#0"}));
ws.send(JSON.stringify({ op: "subscribe", channel: "hip4_l4_diffs", symbol: "#0"}));
// Unsubscribews.send(JSON.stringify({ op: "unsubscribe", channel: "orderbook", symbol: "BTC"}));L4 starts with a snapshot and follows with diff batches.
import websockets, json, asyncio
async def stream_l4(): uri = "wss://api.0xarchive.io/ws?apiKey=YOUR_KEY" async with websockets.connect(uri, max_size=20_000_000) as ws: # Subscribe to L4 diffs (works for l4_diffs, hip3_l4_diffs, or hip4_l4_diffs) await ws.send(json.dumps({ "op": "subscribe", "channel": "l4_diffs", "symbol": "BTC" }))
book = {"bids": {}, "asks": {}} snapshot_ts = None
async for msg in ws: data = json.loads(msg)
if data["type"] == "l4_snapshot": # Full book delivered first (every order including triggers) snapshot_ts = data["timestamp"] for order in data["data"]["bids"]: book["bids"][order["oid"]] = order for order in data["data"]["asks"]: book["asks"][order["oid"]] = order print(f"Snapshot: {len(book['bids'])} bids, {len(book['asks'])} asks")
elif data["type"] == "l4_batch" and snapshot_ts is not None: # Apply diffs to maintain the book for diff in data["data"]: if diff["ts"] <= snapshot_ts: continue side = "bids" if diff["side"] == "B" else "asks" oid = diff["oid"] if diff["dt"] == "new": book[side][oid] = { "oid": oid, "side": diff["side"], "price": diff["px"], "size": diff["sz"], "user_address": diff["user"] } elif diff["dt"] == "update": if oid in book[side]: book[side][oid]["size"] = diff["sz"] elif diff["dt"] == "remove": book[side].pop(oid, None)
asyncio.run(stream_l4())Replay historical data over the same socket, then feed it through your live event loop for backtests.
Replay historical data with original timing and adjustable speed.
// Start historical replay at 10x speed (Hyperliquid)ws.send(JSON.stringify({ op: "replay", channel: "orderbook", symbol: "BTC", start: 1681516800000, // Unix timestamp in ms end: 1681603200000, speed: 10 // 10x playback speed}));
// Start Lighter.xyz orderbook replay with granularityws.send(JSON.stringify({ op: "replay", channel: "lighter_orderbook", symbol: "BTC", start: 1706140800000, end: 1706227200000, speed: 10, granularity: "10s" // Options: checkpoint, 30s, 10s, 1s, tick}));
// Replay candles with specific intervalws.send(JSON.stringify({ op: "replay", channel: "candles", symbol: "ETH", start: 1681516800000, end: 1681603200000, speed: 10, interval: "1h" // Options: 1m, 5m, 15m, 30m, 1h, 4h, 1d, 1w}));
// Replay OI/funding dataws.send(JSON.stringify({ op: "replay", channel: "open_interest", symbol: "BTC", start: 1684540800000, end: 1684627200000, speed: 10}));
// Multi-channel synchronized replay (all channels must be same exchange)// Data is interleaved in timestamp order across channelsws.send(JSON.stringify({ op: "replay", channels: ["orderbook", "trades", "funding"], // Use "channels" (plural) for multi-channel symbol: "BTC", start: 1681516800000, end: 1681603200000, speed: 10}));
// Multi-channel Lighter replayws.send(JSON.stringify({ op: "replay", channels: ["lighter_orderbook", "lighter_trades", "lighter_funding"], symbol: "ETH", start: 1706140800000, end: 1706227200000, speed: 10, granularity: "10s" // Applies to lighter_orderbook channel}));
// Multi-channel replay sends "replay_snapshot" messages before the timeline// starts, providing initial state for each channel:// {"type": "replay_snapshot", "channel": "orderbook", "coin": "BTC", "symbol": "BTC", "timestamp": ..., "data": {...}}// {"type": "replay_snapshot", "channel": "funding", "coin": "BTC", "symbol": "BTC", "timestamp": ..., "data": {...}}// Then interleaved historical_data messages follow in timestamp order
// Pause replayws.send(JSON.stringify({ op: "replay.pause" }));
// Resume replayws.send(JSON.stringify({ op: "replay.resume" }));
// Seek to specific timestampws.send(JSON.stringify({ op: "replay.seek", timestamp: 1681550000000}));
// Stop replayws.send(JSON.stringify({ op: "replay.stop" }));gap_detected marks missing windows during replay.
// Gap detection during replayws.onmessage = (event) => { const msg = JSON.parse(event.data);
if (msg.type === 'gap_detected') { console.log(`Gap in ${msg.channel}/${msg.symbol}:`); console.log(` From: ${new Date(msg.gap_start).toISOString()}`); console.log(` To: ${new Date(msg.gap_end).toISOString()}`); console.log(` Duration: ${msg.duration_minutes} minutes`); }
// Handle other message types... if (msg.type === 'historical_data') { // Process data }};Gap thresholds
Feed synchronized historical market data through the same event loop you use for live handling.
import asyncio, json, websockets
async def backtest_strategy(): uri = "wss://api.0xarchive.io/ws?apiKey=0xa_your_api_key" async with websockets.connect(uri) as ws: # Replay orderbook + trades together at 50x speed await ws.send(json.dumps({ "op": "replay", "channels": ["orderbook", "trades"], "symbol": "BTC", "start": 1704067200000, # Jan 1 2024 "end": 1704153600000, # Jan 2 2024 "speed": 50 }))
orderbook = None trades = []
async for message in ws: msg = json.loads(message)
if msg["type"] == "replay_snapshot": # Initial state before timeline starts if msg["channel"] == "orderbook": orderbook = msg["data"]
elif msg["type"] == "historical_data": if msg["channel"] == "orderbook": orderbook = msg["data"] # Run strategy on each orderbook update signal = my_strategy(orderbook, trades) if signal: execute_paper_trade(signal, msg["timestamp"]) elif msg["channel"] == "trades": trades.append(msg["data"])
elif msg["type"] == "gap_detected": print(f"Data gap: {msg['duration_minutes']}min at {msg['gap_start']}")
elif msg["type"] == "replay_completed": print(f"Backtest done. {msg['snapshots_sent']} data points processed.") break
asyncio.run(backtest_strategy())Tips
Subscription caps, replay limits, idle timeouts, and range constraints.
| Tier | Symbols | Historical Depth | Max Range/Request | Max Subscriptions | Max Replay Speed |
|---|---|---|---|---|---|
| Free | No WebSocket access | - | - | - | - |
| Build | All | 365 days | Unlimited | 25 | 50x |
| Pro | All | Unlimited | Unlimited | 100 | 100x |
| Enterprise | All | Unlimited | Unlimited | 200 | 1000x |
WebSocket access is available on Build tier and above. See pricing for the current tier posture.
| Constraint | Limit | Description |
|---|---|---|
| Message metering | 1 credit/msg | Each WebSocket message counts as 1 API credit toward monthly quota |
| Idle timeout (with subscriptions) | 60 seconds | Send ping every 30s to keep alive |
| Idle timeout (no subscriptions) | 5 minutes | Connections without subscriptions timeout after 5 minutes |
| Subscription rate limit | 10/second | Max subscribe/unsubscribe operations per second |
| Concurrent replay tasks | 1 | One replay per connection at a time (multi-channel counts as one replay) |
| Multi-channel constraint | Same exchange | All channels in a multi-channel replay must be from the same exchange (e.g., all Hyperliquid or all Lighter; HIP-3 and HIP-4 are separate exchanges and cannot be mixed) |
| Per-request range limit | Tier-based | Free: 30 days max, Build/Pro/Enterprise: Unlimited |
| Data availability | Varies by channel | Hyperliquid: April 2023+ (orderbook snapshot spacing varies by market activity and query window; OI/funding: May 2023+, candles: March 2025+, liquidations: December 2025+, L4: March 2026+). Lighter: August 2025+ (trades, candles, OI, funding), January 2026+ (L2 orderbook), March 2026+ (L3 orderbook). HIP-3: February 2026+ for trades, candles, orderbook, OI/funding, and liquidations; L4 March 2026+. HIP-4: May 2026+ for L2/L4 orderbook, trades, OI, and order lifecycle (no funding, no liquidations, no candles by design). |