Breaking
Orderbook V3
The exchange now runs on a new orderbook, OBv3. See the Migrate to V3 guide.API
GET /fills-v3 accepts an orderId filter
GET /fills-v3 can be narrowed to the fills from one of your signed
orders. An unknown or foreign orderId returns an empty page. tradeId still works; both filters
AND when set together.API
outcome.fillAmount documented as the order’s executed amount
The fillAmount on each order outcome returned by POST /orders-v3 is the amount the order you submitted executed, in base units. It was previously described as a cumulative amount filled. The values returned are unchanged — only the description was wrong.Breaking
POST /orders-v3 create-result status is now SUBMITTED
Each per-order result in POST /orders-v3 now returns status: "SUBMITTED" when the order was accepted and sent to the matching engine. FAILED is unchanged. The stored order lifecycle is still PENDING → ACTIVE → INACTIVE.API
POST /orders-v3 outcomes now report blended odds
Each order outcome returned by POST /orders-v3 now includes an optional blendedOdds — the effective blended execution odds across the order’s matches, in the taker frame at 1e20 scale (divide by 10^20 for implied probability). It is present only when the order matched, and only when you request the synchronous outcome with waitForOutcome: true.API
Date filters and sort on bets and fills
GET /trades-v3 and GET /fills-v3
accept optional inclusive startDate / endDate (ISO-8601) and sortAsc (default true,
oldest-first). Omit the dates for the full history.Breaking
GET /trades/consolidated now requires a bettor or marketHash filter
GET /trades/consolidated now requires at least one of bettor or marketHash on every request. Requests that supply neither are rejected with a 400 error.Breaking
Changes to trade history queries
To keep trade-history queries fast and reliable, we’ve made three changes toGET /trades and GET /trades/consolidated. If your integration reads count from /trades or sends more than 100 marketHashes, you’ll need to update it.count removed from GET /trades
The count field (total number of records matching the query) is no longer returned in the GET /trades response. Use cursor pagination instead: keep following nextKey until it’s absent. count on GET /trades/consolidated is unchanged.marketHashes capped at 100 on GET /trades
GET /trades now accepts at most 100 market hashes per request. Requests with more are rejected with a 400 error — batch larger lists into multiple calls.12-month lookback when bettor is not supplied
GET /trades and GET /trades/consolidated now limit results to trades placed within the last 12 months unless bettor is provided. Queries filtered by bettor are unaffected and can still reach further back. If you need older trades without a bettor filter, note that startDate/endDate values outside the window won’t return them.Quarter-line flags on /trades and recent_trades:global
GET /trades response items and the recent_trades:global realtime channel now carry two additive fields so you can identify quarter-line legs without inspecting market metadata:isQuarterLineLeg(boolean, always present) —truewhen the trade’smarketHashis one of the two legs of a quarter-line market.quarterLineParentMarketHash(string, optional) — present only whenisQuarterLineLegistrue. Holds the parent quarter-line market hash (the market the user originally placed on), which differs from the trade’s ownmarketHash. Use this to group both legs back to the user-facing bet.
GET /orders now reports pending fills
GET /orders now returns pendingFillAmount on each order — the sum of accepted fills not yet reflected in fillAmount, from the maker’s perspective.Quarter-line flags on markets and consolidated trades
Quarter-line markets and trades are now explicitly flagged on the API surface, so you no longer need to infer them fromline values or dedupe via quarterLineFillHash alone.What’s new
Market.isQuarterLineMarket— returned onGET /markets/activeandGET /markets/find.truewhen the market is a quarter line (Asian handicap or totals in .25 increments).ConsolidatedTrade.isQuarterLineParent—trueon the synthetic parent entry that aggregates a quarter-line bet’s two legs. Use this for user-facing P&L and volume.ConsolidatedTrade.isQuarterLineLeg—trueon each of the two underlying leg entries.- New query params on
GET /trades/consolidated:hideQuarterLineParents=true— return only raw leg entries.hideQuarterLineLegs=true— return only parent entries (recommended for portfolio/P&L views).
isQuarterLineParent / isQuarterLineLeg flags are now included in payloads on the recent_trades_consolidated:global real-time channel.How to use the flags
- Aggregating P&L or volume: filter to
isQuarterLineParent === true(or passhideQuarterLineLegs=true) and treat non-quarter trades normally — the parent’stotalStake,totalReturn, andnetReturnalready aggregate both legs. - Settlement-level analysis: filter to
isQuarterLineLeg === true(or passhideQuarterLineParents=true) to see the underlying on-chain settlements; each leg settles independently. - Don’t sum both — parents and legs represent the same bet. Pick one side.
quarterLineFillHash field from the May 7 quarter-lines rollout; the booleans are an easier dedup signal for most integrations.See GET /markets/active, GET /markets/find, GET /trades/consolidated, and Consolidated Trade Updates.Cancel endpoints now report orders they couldn’t cancel
The success response fromPOST /orders/cancel/v2, POST /orders/cancel/event, and POST /orders/cancel/all now includes a data.notCancelled array. Each entry is an { orderHash, reason } pair for an order that matched the cancel request but wasn’t cancelled.Possible reason values:not_found_or_inactive— order was not found or is no longer active.already_pending_cancel— order already has a pending cancellation.order_locked— order is temporarily locked, likely because another fill/cancel flow is processing it.update_failed— order could not be cancelled due to an internal processing issue.
cancelledCount and orders fields are unchanged.See Cancel individual orders →, Cancel orders by event →, and Cancel all orders →.GET /trades now requires a scoping filter
Requests must now include at least one of bettor, marketHashes, sportXeventId, or startDate. Calls without any of these will be rejected.Existing queries that already filter by user, market, event, or time window are unaffected.See the endpoint reference →.Quarter lines (gradual rollout)
Soccer markets now support quarter lines (Asian handicap and Asian totals in .25 increments — e.g.-1.25, +0.75, Over 2.75).We’re rolling this out gradually: quarter lines are currently enabled on a subset of soccer fixtures and will expand across the rest of soccer over the coming weeks.Quarter-line markets are returned by /markets/active and /markets/find alongside all other lines, with line set to the quarter value.Example quarter-line market
Example quarter-line market
How quarter-lines work
A bet on a quarter-line market is split by the API into two trades on the surrounding whole and half lines, with half the stake on each at the same odds. A 50 USDC bet onBrighton -1.25 @ 1.9 is recorded as:- 25 USDC on
Brighton -1.0 @ 1.9 - 25 USDC on
Brighton -1.5 @ 1.9
What this means for integrators
- A single quarter-line bet appears as:
- two entries in
/trades— one raw on-chain trade per leg, each on its own legmarketHash. - three entries in
/trades/consolidated— the two leg consolidated trades (each on its legmarketHash) plus a synthetic parent consolidated trade keyed to the quarter-line market’smarketHashand a derived parentfillHash. The parent consolidated trade aggregatestotalStake/totalReturn/netReturnacross the legs and is the entry to use for user-facing P&L on the bet.
- two entries in
- Use
quarterLineFillHashto dedupe/trades/consolidatedresponses: the two leg consolidated trades carryquarterLineFillHash = <parent fillHash>while the parent consolidated trade carriesfillHash = <parent fillHash>andquarterLineFillHash = null. Sum just the parents — or just the legs, but never both — when aggregating P&L or volume. - Each child trade settles independently, so a single bet may produce 0–2 settlement transactions depending on the result. The parent entry’s settlement state in
/trades/consolidatedreflects the combined outcome once both leg entries have settled. - Orderbooks, posting orders, and filling on whole and half lines are unchanged.
Search fixtures by team name
NewGET /search endpoint returns up to 8 active fixtures matching a team name query.- Query:
query(required) — team name or partial name, 3–100 characters - Matching: case-insensitive, partial matches supported
- Response: active fixtures only, ordered by
gameTime, includingeventId, both team names, and the list of available markettypeIDs
WebSocket API migrating from Ably to Centrifugo
The new Centrifugo-based WebSocket API is now live. The legacy Ably-based API will continue to run in parallel until July 1, 2026, when it will be shut down. All clients must migrate before that date.What’s changing
- SDK: Replace
ablywithcentrifuge - Auth endpoint:
GET /user/token→GET /user/realtime-token/api-key - WebSocket URL: Now explicit —
wss://realtime.sx.bet/connection/websocket - Channel names: All channel names have changed (see table below)
- Subscription API:
channel.subscribe(message => ...)→ event-basedsub.on("publication", ctx => ...) - Gap recovery: Time-based
rewindreplaced by the snapshot + subscribe pattern usingpositioned: trueandrecoverable: true
Channel name changes
New channel:
order_book:event_{sportXEventId} — subscribes to all markets in an event with a single subscription.What’s improving
- Channel capacity: 512 channels per connection (up from 200)
- Throughput: No per-channel message rate cap (previously 200 msg/sec)
- Delivery reliability: Exact gap recovery using message IDs and server-side history, replacing imprecise time-based rewind