Skip to main content
Every channel runs over one websocket connection.

Verbs

You send a verb to tell us what to do on the connection. subscribe also adds channels to private. snapshot returns the current state and its seq, and leaves your subscriptions unchanged. query_markets returns one page of open markets, filtered the same way as GET /v3/catalog/markets. query_events returns one page of open events, filtered the same way as GET /v3/catalog/events. Both debit the read throttle.

Choose what to subscribe to

subscribe and snapshot name a selection in three optional fields. An empty selection gets EMPTY_SELECTION.
An events selection also covers markets that open later. unsubscribe takes a list of subject strings instead.
PRIVATE drops every private channel. To drop just one, unsubscribe PRIVATE, then subscribe to the channel you keep.

Trade

Three verbs place and cancel orders over the connection. They need a trading key; a trading::read key gets SCOPE_INSUFFICIENT. Payloads match the REST orders bodies, and they debit the place and cancel throttles, not stream. place books a batch of orders, all or none, for 1 place token each. The ack is placed, one entry per accepted order.
cancel cancels orders by id, 1 cancel token each. It is partial: an unknown id does not stop the rest. The ack is canceled, naming the ids that canceled and those that did not.
cancel_all cancels your resting orders for 1 cancel token, narrowed by market, event, or outcome. An empty filter cancels them all. The ack is canceled_all, the count queued.
An ack is our intent, not the fill. An order’s fills and final state arrive on the orders channel, so subscribe private to see them.

Channels and cost

A pair is one subject on one channel. A subscribe to 100 markets on book weighs /request. We charge at most the stream capacity of tokens. A request above that cap passes only when the throttle is full, and it empties the throttle.
  • snapshot costs the same as subscribe.
  • unsubscribe costs 1 token per subject.
  • status costs 1 token.
  • A subject we can’t resolve still costs tokens.

Nonces

A nonce is the number you put on each request.
  • Start nonces at 1.
  • Send them in increasing order within a connection. Gaps are allowed.
  • The largest nonce is 253−12^{53}-1.
We answer a repeated or lower nonce with STALE_NONCE. A frame that fails to parse or hits the throttle gets a reply with no nonce. That frame leaves your newest nonce unchanged.

Each subscription starts with a snapshot

A snapshot is the current state of what you subscribed to. Deltas, the changes after it, follow. We number the messages on each market channel per market, and on orders and positions per subaccount. That number is seq. The snapshot carries the seq at which we took it. The first delta carries the next seq, with no gap. Changes that happen together arrive in one message. Apply each batch atomically.
snapshot
delta

Compression

Set X-Novig-WS-Compress: deflate on the upgrade request to receive compressed messages. Every message then — snapshots, deltas, and acks — arrives as one binary frame of raw DEFLATE data (RFC 1951). Inflate each frame on its own, then parse the JSON.

Gaps

A gap is a skipped seq. It means you missed a message. delta seq 48122next delta 48124: 48123 is missing, buffer 48124snapshot { markets: { “6f9619ff-…”: “book” } }snapshot @ seq 48125drop buffered deltas ≤ 48125, replay the restCLIENTEXCHANGE

Arrows are messages, top to bottom in time. After the new snapshot, drop the buffered deltas it already covers.

  • A market seq is per market, per channel. There’s no global sequence.
  • A private seq is per subaccount, per channel. It’s independent of every market seq.
  • To recover from a gap, send snapshot. It leaves your subscriptions unchanged.
  • After a reconnect, subscribe again. The reply carries the snapshot.
  • Never compare a seq across connections.
A market that opens under an events subscription arrives with no snapshot. That’s not a gap. Its first delta is OPEN, and its book, bbo, and trades channels start at seq 0, so each one’s first delta carries seq 1.

Why a connection closes

  • SLOW_CONSUMER means a write to you stalled for 15 s.
  • A geolocation close means the companion check refused you while you were connected: your last device geolocation is missing, failed, named no region, or came from a restricted state. The reason is the 451 code. A stale geolocation never closes a socket.
  • If you send no Pong between two of our Pings, we drop the connection without a close frame. Your client usually reports 1006. We ping every 15 s.