Skip to main content

2026-10-09

  • user_fill carries role (maker / taker): maker when your order was resting in the book, taker when it crossed it. It is missing on fills recorded before this release; treat a missing role as unknown, not as taker. See user_fill.
  • order_cancelled and order_expired carry originalQuantity, the order size as placed, the same value as on order_placed. originalQuantity - remainingQty is what had filled when the order ended, not a final fill total; read that from GET /api/orders/{id}. See user_orders frames.
  • Backend v2.9.22, live on production today, changes how order requests fail during an exchange restart. The items below cover it.
  • A recorded cancel with no recorded answer yet is 202 cancel_pending with Retry-After: 5, not 503. POST /api/orders/cancel used to answer this case 503 exchange_unavailable (upstream order_cancel_unanswered) with no Retry-After, and some clients retried every couple of seconds through a matching-engine restart. The response is now 202 with status and code set to cancel_pending, the orderId, a message and retryAfterSec; there is no remainingQty, because the order can still fill until the cancel lands. There is no recorded answer yet, so the order may still be open. Send the same cancel again after Retry-After: it is safe, cannot cancel twice, and completes the cancel as soon as the engine answers. The platform also completes it on its own, more slowly. The final status (CANCELLED, or FILLED if the order filled first) arrives on the user_orders WebSocket channel and on GET /api/orders/{id}. While the release rolls out, an older exchange node can still answer the same condition 503 cancel_unanswered with Retry-After: 5 and details.orderId; handle it the same way.
  • cancel-batch / cancel-all: cancel_unanswered is now cancel_pending. Same condition and the same action: cancel the ids named this way again after a few seconds, with a single cancel or a cancel-batch of exactly those ids (not another cancel-all, which is a new sweep that also cancels orders placed since), and re-read the order. See Cancelling orders.
  • 503 shard_owner_unavailable on every order route, cancel-all included. When the exchange node that serves your account is restarting or being taken over and refuses a request before recording anything, every order route now answers shard_owner_unavailable with Retry-After: 2. Before, a cancel, cancel-batch or cancel-all refused this way by a node that was shutting down or still taking the account over got 503 exchange_unavailable, which reads as “may have been recorded”. Nothing was attempted: repeat the same request. On place, place-batch, cancel and cancel-batch the platform first re-sends the request itself for up to about 6 seconds, so most restarts no longer reach you at all. It never re-sends a cancel-all. The vault split / merge / convert signatures and POST /api/me/splits-merges/{opId}/unlock answer the same code in the same case.
  • The exchange can now refuse an order when it is too busy. On POST /api/orders/place this is 503 trading_temporarily_unavailable with retryable: true. On place-batch only that entry is refused: success: false, code: journal_intent_not_durable, details.retryable: true, and the other entries keep their results. Nothing was admitted for the refused order, so resend the same signed order unchanged, under the same clientOrderId, shortly. Do not sign a new one. See Error codes.
  • lock_invariant in notCancelled comes only from older exchange nodes. Current nodes record the bookkeeping mismatch and cancel the order. The docs said it “typically clears on its own”.
  • Events carry stage and group. GET /api/events, GET /api/events/{slug} and GET /api/events/featured return the event’s tournament stage and group letter. They are set on match-less football fixtures too, where match is null, and they are the values ?stage= and ?group= now filter on, so those filters also match match-less fixtures.
  • IPv6 callers are rate-limited per /64. Every ip-scoped bucket counts an IPv6 caller by its /64 prefix instead of its full address. IPv4 is unchanged. See Rate limits.

2026-09-30

  • GET /api/events returns at most 100 events per response (was 500). A larger limit is served as 100 events: before, a limit from 101 to 500 returned that many events and a limit above 500 was 400 invalid_limit. A digits-only from/to span wider than 100 is cut to 100 events (it was cut to 500). Pages step by the served size, so page=3&limit=500 returns positions 200–299. A request without page, limit or a numeric to returns the first 100 events (was 500). invalid_limit is now returned only for a limit that is not digits or is below 1, and no longer carries error.details.max. See Pagination.
  • Migrating from limit=500. Nothing errors, but every response now holds at most 100 events. A client that reads one 500-event page, or stops paging when a page is shorter than 500, now sees only the first 100 events. A client that steps a numeric from/to by 500 (from=500&to=1000) gets positions 500–599 and never reads 100–499. Send limit=100, walk page=1, 2, … until a page comes back empty, and de-duplicate by id.
  • X-Events-Window-Capped: 100 marks a GET /api/events response whose request asked for more than 100 events (a limit above 100, or a digits-only to more than 100 past from) or gave no window at all (no page/limit and no digits-only to, which used to return up to 500 events); a limit or digits-only span of 100 or less never carries it. It describes the request, not the list: it does not mean more events follow, so keep paging until a page comes back empty.
  • ISO-8601 from/to now combine with page/limit, so a date range longer than one page can be paged. The date must start YYYY-MM-DD (2026-10-01, 2026-10-01T00:00:00Z); any other from/to together with page/limit, digits-only included, is still 400 ambiguous_pagination.
  • Listing totals on the first page of every listing. The first page of GET /api/events (page=1, from=0, or no page and no digits-only from) now carries total, the number of events matching every filter, independent of paging, and openMarketCount / totalMarketCount, their market counts summed. Before, only a q search carried total, and no response carried the two sums. Later pages carry none of them; with q, every page carries all three, plus facets. Treat them as optional: a first page whose totals cannot be computed in time comes without them. See Pagination.
  • Slimmer market rows on GET /api/events. Each markets[] entry no longer carries eventSlug, tags, homeCountry, awayCountry, sport, tradingMode or notTradableReason. resolutionCriteria is left out too, except on long-dated crypto markets (crypto set, not an Up/Down window, cryptoWindow null), which keep it. prediction keeps only version, kind, line, threshold and endsAt (dropped: provider, questionId, question, rulesText, capturedAt), and probabilityChange drops asOf and outcomes[].baselineAt. The event fields are unchanged; the event’s own slug, tags, sport, homeCountry and awayCountry replace the per-market copies. See Events overview.
  • additionalSources stays on list rows. An earlier version of this entry listed it as removed; GET /api/events market rows still carry it.
  • Reading resolutionCriteria from the list? Unlike the fields above, it was in the published schema: list rows were documented as the full market row. It is now an optional key of the list row (EventListMarketDto), present only on long-dated crypto markets: a client generated from the schema, or a strict validator, must accept a row without it. Read it from GET /api/events/{slug} or GET /api/markets/{slug}, or in bulk from GET /api/markets (full rows, up to 500 per page). Long-dated crypto rows on GET /api/events still carry it.
  • Detail is unchanged. GET /api/events/{slug} and GET /api/events/featured still return full MarketSummaryDto rows, as do GET /api/markets and GET /api/markets/{slug}. Read those when you need a dropped field.
  • Closed Up/Down days nest at most three settled windows. On GET /api/events?status=closed (or closed=true), a crypto Up/Down day event now nests only its three latest-ending RESOLVED or VOIDED windows, plus any window still awaiting settlement — the same windows GET /api/events/{slug} already returned. Before, it nested every settled window of the day (up to 288 on a 5-minute series). One exception: a day whose windows were all VOIDED before they ended still nests all of them (its detail returns 404). Other events and long-dated crypto markets are unchanged, and totalMarketCount still counts every window. Read the whole day from GET /api/crypto-series/{id}/windows?state=all with from/to set to that UTC day. See Crypto events.