Skip to main content
Each data stream is a subscription type. You subscribe by type, and the server tags every frame it sends back with the channel field. This page covers the Spot /ws protocol and payloads. For Testnet-Only Perpetuals, use Perpetual Subscriptions.

Subscribe and Unsubscribe

method is subscribe or unsubscribe. To unsubscribe, send the same subscription object you subscribed with. Unsubscribing from one type does not affect other subscriptions. Only subscribe and unsubscribe are supported. This API has no equivalent of posting REST requests over the WebSocket.

Subscription Fields

These fields are rejected rather than silently ignored: nSigFigs, mantissa, and aggregateByTime. These are accepted and ignored, and echoed back in the acknowledgement: dex (single DEX) and isPortfolioMargin (spot only).

Limits

Resubscribing to a type the client already holds refreshes its TTL and does not consume a second slot.

Server Messages

Every server message is a two-field envelope:
channel is the subscription type that produced the frame, with one exception: userEvents data arrives with channel set to user. Three channels carry control messages rather than data: The acknowledgement echoes the subscription back, including ignored fields. On user types the user value is echoed in the format you sent it, even though the server normalizes it internally.
Failures attach error to the echoed subscription. See Errors.

Snapshot-First Delivery

l2Book, candle, openOrders, userFills, spotState, and userTwapHistory send an initial snapshot on subscribe. The server acknowledges, sends the snapshot, and only then starts streaming. Live updates that arrive while the snapshot is being prepared are buffered and flushed after it, so a consumer never sees an update before the baseline it applies to. This ordering applies only within one connection. It does not give you a block height for the snapshot or a sequence number shared with REST. See Maintaining a Local Book.

Types at a Glance

Every type is a read-only view of public chain state, the same data REST serves without authentication. Subscribing to a user type exposes no keys and cannot place orders. Field encodings are listed in Overview.

Market Types

l2Book

levels[0] is bids, descending by price. levels[1] is asks, ascending. Each frame replaces both published sides; it is not a delta to merge by price. sz preserves raw base units. n is currently a zero placeholder, not a usable count of resting orders.
Levels use the same ordering as GET /api/v1/orderbook/{symbol}, so switching between the REST snapshot and this stream needs no re-sorting. See Market Data. The frame has no block height or sequence. Its initial snapshot uses server time; live frames use block time. Do not compare time with a REST nonce or use it to promise lossless recovery.

bbo

Top of book only. bbo[0] is the best bid or null, bbo[1] is the best ask or null. Emitted only when the top of book changed from the previous block.

trades

Executions in the block.
tid is derived from the order id, block height, and post-fill remaining size, so two same-size fills on one order in one block stay distinct. It is not the REST trade id and cannot be passed to GET /api/v1/trade/{id}.

User Types

User types take a user address, given as ault1... or 0x....

orderUpdates

Array of changed orders.

openOrders

Snapshot only, with no streaming updates. It returns at most 500 executable orders, excludes conditional (Testnet-Only) and TWAP parents, and has no continuation token or truncation flag. A successful subscription does not mean you have every order. Each order uses the same shape as orderUpdates.order; dex is always "ault". For a complete inventory, follow every page of REST orders, which includes conditional and TWAP parents, for each wallet or derived account you control. Reconcile changes with orderUpdates and subsequent reads; these interfaces do not share one point-in-time view.

userFills

The first frame is a snapshot of up to 200 recent fills with isSnapshot: true. Live frames carry isSnapshot: false.
Realized fee amount and asset are used when available. Older records fall back to quote estimates and do not indicate that the value is an estimate. Use User Fills and confirmed chain records to reconcile fees by currency. Native event granularity depends on the chain version; see Trade and Fill Data before building an indexer or treating raw events as these API records.
Correlate fills to orders with oidHex, not oid. oid is a lossy 50-bit derivation and cannot be mapped back to the chain order id.
Against older chain nodes, crossed may not reflect the actual leg: live fills default to crossed: true with a taker-rate fee, and older snapshot rows read as crossed: false with a maker-rate fee. cloid may also be absent on live fills from those nodes. For authoritative fees, read User Fills.

userEvents

Frames arrive with channel set to user, not userEvents. Each frame is one of two shapes: a nonUserCancel array, or a fills array with the same shape as userFills.
Subscribe to this type for nonUserCancel, which is the only place expiry is reported. A non-user cancel is currently emitted only for expired orders. The fills shape duplicates userFills, so there is no need to consume both for fills.
The frame carries oid and no oidHex, and oid is not unique enough to identify one order, since orders from the same block can share a value. Treat the frame as a signal that something expired on that market, then reconcile which order it was through Orders.

spotState

The first frame is the full balance set. Subsequent frames are deltas containing only the balances that changed in the block, so merge them into local state by coin. There is no isSnapshot flag.
hold and total correspond to used and total on Balances. Free balance is total minus hold.

Keyless Types

allMids

Every market’s current mid price. The mid is the book midpoint when both sides exist, and falls back to the last trade price when the book has one side or none.
A market that has had neither a book nor a trade never appears. Once a market goes quiet it keeps its last published mid.

Additional Market and Strategy Channels

candle

Subscribe with {type: "candle", coin: "AULT/USDC", interval: "1m"}. Accepted intervals are 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 8h, 12h, 1d, 3d, 1w, 1M. The initial array contains up to 300 bars; updates contain one bar. Each bar has t, T (open/close milliseconds), s (symbol), i (interval), numeric o, c, h, l, v, and n (trade count, zero for snapshot bars). These intervals differ from REST candle support.

activeAssetCtx

Subscribe with {type: "activeAssetCtx", coin: "AULT/USDC"}. The payload is {coin, ctx}. Spot ctx.markPx, midPx and circulatingSupply are JSON numbers; last, bid, bidVolume, ask, askVolume are decimal strings. circulatingSupply is a zero placeholder. markPx is a Spot display fallback from mid/book/last and is not a Perp risk mark. There are no 24-hour aggregates: fetch REST tickers. A missing bid/ask means that side is empty; do not retain a previous quote. Missing 24-hour fields do not erase the separately loaded ticker.

userTwapHistory

Subscribe with {type: "userTwapHistory", user: "ault1..."}. Payload: {isSnapshot, dex, user, history}. The snapshot has up to 100 newest parents. Each item has coin, side, sz, depositAsset, numSlices, sliceIndex, oid, oidHex, status, chainStatus, startTime, deadline, timestamp; priceTolerance, worstPx, closeReason are optional. sz is human units of the payment asset: base for sells, quote for buys. It is a strategy budget, not proof of full-parent escrow. Live frames are emitted only in blocks with a slice execution. Placement and closure produce no parent frame, so query REST orders to confirm those transitions.

chainBlock

Subscribe with {type: "chainBlock"}. Each frame contains numeric height and timestamp (milliseconds). Optional hash, proposer, txCount, gasUsed, gasWanted may be absent. hash is bare uppercase hex. A block frame cannot be used to replay missed data or to mark a point at which all subscriptions are in sync.

Ordering Across Types

Each subscription is delivered independently, so a fill can arrive before the order update that reflects it. Treat each type as authoritative for its own data, and reconcile against REST after any reconnection.

Example Session