Skip to main content
Two Server-Sent Event streams deliver live data: one for your account and trading activity, one for the raw order book.
Prerequisites: an active VOOI API token and at least one connected exchange. See API Tokens and Use your own keys if you don’t have them yet.
The streaming endpoints are under active development. Event shapes and delivery guarantees can change without notice — pin your client to a known-good API version and re-test after upgrades.

Account and trading stream

Subscribing is a two-step flow: mint a one-time token, then open the SSE connection with it. The token travels in the query string so clients that cannot set request headers on a streaming GET can still authenticate.
  1. POST /exchange/updates-token — returns { token, expiresAt }.
  2. GET /exchange/updates — open with ?token=<one-time-token>.

Frame format

Events are emitted as named SSE frames with an auto-incrementing id: per connection. Each frame carries one JSON value on a single data: line:
  • id: is a per-connection counter that starts at 1 on every reconnect. It is not a resumable cursor — the server does not honor Last-Event-ID and does not replay missed events. Use it for local logging/ordering only.
  • The server does not send SSE keep-alive comments (: ping). TCP keep-alive is enabled, but if you need an application-level idle timeout, implement it client-side.
  • Each event is one JSON value on one data: line — never multi-line.

Event catalog

Example frames:
Exact field lists live in the API reference pages linked in the table above. SSE frames carry the same shapes, with exchange added to every row.

Snapshot vs delta

  • state is a full snapshot for one exchange. It fires once per exchange on subscribe, and only when recent state is available for that (user, exchange) pair — typically after a REST read or a recent prior stream. Expect one state frame per connected exchange that has recent state, or none at all on a first-time connection. It is not guaranteed.
  • accounts, order, position, and trade frames are partial — they contain only the rows that changed since the last frame, not a full replacement. Merge them into your client cache by identity.
  • Position closures arrive as a position entry with size: "0" — the closed row is pushed, not silently dropped. Cancelled or fully-filled orders arrive with their terminal status (canceled or executed).
  • marketPrice has no snapshot. The first frame for a symbol arrives with the next upstream tick. Symbol set and cadence follow the venue’s own feed.

Identity keys for merging

Use these to dedup and merge events into local state:

Filter by venue

The optional exchanges query parameter narrows the stream to specific venues. Repeat the parameter for each venue:
Comma-separated values (?exchanges=hyperliquid,aster) are not accepted — the server validates each value against the venue enum, so a CSV fails. Use repeated parameters.
Omit the parameter to subscribe to all venues (Aster, Hyperliquid, Lighter, Robinhood, Extended, Binance, Bybit, MEXC, Ondo). Venues you have not connected stay silent — you’ll still receive marketPrice for them (it’s public), but no accounts, order, position, or trade events.

Token lifecycle

  • TTL is short — currently 2 minutes — and the exact deadline is returned as expiresAt on the mint response. Treat expiresAt as the source of truth and open the connection well before it.
  • Each token is single-use. It is consumed on the first connect attempt — even one that fails. You cannot reuse a token for reconnects.
  • Mint a fresh token for every reconnect. There is no documented rate limit on token minting today.
  • Multiple concurrent streams per user are allowed.
  • The token only gates the connection handshake. Once the stream is open it runs independently; token expiry mid-stream does not close the connection.

Errors and reconnection

  • restarting is transient — the server is re-establishing its link to the upstream exchange. Keep the connection open; fresh state/delta frames follow once it recovers.
  • error is terminal for that exchange. The per-exchange stream will not deliver further events. To resume, close the connection, mint a new token, and reconnect.
  • Network drops and HTTP-level errors surface to your SSE client’s error handler. Reconnect with exponential back-off; every reconnect needs a new token.
  • Connections are not closed on a schedule — they only end on network errors, client disconnect, or an error event from the upstream exchange.
1

Seed from REST

Fetch initial state once per session: GET /exchange/accounts, GET /exchange/positions, GET /exchange/open-orders. The state SSE event is opportunistic, so don’t rely on it to seed a fresh client.
2

Mint a one-time token

POST /exchange/updates-token — Bearer-authenticated. Returns { token, expiresAt }.
3

Open the SSE connection

GET /exchange/updates?token=<token>. Dispatch on the event: name of each frame.
4

Merge deltas by identity

On each event, apply the payload rows to your cache using the identity keys above.
5

Reconnect on drop

On a transport-level error or a server-sent error frame, close the connection, mint a fresh token, and start over from step 2. Back off exponentially on repeated failures.

Order book stream

GET /exchange/updates-orderbook — public order-book stream for a single asset, across one or more venues. No authentication required for the public venues. Two event types:
  • orderBookSnapshot — full book. Sent immediately on connect, then refreshed every 10 seconds.
  • orderBookDiff — only changed price levels since the previous snapshot/diff. A level that has been removed appears with size: "0".
This stream uses the same SSE frame format described above (event:/id:/data:); the id: line is omitted from the examples below for brevity.
orderBookSnapshot
Query parameters:
  • asset (required) — a market identifier, e.g. BTC or BTCUSDC. See Markets for the accepted forms and disambiguation rules.
  • exchanges — same repeated-parameter syntax as the account stream. Defaults to all venues.
  • levels — cap the number of price levels per side. Even without it, a snapshot contains at most 1000 levels per side on Aster, Lighter, Robinhood, Extended, Binance, Bybit, MEXC, and Ondo.
  • significantFigures — round prices to 2, 3, 4, or 5 significant figures (useful for bucketed depth charts).

Next steps

Orders

Place orders that will emit order and trade events

Positions

Polling alternative for position state