wss://api.backquant.com/v2/ws/options
Single connection, multiple subscriptions. The same canonical trade payload from the REST tape, pushed sub-second after the upstream venue emits it.
Authentication
Pass your API key on the WebSocket handshake - either of:- Query parameter -
wss://api.backquant.com/v2/ws/options?api_key=bq_live_...(easiest from browsers). Authorization: Bearerheader - preferred from server-to-server clients.X-API-Keyheader - accepted as a fallback for clients that can set arbitrary handshake headers.
1008 policy-violation code and a human-readable reason.
Channel inventory
{coin} is typically BTC or ETH (SOL/HYPE appear when tape exists).
{venue} is one of deribit, bybit, okx, binance, derive,
thalex, delta_india, delta (case-insensitive on subscribe; the
server normalises to lowercase). {SYMBOL} is BTCUSDT, ETHUSDT,
SOLUSDT, or HYPEUSDT. See Venue coverage for
what each venue ships.
Levels note (changed in 2.6.0): gex.levels.* pushes flow-signed
levels - the same model and the same builder as REST
/v2/gex/levels, so the two surfaces always agree.
Before 2.6.0 this channel pushed a std (textbook) snapshot while REST
defaulted to flow.
When the aggressor tape is unavailable for a symbol the payload falls back
to the worker’s std snapshot and says so: positioning: "std" plus a
fallback_reason. Always read positioning before overlaying the numbers
on anything else. See Positioning.
On subscribing to gex.levels.* the server sends the current levels
immediately, flagged "snapshot": true, then pushes again only when the
levels move. Without that first frame you could not tell an idle book from a
subscription that never took.
Max 48 channels per connection.
Protocol
Server → client envelope
Every server frame is a JSON object with anevent field:
Client → server commands
Trade payload
amount and premium_usd are comparable across venues. For Delta ingest
details see Venue coverage → Delta units.
Connection lifecycle
- Connect with API key → server validates → counts connection against your per-tier cap.
- Receive
welcome→ sanity-checksubscription_tierandavailable_channels. - Send
subscribe→ server replies withsubscribedack + immediately starts streaming matching trades. - Stream - trades arrive on their channel;
status.heartbeatarrives every 15 s if subscribed. - Heartbeat liveness - the server expects at least one client frame (subscribe / unsubscribe / ping) within 60 s. Otherwise it closes with code
1001. - Slow consumer - each client has a 1000-message send buffer. If full for > 5 s the server sends a
slow_consumerevent then closes with code1013. Reduce your subscription set or process frames faster. - Disconnect → connection counter decrements; reconnect any time.
Per-tier connection caps
Exceeding the cap on connect returns close code
1008 with a
connection-cap message. WS usage does not draw down monthly REST
quota - see Rate limits.
Reconnect template
The server has no replay -$ cursor is used on the underlying Redis stream, so a reconnect resumes with whatever’s live, not whatever you missed. For zero-gap consumers, also poll /v2/tape?after=<last_ts> on reconnect to fetch trades that arrived during the gap.
scripts/v2_ws_demo/ws_tape_demo.py.
