Every realtime feature in the SDK follows the same pattern: a service method named subscribe* takes event handlers and returns an unsubscribe function. Under the hood, one shared
WebSocket client multiplexes all channels, so subscribing to twenty streams costs one connection
(two, when public and private channels are both active).
The subscription contract
const unsubscribe = client.orders.subscribe({
accountId,
onEvent: (order) => console.log(order), // required: each published event
onOpen: () => console.log("subscribed"), // optional: (re)connected
onClose: () => console.log("disconnected"), // optional: connection dropped
onError: (ctx) => console.error(ctx.channel, ctx.type, ctx.error), // optional
});
unsubscribe(); // idempotent; releases the channel when the last consumer leaves- Handlers receive fully parsed, typed objects — the same shapes the request methods return, decimal strings included.
onErrorreceives anSdkSubscriptionErrorContextwith thechannel, an errortype(subscription, connection token, publication handler, …), and the underlyingerror. Errors in your handlers are isolated and routed here too — a throwingonEventnever kills the transport.- Subscriptions are shared: two
subscribecalls on the same channel attach to one underlying subscription and each gets every event.
What you can stream
| Stream | Method | Auth |
|---|---|---|
| Candles | client.candles.subscribe / subscribeInts | public |
| Public trades | client.marketData.subscribeTrades | public |
| Market overview | client.marketOverview.subscribe | public |
| Order book | client.orderbook.createSubscription / subscribe | public |
| Heatmap buckets | client.heatmap.subscribeLive | public |
| Lifecycle flows | client.lifecycle.subscribeOpenFlows / subscribeFlowDetail | public or private |
| Zipped asset supply | client.zipper.subscribeZippedAssetSupply | public |
| Your orders | client.orders.subscribe | private |
| Your triggers | client.triggers.subscribe / subscribeEvents | private |
| Your trades | client.trades.subscribe | private |
| Balances | client.balances.subscribe | private |
| Ledger transfers | client.transfers.subscribe | private |
| Subaccounts / API keys / policies | client.subaccounts.subscribe, client.apiKeys.subscribe, client.subaccounts.policies.subscribePolicies | private |
| Address book invalidations | client.addressBook.subscribeViewInvalidations | private |
| Profile identity | client.auth.profile.subscribeIdentity | public |
Private channels authenticate with the client's auth provider (JWT or API key) — an
unauthenticated client's private subscriptions surface an error through onError.
Snapshot-then-stream
Streams that represent state rather than an event log — the order book and the market overview —
first fetch a snapshot over HTTP, buffer publications that arrive in the meantime, then apply
deltas in order. They also refetch automatically after a reconnect or a sequence gap, so your onEvent always sees a consistent view:
const subscription = client.orderbook.createSubscription({
symbol: "BTC-USDC",
symbolId,
depth: 50,
onEvent: (book) => render(book), // always a complete, consistent book
});
subscription.isReady(); // has the initial snapshot landed?
subscription.refreshSnapshot(); // force a refetch
subscription.unsubscribe();Connection management
The shared realtime client is available as client.realtime:
client.realtime.isConnected; // any active websocket?
client.realtime.activeChannels; // number of attached channels
client.realtime.disconnect(); // tear down everything
client.realtime.disconnectPrivate(); // drop only the private connection (e.g. on logout)You rarely call these — connections open lazily with the first subscription and close when the
last consumer unsubscribes, and auth.logout() already disconnects private channels.
Ordering and reference data
Events that carry prices or quantities need the catalog's scale data to decode. Subscriptions handle this with an internal gate: events arriving before the catalog is ready are queued and flushed in order once it is. You never see a partially decoded event.
For entity streams (orders, balances, trades), pair the stream with a snapshot read
(listOpen, list, …) keyed by sequence/timestamps if you need guaranteed no-gap state — the
SDK gives you both primitives.