Skip to main content
OCX pushes real-time data over Server-Sent Events (SSE), not raw WebSockets. Connect with any SSE or EventSource client, apply the initial snapshot, then fold in delta/update events. All streams diff server-side and send a comment heartbeat (:heartbeat) roughly every 15s so proxies keep the connection open.
Reconnect discipline. On any disconnect, reconnect and re-apply the fresh snapshot — do not assume you can resume mid-stream. Messages carry a monotonic sequence (id: or eventSeq); if you detect a gap, reconnect and re-snapshot rather than trusting a partial book.

The five streams

Perp order book

GET /perps/book-stream — public depth for one perp/future market.

Perp user stream

GET /perps/stream/events — book + your fills + your positions.

Options board

GET /markets/board/stream — public marks, IV, Greeks per underlying.

Options CLOB stream

GET /options/stream/events — tradable book + tape for one instrument.

Portfolio balances

GET /portfolio/balances/stream — your cross-bucket balance state.
Public streams (book-stream, board/stream) need no credentials. Private streams require a session cookie or API key; their fills, positions, and balances are scoped to the authenticated account.

Envelope & event names

Named-event streams emit event: snapshot on connect then event: delta (or event: update) on change, with a canonical envelope:
{ "eventId", "eventType", "eventSeq", "engineTs", "sourceTs", "payload" }
In an order-book delta, a level with size: "0" means that price level was removed. An event: error carries { code, message, retryable }.

Perp order book — public

marketId
string
required
depth
number
Levels. Default 25, max 100.
intervalMs
number
Emit cadence. Default 100, range 502000.
GET /perps/book-stream — pure market data, no user fields. First snapshot is the full book; then update diffs. Each level is { price, size, total }.
curl -N "https://{API_BASE}/perps/book-stream?marketId=BTC-PERP&depth=25&intervalMs=100"
data
{ "marketId": "BTC-PERP",
  "bids": [{ "price": "68200", "size": "1.25", "total": "1.25" }],
  "asks": [{ "price": "68220", "size": "0.80", "total": "0.80" }] }

Perp user stream — private

marketId
string
required
One market per connection.
depth
number
Default 25, max 250.
openOnly
boolean
intervalMs
number
Default ~500, range 1005000.
GET /perps/stream/eventsauth required. One market per connection, carrying three things: the public order book, your fills, and your positions. Pass marketId. GET /perps/stream/userauth required. Your fills and positions across all markets on one connection (no order book). This is the stream to use for account-wide fill tracking and market making — one connection instead of one per market. Params: openOnly, intervalMs (default ~250).
payload.orderbook
object
Book levels; deltas send changed levels (size:"0" = removed).
payload.trades
array
Your fills: { side, price, quantity, liquidity, createdAt } where liquidity is maker or taker.
payload.positions
array
Your positions: { side, quantity, entryPrice, realizedPnl, status }. Deltas send position upserts and removedIds.
const es = new EventSource(
  `https://${API_BASE}/perps/stream/events?marketId=BTC-PERP`,
  { withCredentials: true }
);
es.addEventListener("snapshot", e => init(JSON.parse(e.data).payload));
es.addEventListener("delta",    e => apply(JSON.parse(e.data).payload));
A fill arrives as a trade plus a position delta. Market makers feed this straight into the next quotes/bulk cycle — skew or shrink quotes as inventory builds. See the Market makers guide.

Options board — public

underlying
string
e.g. BTC. Defaults to the primary underlying.
GET /markets/board/stream — ~1 update/sec, data-only messages wrapping { eventType: "OptionsBoardSnapshot", eventSeq, engineTs, payload }. The payload is the full board snapshot: underlyingPrice, underlyingSymbol, expiries[], per-expiry forwards, and options[] rows with strike, expiry, bid, ask, mark, iv, delta plus the volatility-index block.
curl -N "https://{API_BASE}/markets/board/stream?underlying=BTC"
The board is the reference marks surface — not a tradable book. For a hittable options book, use the options CLOB stream below.

Options CLOB stream — private

marketId
string
required
One option instrument.
depth
number
Default 25, max 250.
intervalMs
number
Default ~100, range 502000.
includeReference
boolean
Add the reference-mark overlay.
GET /options/stream/eventsauth required. Same envelope as the perp user stream: event: snapshot (OptionsStreamSnapshot) then event: delta (OptionsStreamDelta) with monotonic eventSeq. Payload: the instrument’s tradable CLOB order book, the public trade tape ({ side, price, quantity, liquidity }, no counterparty data), and — only when includeReference=true — a reference overlay { markPrice, source, tradable: false }.
The reference overlay is always flagged tradable: false — it is a marks reference, never a hittable price. Route orders against the CLOB book, not the overlay.
const es = new EventSource(
  `https://${API_BASE}/options/stream/events?marketId=BTC_CALL_70000_2026-12-25&includeReference=true`,
  { withCredentials: true }
);
es.addEventListener("snapshot", e => init(JSON.parse(e.data).payload));
es.addEventListener("delta",    e => apply(JSON.parse(e.data).payload));

Portfolio balances — private

GET /portfolio/balances/streamauth required. event: snapshot ({ type: "snapshot", payload }) on connect, then event: update whenever your unified balances change across the wallet / perps / options / spot buckets. Heartbeat every 15s; event: error on upstream failure.
const es = new EventSource(`https://${API_BASE}/portfolio/balances/stream`, { withCredentials: true });
es.addEventListener("snapshot", e => setBalances(JSON.parse(e.data).payload));
es.addEventListener("update",   e => setBalances(JSON.parse(e.data).payload));

Consumption checklist

1

Connect and snapshot

Open the stream, apply the first snapshot as your full state.
2

Fold in deltas

Apply each delta/update; treat a level with size: "0" as removed.
3

Watch the sequence

Track id: / eventSeq. A gap means you missed data.
4

Reconnect on drop or gap

Reconnect and re-snapshot — never resume a book from a partial state.