Documentation Index
Fetch the complete documentation index at: https://docs.sx.bet/llms.txt
Use this file to discover all available pages before exploring further.
Overview
SX Bet’s WebSocket API delivers real-time updates on orderbook changes, trade executions, market status, and live scores. All channels are powered by Centrifugo and require an API key. Centrifugo provides official client SDKs for JavaScript, Python, Go, Dart, Swift, Java, and C#. Rather than polling REST endpoints, subscribe to the channels relevant to your workflow. The recommended pattern for most use cases is: fetch current state via REST, then subscribe to stay updated — this avoids gaps between your initial snapshot and the live feed.Getting started
Authenticate
To connect, you need a realtime token from the relayer. Fetch it from/user/realtime-token/api-key and pass your API key in the x-api-key header. In the client SDK, provide a getToken callback that returns this token.
The SDK calls getToken again whenever the current token expires, so passing a function instead of a static token is all that’s needed to keep the connection alive. See Common failures → Auth for how to signal permanent auth failure vs. a transient fetch error.
Connect
Create oneCentrifuge client per process and reuse it for all subscriptions. All channel subscriptions are multiplexed over the same connection.
Subscribe
Useclient.newSubscription(channel, options) to create a subscription, attach event handlers, then call .subscribe():
If you need at-least-once delivery across reconnects, pass positioned: true and recoverable: true together for channels that support recovery. See Recovery & reliability for details.
Channels
| Channel | What you receive | Payload reference |
|---|---|---|
active_orders:{maker} | Your orders being filled, cancelled, or posted | Active Order Updates → |
order_book:market_{marketHash} | All order changes for a specific market | Order Book Updates → |
order_book:event_{sportXEventId} | All order changes for every market in an event | Order Book Updates → |
best_odds:global | Best available odds changes across all markets | Best Odds → |
recent_trades:global | Global feed of all exchange trades | Trade Updates → |
recent_trades_consolidated:global | Consolidated trade feed — one entry per fill, not per order | Consolidated Trade Updates → |
markets:global | Market status changes, suspension, settlement | Market Updates → |
main_line:global | Main line shifts on spread and totals markets | Line Changes → |
fixtures:global | Fixture metadata updates | Live Score & Fixture Updates → |
fixtures:live_scores | Live match scores | Live Score Updates → |
ce_refunds:{bettor} | Chain enforcer refund events for your address | CE Refund Events → |
parlay_markets:global | Incoming parlay RFQ requests | Parlay Market Requests → |
Recovery & reliability
Enabling recovery
Pass options tonewSubscription to control reliability behavior:
| Flag | Type | Description |
|---|---|---|
positioned | boolean | Enables stream position tracking for the subscription. This lets the client keep its current offset and allows the server to signal when the stream position has become invalid. |
recoverable | boolean | Enables automatic recovery for the subscription. On resubscribe, the client sends its last known stream position and the server tries to replay missed publications from history. |
positioned bookmarks your place in the stream while you’re connected; recoverable uses that bookmark to fetch the missed pages when you come back.
best_odds:global, parlay_markets:global), both flags can be omitted.
Interpreting subscribed after reconnect
After a reconnect, thesubscribed event fires with context that tells you whether your local state is still consistent:
The subscribed context includes:
wasRecovering: the client attempted to recover from a previous stream positionrecovered: the server successfully replayed all missed publicationspositioned: the subscription has stream position tracking enabledrecoverable: the subscription supports automatic recovery
wasRecovering | recovered | State | What to do |
|---|---|---|---|
true | true | Recovered | History replay filled the gap — no action needed |
true | false | Unrecovered | History was pruned — re-seed from REST |
false | — | Fresh connect | First connection or clean reconnect — seed from REST |
Delivery guarantees
For namespaces with history enabled, Centrifugo provides at-least-once delivery within the recovery window — missed messages are replayed from server-side history on reconnect. The recovery window is 5 minutes, but may be shorter if the namespace’s message cap is reached first. After the window expires,wasRecovering: true, recovered: false fires and you must re-seed from REST.
best_odds and parlay_markets do not have history enabled — recovery is not available on those channels.
Note on epoch: Recovery can fail even after a short disconnect if the server no longer has the missed publications in history or if the saved stream position is no longer valid. This is rare, but when it happens recovered will be false and you must re-seed from REST.
Dedup
At-least-once delivery means a message may occasionally be replayed more than once during recovery. Every publication includes amessageId in ctx.tags — use it to deduplicate on the client side:
History
Each namespace with history enabled maintains a server-side log of recent publications. You can fetch this directly withsub.history() — useful for seeding initial state or auditing recent activity without a separate REST call.
Parameters
| Parameter | Type | Description |
|---|---|---|
limit | number | Max publications to return. 0 returns only the current stream position (no publications). Omit for all history up to the server cap. |
since | { offset: number, epoch: string } | Start from a known stream position. Useful for paginating through history. Omit to start from the beginning (forward) or end (reverse). |
reverse | boolean | false (default) = oldest first. true = newest first. |
publications array and the current stream offset and epoch. Each entry in publications has data, offset, tags, and info fields — access the payload via pub.data, the same as ctx.data in a live publication event.
Limits
History fetches are bounded by the per-namespace caps in Namespace history capabilities and the global limit of 1,000 items per request. Callingsub.history() on a channel with no history enabled returns error code 108.
Snapshot + subscribe pattern
Subscribe withpositioned: true, recoverable: true and seed from REST inside the subscribed handler. The handler fires on every connect and tells you whether recovery filled the gap — so you only hit REST when you actually need to:
best_odds:global channel.
Connection & Subscription Lifecycle
The client connection and each subscription have separate lifecycles. The key rule is:connectingandsubscribingare non-terminal states. They fire on the initial connect or subscribe and also on automatic retry paths.disconnectedandunsubscribedare terminal states for automatic retry.
Client lifecycle
The client connection moves through these states:disconnected -> connecting -> connected: initial connectconnected -> connecting -> connected: retryable disconnect, then successful reconnectconnecting/connected -> disconnected: terminal disconnect
connecting: fired on the initialconnect()and on retryable reconnects. The event includes acodeandreason.connected: fired when the transport is established and the client is ready.disconnected: fired only when the client reaches terminaldisconnectedstate. After this, the SDK will not reconnect automatically.error: fired for internal errors that do not necessarily cause a state transition, such as transport errors during initial connect or reconnect, or connection token refresh errors.
client.connect() explicitly.
Subscription lifecycle
Each client-side subscription moves through its own state machine:unsubscribed -> subscribing -> subscribed: initial subscribesubscribed -> subscribing -> subscribed: retryable interruption, reconnect, or resubscribesubscribing/subscribed -> unsubscribed: terminal subscription stop
subscribing: fired on the initialsubscribe()and on retryable resubscribe paths.subscribed: fired when the subscription becomes active.unsubscribed: fired only when the subscription reaches terminalunsubscribedstate. After this, the SDK will not resubscribe automatically.publication: fired whenever a new message arrives on the subscription while it is active.error: fired for internal subscription errors that do not necessarily cause a state transition, such as temporary subscribe errors or subscription token related errors.
sub.subscribe() explicitly.
Retryable vs terminal conditions
In practice, this means:- temporary transport loss moves the client back to
connecting - reconnectable subscription interruptions move the subscription back to
subscribing - calling
client.disconnect()or hitting a terminal disconnect condition moves the client todisconnected - calling
sub.unsubscribe()or hitting a terminal subscription condition moves the subscription tounsubscribed
subscribed event via wasRecovering and recovered. See Recovery & reliability for how to interpret those fields.
Examples
Consume a global feed
Maintain a recoverable order book
Usepositioned: true and recoverable: true together, then handle the three subscription states to maintain consistent local state across reconnects:
Monitor your active orders
Subscribe toactive_orders:{maker} to receive fills, cancellations, and new posts for your address in real time:
Common failures
In most cases, you do not need to write custom retry logic around these errors. The SDK already handles reconnect and resubscribe automatically when the condition is retryable. The codes below are most useful for telemetry, debugging, and contacting support if an issue persists.Auth
ThegetToken callback is called on initial connect and whenever the token needs to be refreshed. How you throw from it controls what the SDK does next:
401 or 403, throw UnauthorizedError so the connection stops retrying and moves to terminal disconnected. For transient failures like 429 or 5xx, throw a normal error so the SDK keeps retrying.
The server may also issue a terminal auth disconnect such as code 3500 ("invalid token"). In that case, the client stops reconnecting automatically.
Subscribe errors
Retryable subscription errors emit the subscriptionerror event. Terminal subscription errors move the subscription to unsubscribed.
| Code | Meaning | What happens next |
|---|---|---|
100 | Internal server error | The subscription stays in subscribing and the SDK retries. |
101 | Unauthorized | The subscription moves to terminal unsubscribed. |
102 | Unknown channel | The subscription moves to terminal unsubscribed. |
103 | Permission denied | The subscription moves to terminal unsubscribed. |
106 | Limit exceeded — connection is at the 512 channel cap | The subscription moves to terminal unsubscribed. |
109 | Token expired | The subscription stays in subscribing; the SDK refreshes the token and retries. |
111 | Too many requests | The subscription stays in subscribing and the SDK retries. |
Recovery lost / insufficient state
If Centrifugo detects that recovery cannot continue from the current stream position, it may either resubscribe the affected subscription or reconnect the client, depending on where the problem is detected. This can surface as unsubscribe code2500 or disconnect code 3010, both with reason "insufficient state".
This is not terminal by itself. The next subscribed event tells you whether the replay succeeded:
wasRecovering: true, recovered: true: replay filled the gapwasRecovering: true, recovered: false: replay could not fill the gap, so re-seed from REST
insufficient state frequently, it usually indicates a stream continuity problem rather than a client bug.
Terminal disconnects
The client reconnects automatically after most disconnects. It does not reconnect for built-in terminal disconnect codes in the3500-3999 range.
Common terminal examples include:
3500invalid token3501bad request3503force disconnect3507permission denied
Slow consumer
The server buffers up to 1 MB per connection. If yourpublication handler is slow, that buffer fills faster than it drains and the server closes the connection. In Centrifugo this can surface as disconnect code 3008 ("slow"), which is reconnectable but indicates your consumer cannot keep up.
Keep handlers fast: receive the message and hand it off to a queue or async task immediately. Unexpected disconnects that are not auth-related are often caused by a saturated buffer.
For the full list of built-in unsubscribe and disconnect codes, see Centrifugo client protocol codes.
Related
Market Making →
Using active_orders to monitor your open orders in real-time.
Filling Orders →
How to submit fills and monitor your trade history.
WebSocket Initialization →
Connecting and subscribing with the Centrifuge client.
Market Making Parlays →
Responding to parlay RFQ requests via parlay_markets:global.
