Skip to main content
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 PENDINGACTIVEINACTIVE.
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 to GET /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) — true when the trade’s marketHash is one of the two legs of a quarter-line market.
  • quarterLineParentMarketHash (string, optional) — present only when isQuarterLineLeg is true. Holds the parent quarter-line market hash (the market the user originally placed on), which differs from the trade’s own marketHash. Use this to group both legs back to the user-facing bet.
This mirrors the May 12 flags on consolidated trades, but at the per-order grain.

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 from line values or dedupe via quarterLineFillHash alone.

What’s new

  • Market.isQuarterLineMarket — returned on GET /markets/active and GET /markets/find. true when the market is a quarter line (Asian handicap or totals in .25 increments).
  • ConsolidatedTrade.isQuarterLineParenttrue on the synthetic parent entry that aggregates a quarter-line bet’s two legs. Use this for user-facing P&L and volume.
  • ConsolidatedTrade.isQuarterLineLegtrue on 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).
The same 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 pass hideQuarterLineLegs=true) and treat non-quarter trades normally — the parent’s totalStake, totalReturn, and netReturn already aggregate both legs.
  • Settlement-level analysis: filter to isQuarterLineLeg === true (or pass hideQuarterLineParents=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.
This pairs with the existing 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 from POST /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.
Previously these orders failed silently. The 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.

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 on Brighton -1.25 @ 1.9 is recorded as:
  • 25 USDC on Brighton -1.0 @ 1.9
  • 25 USDC on Brighton -1.5 @ 1.9
The two child trades settle independently against the same final score, which reproduces the five quarter-line outcomes that can’t be expressed by a single binary market:

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 leg marketHash.
    • three entries in /trades/consolidated — the two leg consolidated trades (each on its leg marketHash) plus a synthetic parent consolidated trade keyed to the quarter-line market’s marketHash and a derived parent fillHash. The parent consolidated trade aggregates totalStake / totalReturn / netReturn across the legs and is the entry to use for user-facing P&L on the bet.
  • Use quarterLineFillHash to dedupe /trades/consolidated responses: the two leg consolidated trades carry quarterLineFillHash = <parent fillHash> while the parent consolidated trade carries fillHash = <parent fillHash> and quarterLineFillHash = 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/consolidated reflects 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

New GET /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, including eventId, both team names, and the list of available market type IDs
See the endpoint reference → for the full schema.

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 ably with centrifuge
  • Auth endpoint: GET /user/tokenGET /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-based sub.on("publication", ctx => ...)
  • Gap recovery: Time-based rewind replaced by the snapshot + subscribe pattern using positioned: true and recoverable: 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

Action required

Migrate before July 1, 2026 — that is when the Ably API will be shut down. Message payloads are unchanged — only the SDK, auth flow, channel names, and subscription pattern require updates.See the Migration Guide → for step-by-step instructions.
Last modified on August 19, 2026