Skip to content
Docs
Developers

WebSocket protocol

Subscribe to public markets and authenticated accounts, then track sequence continuity and freshness.

Realtime channels

Realtime channels

100%
Diagram preview

Drag to pan, or use arrow keys when the diagram is focused. Use plus and minus to zoom, and zero to fit. On a touch screen, pinch to zoom.

Market and account channels use distinct access rules and share explicit subscription and freshness checks.

Public market channels require no ticket. Account channels require an account-bound realtime ticket and successful auth_ok before subscription.

Confirm subscriptions. Match each subscription-accepted frame to its requestId and validate incoming identity and sequence fields.

Check source availability. market.book and market.status are projections, market.ticker is derived, and account.margin is a digest. account.reconciliation and account.alerts are currently unavailable.

Replace book snapshots. market.book carries bids, asks and snapshotSequence. After sequence validation, replace the previous projected book with the complete received snapshot.

Open a public subscription

Use the WebSocket URL supplied for your environment, with the /streams/v1/ws path. Market channels need no authentication. Reuse one connection for multiple channels. Send version, requestId, channel and the channel's required scope fields. There is no action field. Only market.candles takes series.

Wait for realtime-subscription-accepted/v1 with the matching requestId. The acceptance supplies nextSequence, heartbeatIntervalMs and replay metadata. Track acceptance separately from connection health. Replace the placeholder below with an exact marketId returned by GET /v1/markets.

Public subscribe envelope — placeholder market identifier
{
  "version": "realtime-subscribe/v1",
  "requestId": "book-subscription-1",
  "channel": "market.book",
  "marketId": "<marketId from the venue catalogue>"
}

Authenticate private channels in order

  • 01POST /v1/realtime/ticket using your permitted REST bearer. The response supplies ticket, accountId and expiresAt in epoch milliseconds and must not be cached.
  • 02Open the socket and send an auth frame containing op: auth and the ticket. Do not send the bearer.
  • 03Wait for auth_ok. Read its accountId and expiresAt.
  • 04Send each private subscription using that accountId and wait for its matching acceptance.

A ticket authenticates a socket once. Reusing it on any socket is refused as replayed. Sending a private subscription before auth_ok is refused as unauthorized. Auth errors use an op: error frame; a refusal may close the socket with code 1008. Open a new socket with a fresh ticket when necessary.

Socket authentication — ticket placeholder only
{
  "op": "auth",
  "ticket": "<fresh single-use ticket from POST /v1/realtime/ticket>"
}

Rotate and authenticate again before expiry

Use expiresAt rather than assuming a fixed ticket lifetime. During the last 30 seconds, the socket emits one auth_expiring frame. PUT /v1/realtime/ticket with the current ticket in the JSON body; this operation uses the ticket itself and takes no bearer. Send the returned ticket in a fresh auth frame on the same socket. A new auth_ok extends the socket session while account subscriptions remain open. Rotation alone does not extend it.

After rotation, the previous ticket cannot be used to rotate or authenticate again; attempts return replaced. If expiry arrives first, public subscriptions continue but all private subscriptions are removed. Mint a new ticket with the REST bearer, authenticate, wait for auth_ok and resubscribe. DELETE /v1/realtime/ticket revokes the whole family; it accepts the current or immediately previous ticket. Never log these bodies.

private_subscriptions_dropped reasonRecovery
auth_expiredMint a fresh ticket, authenticate and subscribe again
auth_replacedReconcile and subscribe under the newly authenticated session
auth_revokedStop; mint again only when continued access is authorized
auth_unavailableMark private state stale; recover authentication and subscriptions

Track heartbeat and subscription health

The published contract advertises a 10,000 ms heartbeat interval. Use the interval supplied by subscription acceptance. Heartbeats have version realtime-stream/v1 and type heartbeat, with a subscriptions array containing each subscription's nextSequence and optional checkpoint. They are not book updates or fills.

Maintain separate connection, authentication, subscription and data-freshness states. A healthy public stream cannot establish that private channels are authorized. Missing heartbeats, a closed socket, source_unavailable or private_subscriptions_dropped must make affected state visibly stale. Reconnect with bounded backoff and jitter; set a client timeout policy based on the advertised interval. The protocol does not define a heartbeat reply.

Reconcile snapshots and stream sequences

Acquire the initial scoped state and follow each channel’s watermark and replay contract. Keep envelope sequence, engine journal sequence, projection checkpoint and candle snapshot cursor distinct. A replay mode of latest does not recover every event missed during disconnection.

On a gap or rejected cursor, mark the affected state stale and follow the returned recoveryAction. Use refresh_snapshot or resubscribe_stream only where the channel contract directs it. Recovery should rebuild the affected scope within a bounded request budget; healthy live updates should continue directly from WebSocket messages.

Validate account and market identity before advancing cursors, discard duplicates and older state, and pause decisions that require uninterrupted data when continuity is lost. market.book frames are complete projected snapshots: replace levels after sequence validation. If the channel lacks the fields or replay needed to restore a view, retain its stale or snapshot-only status instead of treating a reconnect as proof that it is current.

A socket reopening does not make cached balances, positions or orders current. Resume trading decisions only after private authentication, subscription acceptance and state reconciliation succeed.

Use the two error shapes correctly

Authentication failures use op: error with codes such as unauthorized, rate_limited and auth_unavailable; ticket-specific reasons include invalid, replayed, replaced, expired, revoked and store_unavailable. Subscription failures use realtime-error/v1 with code, retryable and recoveryAction.

For sequence_gap or resync_required, follow refresh_snapshot or resubscribe_stream as directed. For operation_not_available, change the requested capability rather than looping. A source marked unavailable is not an empty or zero-valued feed. The channel reference and product AsyncAPI define the exact envelopes.

Product channel directory

Source status describes the channel’s data source. It does not guarantee that every environment accepts every subscription. Check the subscription response and treat unavailable sources as unavailable data.

The product AsyncAPI defines the envelopes, operations, messages and referenced schemas for the channels below.

ChannelAccessSource status
market.tradespubliclive
market.bookpublicprojection
market.tickerpublicderived
market.pricepubliclive
market.fundingpubliclive
market.statuspublicprojection
market.candlespubliclive
account.ordersprivatelive
account.fillsprivatelive
account.durabilityprivatelive
account.positionsprivatelive
account.pnlprivatelive
account.marginprivatedigest
account.balanceprivatelive
account.withdrawalsprivatelive
account.reconciliationprivateunavailable
account.alertsprivateunavailable
account.earningsprivateprojection