Skip to main content
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: Bearer header - preferred from server-to-server clients.
  • X-API-Key header - accepted as a fallback for clients that can set arbitrary handshake headers.
The handshake validates the key, subscription status and tier eligibility. Failures close the connection with a 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 an event field:

Client → server commands

Each command receives a corresponding ack frame.

Trade payload

amount and premium_usd are comparable across venues. For Delta ingest details see Venue coverage → Delta units.

Connection lifecycle

  1. Connect with API key → server validates → counts connection against your per-tier cap.
  2. Receive welcome → sanity-check subscription_tier and available_channels.
  3. Send subscribe → server replies with subscribed ack + immediately starts streaming matching trades.
  4. Stream - trades arrive on their channel; status.heartbeat arrives every 15 s if subscribed.
  5. Heartbeat liveness - the server expects at least one client frame (subscribe / unsubscribe / ping) within 60 s. Otherwise it closes with code 1001.
  6. Slow consumer - each client has a 1000-message send buffer. If full for > 5 s the server sends a slow_consumer event then closes with code 1013. Reduce your subscription set or process frames faster.
  7. 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.
Full working client: scripts/v2_ws_demo/ws_tape_demo.py.

See also

REST tape with filters

Tape overview

GEX levels (REST, flow default)

Authentication