> ## Documentation Index
> Fetch the complete documentation index at: https://docs.0xinsider.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> The Markdown of an API reference page leaves out the OpenAPI schema.
> Every object an endpoint returns or a webhook carries, with each field's type, presence and meaning, is on https://docs.0xinsider.com/api-reference/objects.md.
> The full OpenAPI 3.1 spec names each operation's and each webhook's exact parameters, request body and response schema: https://docs.0xinsider.com/api-reference/openapi.json.

# API changelog: November 7, 2026

> 29 API, SDK, CLI, MCP, OAuth, and feed changes from November 7, 2026, newest first, each with its effect on clients. Newer changes are on the API changelog.

These are the API changelog entries from November 7, 2026, newest first. The newest changes are on the [API changelog](https://docs.0xinsider.com/changelog.md).

**November 7, 2026**

Settled markets report PGA Tour golf as league pga, and the league filter selects it

[`GET /api/v1/markets/settled`](https://docs.0xinsider.com/api-reference/endpoint/list-settled-markets), the [`market_settled`](https://docs.0xinsider.com/api-reference/webhooks/market-settled) webhook, and the `MarketSettled` frame on [Stream](https://docs.0xinsider.com/api-reference/endpoint/get-stream.md) now set `league` to `pga` on a PGA Tour market. `league=pga` on the list and `"leagues": ["pga"]` in `settlement_filters` select those markets.

Before this change, a PGA Tour market had `league: null`, because its event slug starts with a year (`2026-world-wide-technology-championship-winner`) instead of a league. No league filter could select it. On November 7, 453 of the 767 moneyline states recorded in one hour were these markets.

* Other golf tours keep their leagues from the event slug: `lpga`, `dpwt`, `kft`, and `liv`.
* The list derives `league` when you read it, so PGA Tour rows recorded before this change also read `pga` now.
* A market whose slug names no league and is not on the PGA Tour still has `league: null`.

**Backward compatible.** No field is added or removed. `league` on PGA Tour markets changes from `null` to `pga`, and a subscription that names leagues still leaves them out unless it names `pga`.

**What to change:** Nothing to keep your current results. To receive PGA Tour golf, add `pga` to `league` or `leagues`. If you grouped `league: null` rows as golf, read `pga` instead.

**November 7, 2026**

Game market settlements name the winner of a resolved oracle question

[`GET /api/v1/games/{event_slug}/markets`](https://docs.0xinsider.com/api-reference/endpoint/list-game-markets) now sets `settlement.result` and `settlement.winning_outcome_index` on a settled market whose settlement is a resolved oracle question. We read the winner from the payout Polymarket's ConditionalTokens contract recorded on Polygon, once per market. `payouts` stays `null` there, because Polymarket's resolution row for the question carries none.

Before this change, both fields were read from `payouts` only, so a resolved oracle question read settled with no winner. On November 7, 61 of the 100 markets of one golf tournament read that way.

The [`market_settled`](https://docs.0xinsider.com/api-reference/webhooks/market-settled) webhook, the `MarketSettled` frame on [Stream](https://docs.0xinsider.com/api-reference/endpoint/get-stream.md), and [List settled markets](https://docs.0xinsider.com/api-reference/endpoint/list-settled-markets.md) carry the same states:

* A question recorded before the contract paid, with a `null` winner, is followed by a state that names the winner, with its own `settlement_id`.
* A later state that names a winner already sent, such as Polymarket's row with `payouts`, is not sent by the webhook or Stream. List settled markets still returns it, at its own `observed_at`.
* A state we first see more than 6 hours after the market resolved is still not sent, and still listed.

**Backward compatible.** No field is added or removed. Two fields that were `null` now carry the winner, and a market can have one more settled state.

**What to change:** Nothing if you read the winner from `result` and `winning_outcome_index`. If you derived it from `payouts`, read those two fields instead, because `payouts` stays `null` on a resolved oracle question. If you act once per winner on List settled markets rows, skip a row whose market and winner you already handled.

**November 7, 2026**

Edge Hunter most-held markets keep an untraded holder read for up to 30 minutes

[`GET /api/v1/edge-hunter`](https://docs.0xinsider.com/api-reference/endpoint/get-edge-hunter#most-held-selection) with `admission=most_held_48h` or `most_held_48h_by_type`: a market only the most-held selection takes, which kicks off more than an hour later and has had no trade since its last holder read, now keeps that read for up to 30 minutes, with no new read. The default selection already did this.

Before this change such a market kept its last read for only 15 minutes. When a busy kickoff window left no time to read it again, it then dropped as `holder_deadline_unavailable` and the snapshot was marked `degraded`. The same market in the default selection kept its row.

**Backward compatible.** The response shape is unchanged. `holder_snapshot_at` on such a row can now be up to 30 minutes old, and `provider_read_source` is `cached`.

**What to change:** Nothing. To judge a row's age, read `holder_snapshot_at`.

**November 7, 2026**

Edge Hunter most-held selections skip stopped games' side markets and keep live underdogs

[`GET /api/v1/edge-hunter`](https://docs.0xinsider.com/api-reference/endpoint/get-edge-hunter#most-held-selection) with `admission=most_held_48h` or `most_held_48h_by_type` now skips every market of a game Polymarket marks postponed, cancelled, suspended, or final, including its exact score, half-time, and other side markets. A market on the game's main event, such as a team-win market, is skipped as decided only at a Yes price of 0.02 or lower, or 0.98 or higher. A skipped market counts as `provider_excluded` in `funnel`, `coverage`, and `departures[].reason`.

Before this change:

* The decided bounds were 0.05 and 0.95, so a live underdog priced at 0.048 lost its place in the Soccer selection.
* A postponed game's exact score and half-time markets could still take places, because only the game's main markets were matched.
* A skipped market was reported as `capacity_limited`, which reads as losing its place to a market with more graded holders.

**Backward compatible.** The response shape is unchanged, and `provider_excluded` is an existing reason. Rows change: a market priced between 0.02 and 0.05, or between 0.95 and 0.98, can now take a place.

**What to change:** Nothing. To tell a skipped market from one that lost its place, read `provider_excluded` apart from `capacity_limited`.

**November 7, 2026**

New GET /api/v1/markets/settled lists game market settlements since a time

The new [`GET /api/v1/markets/settled`](https://docs.0xinsider.com/api-reference/endpoint/list-settled-markets) lists the sports and esports game markets that settled since a time, oldest first. Each row is the `data` object the [`market_settled`](https://docs.0xinsider.com/api-reference/webhooks/market-settled) webhook sends. Every plan with an API key can read it, at a cost of 1.

Before this change, a missed `market_settled` delivery could be replayed only from the delivery log, and a client without a webhook learned of a settlement only by reading each game's markets until its `settlement` changed.

* `since` (RFC 3339) starts the list; `next_cursor` continues it and is always returned. `has_more: false` means you have read up to `complete_through`, 2 minutes before the read. Keep the cursor and read it again later for newer rows.
* `sports_market_type` (default `moneyline`, `*` for every type) and `league` filter the rows as `settlement_filters` does. Filters sent with a cursor must match the cursor's, or the read is a `400` with `error.reason` `cursor_expired`.
* Unlike the webhook, it lists a state first seen more than 6 hours after its market resolved.
* A first page that misses the 200 ms read deadline answers `503` with `error.reason` `read_model_warming` and `Retry-After: 1`.

The remote MCP server adds the `list_settled_markets` tool with the same parameters; `complete_through` is kept beside `data`. The TypeScript SDK adds `listSettledMarkets()` in its next release. The [settlement notifier recipe](https://docs.0xinsider.com/recipes/settlement-notifier.md) uses the endpoint to catch up after downtime.

**Backward compatible.** This adds an endpoint and an MCP tool.

**What to change:** Nothing. To catch up on settlements after your receiver was down, or to follow them without a webhook, read this endpoint from a `since` time and keep its `next_cursor`.

**November 7, 2026**

market\_settled's default filter includes every golf player market; webhook signatures can carry two candidates at any time

The default `settlement_filters` of the [`market_settled`](https://docs.0xinsider.com/api-reference/webhooks/market-settled) webhook, `{"sports_market_types": ["moneyline"]}`, was described as each game's winner market. It matches every market Polymarket labels `moneyline`. That is each head-to-head game's winner market, but in golf and other field events Polymarket labels every player market `moneyline`: the winner, make the cut, and round leader markets. One golf tournament sent 452 states to an endpoint with the default filter in about two minutes.

**Narrow it.** Set `leagues` to the leagues you want, or name markets with `condition_ids`. A golf event slug that starts with a year, such as `2026-world-wide-technology-championship`, has a `null` `league`, and no `leagues` list matches it.

**Signatures.** `x-0xinsider-signature` can carry more than one comma-separated candidate while your endpoint's `secret_rotation.status` is `idle`, because a rotation of 0xinsider's own signing key adds one to every delivery. The [Webhooks guide](https://docs.0xinsider.com/guides/webhooks.md#check-the-signature) said this happened only during a staged rotation. Accept the request when any candidate matches.

**Backward compatible.** Filtering and signing are unchanged; only their descriptions changed, in the OpenAPI spec and on this site. An endpoint subscribed without `settlement_filters` keeps the same default.

**What to change:** If your endpoint uses the default filter and should not receive golf player markets, set `leagues` or `condition_ids`. If your receiver checks only the first signature candidate, check every one.

**November 7, 2026**

Pick of the Day display holders carry the price edge the pick card prints

[`GET /api/v1/pick-of-the-day`](https://docs.0xinsider.com/api-reference/endpoint/get-pick-of-the-day) now returns `category_price_edge` and `category_scope_price_edge` on `display_holders[]` entries. Each is a `PickHolderPriceEdge` object: `markets`, `hit_rate`, `avg_price`, and `edge`, which is `hit_rate` minus `avg_price`, all fractions. It shows how often the side the wallet bought won, against the average price it paid, over its settled markets of \$20 or more at a known entry price.

Before this change, only the pick card on 0xinsider.com printed this edge, for example `-17.1 pp` beside a Premier League win rate. The API returned the win rate and the average price, but not the edge.

* `category_price_edge` covers the pick's whole category, such as Soccer, beside a measured `category_win_rate`. It stays the whole category's edge when the rate was measured in a game or league.
* `category_scope_price_edge` covers only the game or league that `category_win_rate_game` names, and appears only beside it. It is never filled from the whole category's edge.
* An edge appears only when it has 30 or more counted markets, a sample that agrees with the win rate within 10 points, and a realized profit over those markets.
* Otherwise `category_price_edge_hold` or `category_scope_price_edge_hold` says why: `hold` is `category_loss`, `too_few_markets`, `sample_mismatch`, or `no_markets`, and `record` carries the four figures when they exist. A hold never sits beside the edge it explains.
* Recomputed when served from the daily record rebuild, never frozen with the pick. Full picks only: a locked or teaser pick carries no holders, and the `holders[]` compatibility list never carries these fields.

The SDK adds the four fields to `PickHolder`, with the new `PickHolderPriceEdge` and `PickHolderPriceEdgeHold` types. The `get_pick_of_the_day` MCP tool's output schema carries them.

**Backward compatible.** Four optional fields are added.

**What to change:** Nothing. To show the edge the pick card prints, read `category_scope_price_edge` beside `category_win_rate_game`, and `category_price_edge` otherwise.

**November 7, 2026**

New market\_settled webhook and MarketSettled stream frame for settled game markets

The new [`market_settled`](https://docs.0xinsider.com/api-reference/webhooks/market-settled) webhook sends a sports or esports game market's settlement once per settled state, usually within a few minutes of Polymarket resolving it. [`GET /api/v1/stream`](https://docs.0xinsider.com/api-reference/endpoint/get-stream) carries the same fields as `MarketSettled` frames. Both are Pro and Max only.

Before this change, you learned that a game market had settled only by reading [`GET /api/v1/games/{event_slug}/markets`](https://docs.0xinsider.com/api-reference/endpoint/list-game-markets) until its `settlement` changed.

**Once per settled state.** A market can settle twice: first as a resolved oracle question without payouts, then with them. Each state is sent once, with its own `settlement_id`, and the event `id` is `evt_market_settled_{condition_id}_{settlement_id}`. A state we first see more than 6 hours after it resolved is not sent.

**Subscribe.** [`POST /api/v1/webhooks`](https://docs.0xinsider.com/api-reference/endpoint/create-webhook) and `PATCH /api/v1/webhooks/{id}` take a new optional `settlement_filters` object. It is refused with `400` (`param: "settlement_filters"`) when `event_types` does not include `market_settled`. Every field you set must match:

* `leagues`: slug prefixes such as `nba` or `epl`, at most 32.
* `sports_market_types`: Polymarket market types such as `moneyline`, `spreads`, or `totals`, at most 64, where `*` is every type. Omitted, it is `["moneyline"]`, or `["*"]` when `condition_ids` is set.
* `event_slugs` and `condition_ids`: at most 100 each.

Subscribing without `settlement_filters` stores the moneyline default. A PATCH without it keeps the stored settings, and removing the event clears them. The webhook object now carries `settlement_filters`, which is `null` unless the event is subscribed.

**Payload.** `data` carries:

* The market: `condition_id`, `event_slug`, `game_id`, `league`, `sports_market_type`, and `outcomes`, each outcome name at its index.
* `settlement`, the object the game markets routes serve: `payouts`, `result`, `winning_outcome_index`, `was_disputed`, `resolved_at`, and `observed_at`.
* `copy_of_event_slug`, set on a Polymarket `polyv2` copy of a game to the original game's slug. A copy is sent only to a filter that names its condition ID or its own event slug.

**Stream.** `MarketSettled` frames are sent for every game market of every type, with no `settlement_filters`. Select them with `event=MarketSettled`. `condition_id` matches the frame's market.

The MCP tools `list_webhooks` and `get_webhook` return `settlement_filters`. The TypeScript SDK adds `MarketSettledEvent` and `MarketSettledData` in its next release.

**Backward compatible.** This adds an event type, a request field, a response field, and a stream frame type. Existing webhook subscriptions are unchanged. A stream opened without `event` now also receives `MarketSettled` frames.

**What to change:** Nothing, unless you want settlement alerts: subscribe an endpoint to `market_settled`, or open the stream with `event=MarketSettled`. If you read the stream without `event`, skip frame types you do not handle.

**November 7, 2026**

Pick of the Day display holders carry category\_scope\_avg\_price, the league's own price

[`GET /api/v1/pick-of-the-day`](https://docs.0xinsider.com/api-reference/endpoint/get-pick-of-the-day) now returns `category_scope_avg_price` on a `display_holders[]` entry whose win rate was measured in a game or league (`category_win_rate_game` present). It is the wallet's average entry price in that game or league alone, a 0..1 fraction of \$1, under the same sample rules and 5-market floor as `category_avg_price`.

Before this change, a holder row named the league its rate came from but carried only `category_avg_price`, the whole sport's price. A Premier League rate read against the Soccer price.

* Present only beside `category_win_rate_game`, and only when the wallet's sample there clears the floor. Absent otherwise.
* Recomputed when served, never frozen with the pick, like the win rate beside it.
* Full picks only: a locked or teaser pick carries no holders. The `holders[]` compatibility list never carries it.

The SDK's `PickHolder` type and the `get_pick_of_the_day` MCP tool's output schema carry the field.

**Backward compatible.** One optional field is added.

**What to change:** Nothing. To read a league rate against its own price, use `category_scope_avg_price` when present and `category_avg_price` otherwise.

**November 7, 2026**

A settled game market answers its settlement on the first read, with a fixed observed\_at

[`GET /api/v1/games/{event_slug}/markets`](https://docs.0xinsider.com/api-reference/endpoint/list-game-markets) now answers a settled game market with `settlement.status` `observed` on the first read of its game. We record each game market's settlement a few minutes after it resolves, whether or not anyone reads the game. [`GET /api/v1/games/{event_slug}`](https://docs.0xinsider.com/api-reference/endpoint/get-game), [`GET /api/v1/games`](https://docs.0xinsider.com/api-reference/endpoint/list-games) with `expand=settlement` and [`GET /api/v1/market/{condition_id}/snapshot`](https://docs.0xinsider.com/api-reference/endpoint/get-market-snapshot) serve the same record.

Before this change, a game no read had asked about lately answered `unavailable` on its first read, even when its markets had settled days earlier. On a settled market, `observed_at` moved to the time of the latest Polymarket read, about every 10 minutes.

* On a settled market, `observed_at` is now when we first saw that settlement. It is the same on every read.
* A market that resolved before this change carries the time we first saw it after the change.
* On a market that has not settled, `observed_at` is unchanged: when Polymarket last answered.
* `unavailable` now means no read is in hand and no settlement of the market is recorded. Ask again shortly, and never treat it as unsettled.

The MCP tools `list_game_markets`, `get_game`, `list_games` and `get_market_snapshot` serve the same block.

**Backward compatible.** No field is added or removed.

**What to change:** Nothing. To time a settlement, read `observed_at` on a settled market once: it does not move.

**November 7, 2026**

Games carry status.ended\_at and status.ended\_at\_source, the observed end time

[`GET /api/v1/games`](https://docs.0xinsider.com/api-reference/endpoint/list-games) and [`GET /api/v1/games/{event_slug}`](https://docs.0xinsider.com/api-reference/endpoint/get-game) now return `status.ended_at`, when the game ended as first observed, and `status.ended_at_source`, who observed it. Both are on every game and are `null` unless the game is `ended` and an end was observed.

Before this change, a game said whether it had ended but not when.

* `sports_ws`: the first Polymarket Sports WebSocket update that reported the game final, at the time we received it.
* `gamma_finished`: Polymarket's event finish time, for a game no such update ended. It is served only when it is earlier than the market's resolution, and never for an MMA or boxing card.
* We never infer an end from the kickoff, the game clock or the resolution. A game that ended before this change has `null`.

The SDK's `GameStatus` type and the `list_games` and `get_game` MCP tools carry both fields.

**Backward compatible.** Two fields are added to `status`.

**What to change:** Nothing. To time a game's end, read `status.ended_at` and treat `null` as unknown, not as still in play.

**November 7, 2026**

Leaderboard value-sort cursors report a ranking that moved within one generation

[`GET /api/v1/leaderboard`](https://docs.0xinsider.com/api-reference/endpoint/get-leaderboard) now sets `meta.continued_from_generation` on a `sort=volume`, `predictions`, `pnl`, `win_rate` or `edge` page whenever the board was refreshed after your cursor was issued. The board refreshes about once a minute, and nearly every refresh changes some trader's volume, P\&L, win rate or edge, so the field can now equal `meta.ranking_generation`.

Before this change, these sorts set the field only when the ranking generation changed. A page read after a refresh inside one generation came back without it, although its order could have moved and it could repeat or miss a trader.

* On these sorts, a page is exact only when it is read from the same refresh as the page that issued its cursor. If you wait a minute between pages, expect the field to be set.
* The default order, `sort=rank` and `sort=grade` keep the rule of the earlier November 7 entry on leaderboard cursors: they report a move only when a wallet's rank, grade or membership changed.
* A value-sort cursor issued before this change is reported as moved on its next page.
* `next_cursor` on these sorts now changes with each refresh, so the page's `ETag` does too.

**Backward compatible.** No field is added or removed. The field is now set on more pages, in the cases where it was missing.

**What to change:** Nothing, if you already handle `meta.continued_from_generation`: de-duplicate rows by `id`, or restart from the first page when it is set. For an exact walk of a value sort, read the pages without long pauses.

**November 7, 2026**

List props book filters skip markets we do not serve before they check books

[`GET /api/v1/props`](https://docs.0xinsider.com/api-reference/endpoint/list-props) with `max_spread_c` or `min_book_usd_within_2c` now leaves out the markets we do not serve before it checks any order book. A game's event can hold markets outside sports and esports, which no page lists, and those markets no longer change what a filtered page returns.

Before this change, such a market was checked like any prop, so one market we do not serve could change a filtered page:

* When we could not read its book, the page ended before it with `has_more: true`, or, as the page's first prop, the request answered `503` with `error.reason` `read_model_warming`.
* When we held no book for it, it was counted on `coverage.rows_without_book`.

Pages without either filter are unchanged.

**Backward compatible.** A filtered page can now hold more props, and `coverage.rows_without_book` counts only props the page could list.

**What to change:** Nothing.

**November 7, 2026**

Leaderboard activity filter counts 30 days from the ranking's date, not your request's

[`GET /api/v1/leaderboard`](https://docs.0xinsider.com/api-reference/endpoint/get-leaderboard) with `activity=active` or `activity=inactive` now counts the 30 days of activity up to the UTC date of the ranking it serves. That is the date `summary.active_30d` is counted at, so the list and the count always agree.

Before this change, the filter counted up to the date of your request. For up to 3 minutes after UTC midnight, before the ranking refreshed, `activity=active` could leave out a trader that `summary.active_30d` still counted, and a `next_cursor` walk could report an exact continuation although that trader had left the list.

**Backward compatible.** No parameter or field changes. Outside those 3 minutes after midnight, every response is the same.

**What to change:** Nothing.

**November 7, 2026**

Quotes carry order book depth, and list props filters on it with min\_book\_usd\_within\_2c

Every quote from [`GET /api/v1/games/{event_slug}/markets`](https://docs.0xinsider.com/api-reference/endpoint/list-game-markets) and [`GET /api/v1/props`](https://docs.0xinsider.com/api-reference/endpoint/list-props) with `expand=quotes` now carries `book_depth_within_2c`: the dollars resting on each side of the book within 2 cents of its midpoint. List props takes a new `min_book_usd_within_2c` parameter that keeps only props with that much on each side, so `?league=nfl&min_book_usd_within_2c=500` lists the NFL props you can trade \$500 against near the price.

Before this change, no quote carried depth, and [Market snapshot](https://docs.0xinsider.com/api-reference/endpoint/get-market-snapshot.md) never filled its `bid_depth_usdc` and `ask_depth_usdc`.

* `book_depth_within_2c` is `{bid_usd, ask_usd}`: price times size of bids at or above the midpoint minus 2 cents, and of asks at or below the midpoint plus 2 cents, over every level of the book. It is the measure the `market_price_moved` webhook already sends under the same name, here as numbers. It is `null` when we hold no depth for the book; read again after a few seconds.
* Market snapshot's `order_book.bid_depth_usdc` and `ask_depth_usdc` now carry the same two sums, rounded to the cent.
* `min_book_usd_within_2c` is a whole number of dollars from 1 to 10,000,000, the range the webhook filter takes. Any other value is a `400` with `error.reason` `invalid_query`. With `max_spread_c`, one outcome must pass both.
* A prop whose book or depth we do not hold yet is left out of `data` and counted on `coverage.rows_without_book`, as with `max_spread_c`.
* The `list_props` MCP tool takes `min_book_usd_within_2c`. The SDK's types carry both fields in the next `@0xinsider/sdk` release.

**Backward compatible.** One optional parameter and one quote field are added, and two snapshot fields that were always absent now appear; nothing is removed.

**What to change:** Nothing. To list props with room to trade, send `min_book_usd_within_2c`, and read `coverage.rows_without_book` before you treat a short page as the whole board.

**November 7, 2026**

List props filters on the order book spread with max\_spread\_c

[`GET /api/v1/props`](https://docs.0xinsider.com/api-reference/endpoint/list-props) takes a new `max_spread_c` parameter. It keeps only props where one outcome's best ask minus its best bid is at most that many cents, so `?league=nfl&max_spread_c=2` lists the NFL props with a market at most 2 cents wide. It reads the order books we already hold, the ones `expand=quotes` serves, and makes no call to Polymarket while you wait.

Before this change, you read every page of a league and compared `best_bid` and `best_ask` yourself.

* The value is in cents, above 0 and at most 100, in steps of 0.01. A book exactly at the cap is kept. Any other value is a `400` with `error.reason` `invalid_query`.
* A prop whose book we do not hold yet is left out of `data` and counted on the new `coverage.rows_without_book`, which is `null` without the filter. The read starts a fetch of the league's books, so read again after a few seconds.
* The filter decides which props are on a page, so `next_cursor` stays exact. A page can hold fewer than `limit` props while `has_more` is `true`.
* Each page checks the books as we hold them when you read it, and `next_cursor` never comes back to a prop a page passed over. When `coverage.rows_without_book` is above 0, read from the first page again after a few seconds to check those props.
* If we cannot read the order book of the first prop a page has to check, the read answers `503` with `error.reason` `read_model_warming` and a `Retry-After`, instead of an empty page whose `next_cursor` is the one you sent.
* The `list_props` MCP tool takes `max_spread_c`. The SDK's types carry it in the next `@0xinsider/sdk` release.
* This change adds no depth filter. `min_book_usd_within_2c`, in the entry above, does.

**Backward compatible.** One optional parameter and one response field are added; nothing is removed.

**What to change:** Nothing. To list tight markets, send `max_spread_c`. If `coverage.rows_without_book` is above 0, read from the first page again before you treat the result as the whole board.

**November 7, 2026**

Leaderboard cursors survive a ranking refresh and say when the ranking moved

[`GET /api/v1/leaderboard`](https://docs.0xinsider.com/api-reference/endpoint/get-leaderboard) now continues a `next_cursor` from an earlier ranking generation instead of refusing it. The next page follows the cursor's position in the current ranking, and a new `meta.continued_from_generation` names the cursor's generation when the ranking moved under it.

Before this change, a cursor answered `400` with `error.reason` `cursor_expired` whenever the ranking generation moved, about once a minute, even when no rank had changed. A caller that paused between pages had to restart its walk.

* `meta.continued_from_generation` is absent on a first page and on an exact continuation. The default order, `sort=rank` and `sort=grade` are exact while no wallet's rank, grade or membership moved; every other sort reports any new generation.
* When it is set, a trader who crossed the cursor's boundary can repeat a row you already hold or be missing from the walk.
* A cursor sent with another `category`, `strategy` or `activity` still answers `400` `cursor_expired`.
* The `get_leaderboard` MCP tool's output schema and the `@0xinsider/sdk` types carry the new field in their next release.

**Backward compatible.** One optional field is added, and a request that used to fail now succeeds.

**What to change:** Nothing. For an exact walk, de-duplicate rows by `id`, or restart from the first page when `meta.continued_from_generation` is set.

**November 7, 2026**

List games refuses an unknown status and names an uncovered sport in a header

[`GET /api/v1/games`](https://docs.0xinsider.com/api-reference/endpoint/list-games) now answers a `status` outside its published values with `400`, `error.reason` `invalid_query` and `error.param` `status`, and the message lists the accepted values. A `sport` we do not cover still returns an empty list, and the response now names `sport` in the new `X-Query-Unmatched` header.

Before this change, both returned `200` with an empty list and nothing said why, so `status=upcoming` read as "no games". Upcoming games are `scheduled`.

* `status` accepts `scheduled`, `live`, `paused`, `ended`, `postponed`, `cancelled`, `suspended`, `delayed` and `unknown`, the values the OpenAPI spec and the `list_games` MCP tool have always listed.
* `X-Query-Unmatched` is readable from browser JavaScript, and `@0xinsider/sdk` lifts it into `meta.queryUnmatched` in its next release.

**Breaking** for clients that send a `status` outside the published values: they get `400` instead of an empty list. Every other request is unchanged.

**What to change:** Send one of the published `status` values. To read an empty page, check `X-Query-Unmatched` or `coverage.sports`.

**November 7, 2026**

Props quotes carry a book for props we do not stream, and quotes name the book's source

[`GET /api/v1/props`](https://docs.0xinsider.com/api-reference/endpoint/list-props) with `expand=quotes` now carries the best bid and best ask for props we are not streaming. Each read starts a fetch of the league's order books from Polymarket after the response, at most every 10 seconds, so the next read of any page of that league has them.

Before this change, every prop we were not already streaming read `book_unavailable_reason: not_streamed` with no book. On November 7, 2026, all 200 outcomes of the first 100 NFL props read that way.

* A new `quote.book_source` field names who read the book: `clob_stream` (Polymarket's live market feed) or `clob_rest` (a fetch of Polymarket's order books). It is `null` when there is no book. It is on [`GET /api/v1/games/{event_slug}/markets`](https://docs.0xinsider.com/api-reference/endpoint/list-game-markets) too.
* A prop with no book yet reads `cache_miss`, which means read again after a few seconds. `not_streamed` stays in the enum, but no endpoint returns it any more.
* `book_observed_at` is when the book last changed, from either source.
* The `list_props` and `list_game_markets` MCP tools describe the change. The SDK's types carry `book_source` in the next `@0xinsider/sdk` release.

**Backward compatible.** One field is added; no field or value is removed.

**What to change:** Read `best_bid` and `best_ask` on props as you do on game markets. If you skipped `not_streamed` books, read again on `cache_miss` instead.

**November 7, 2026**

Market snapshot top\_of\_book reasons add cache\_unavailable when we could not read a book from our cache

[`GET /api/v1/market/{condition_id}/snapshot`](https://docs.0xinsider.com/api-reference/endpoint/get-market-snapshot) now reads `reason: cache_unavailable` on an outcome's `top_of_book`, and on `freshness.top_of_book`, when we could not read that outcome's book from our cache: the read failed, ran out of time, or found an entry we could not decode. Nothing is known about the book then, and a read a few seconds later may answer. The game markets and props quotes name the same failure the same way.

Before this change, the snapshot served `cache_miss` for that failure, or `stream_capacity` when we were not streaming the market, which says reading again does not help.

* `cache_miss` now means only that our cache answered and holds no book for the outcome yet.
* `stream_capacity` keeps its meaning, and is served only when the cache read succeeded.
* `freshness.top_of_book.reason` reads `cache_unavailable` when no outcome has a book with a side and the read did not answer for at least one of them.

**Backward compatible.** The `reason` string gains one value; no field changes.

**What to change:** Treat `cache_unavailable` as retryable and read again after a few seconds. A client that refuses unknown reason values must accept it.

**November 7, 2026**

Quote book reasons add cache\_unavailable when we could not read a book from our cache

The `quote` object on [`GET /api/v1/games/{event_slug}/markets`](https://docs.0xinsider.com/api-reference/endpoint/list-game-markets) and [`GET /api/v1/props`](https://docs.0xinsider.com/api-reference/endpoint/list-props) with `expand=quotes` now reads `book_unavailable_reason: cache_unavailable` when we could not read that outcome's book from our cache: the read failed, ran out of time, or found an entry we could not decode. Nothing is known about the book then, and a read a few seconds later may answer.

Before this change, the same failure read `cache_miss` or `stream_capacity` on game markets, and `not_streamed` on props, which says reading again does not help.

* `cache_miss` now means only that our cache answered and holds no book for the outcome yet.
* `not_streamed` and `stream_capacity` keep their meaning, and are served only when the cache read succeeded.
* The `list_game_markets` and `list_props` MCP tools name the new value. The SDK's types carry it in the next `@0xinsider/sdk` release.

**Backward compatible.** The `book_unavailable_reason` enum gains one value; no field changes.

**What to change:** Treat `cache_unavailable` as retryable and read again after a few seconds. A client that refuses unknown enum values must accept it.

**November 7, 2026**

Game markets answers first reads within 250 ms; a slow expand reads unavailable sooner

[`GET /api/v1/games/{event_slug}/markets`](https://docs.0xinsider.com/api-reference/endpoint/list-game-markets) now answers the first read of a game within its 250 ms budget, with every `expand`. When listing the game's markets takes long, the endpoint waits less for `settlement`, `sharp_money`, `quote`, and `trading_terms`, and a block it cannot read in that time is `unavailable`.

Before this change, each block waited its full time after the markets were listed. On November 7, 2026, 6 of 13 first reads of NFL games with every `expand` took longer than 250 ms, up to 279 ms.

* A first read can return `unavailable` for a block that it used to wait for and return. The read keeps running, so the next read a few seconds later carries it.
* A read of a game that was read recently is unchanged.

**Backward compatible.** No field or status changes.

**What to change:** Nothing, if you already read again when a block is `unavailable`, as the endpoint page says.

**November 7, 2026**

ITF live state skips a match when an abbreviated name fits two players in the draw

ITF singles live state now skips a match when API-Tennis abbreviates a player's name and that name fits two players Polymarket listed in the same tournament over the last 48 hours. "J. Smith" fits both James Smith and John Smith. Brothers often play the same ITF draw.

Before this change, such a match could take another match's state. One brother's finished match could set the final score of the other brother's next match against the same opponent.

* A skipped match keeps `phase: unknown` on the [`market_price_moved`](https://docs.0xinsider.com/api-reference/webhooks/market-price-moved) webhook and the `MarketPriceMoved` stream frame. On [`GET /api/v1/games`](https://docs.0xinsider.com/api-reference/endpoint/list-games) it carries no live score, the same as a match API-Tennis does not list.
* A full name, or an abbreviation that fits one player only ("Ja." against "Je."), binds as before.

**Backward compatible.** No field changes; a few ITF matches carry no live state rather than a wrong one.

**What to change:** Nothing.

**November 7, 2026**

New GET /api/v1/props lists one league's player props with player, stat and line

[`GET /api/v1/props`](https://docs.0xinsider.com/api-reference/endpoint/list-props) lists the player props of one league's games in one paged list, each row with the player's name, the stat and the line as separate fields. It covers the games [`GET /api/v1/games`](https://docs.0xinsider.com/api-reference/endpoint/list-games) serves for the league, and reads both places Polymarket files props: an NFL game's `-player-props` and `-first-td-scorer` events, and the NBA game event.

Before this change, reading a league's props took one `GET /api/v1/games` call and then one [`GET /api/v1/games/{event_slug}/markets`](https://docs.0xinsider.com/api-reference/endpoint/list-game-markets) call per game, and the player's name had to be split out of `group_item_title`.

* Filters: `league` (required), `starts_after` and `starts_before`, `player` (part of the name, ignoring case and accents), `stat` (Polymarket market types), `status`, `limit` (1 to 200, default 50) and `cursor`.
* `player.name` is Polymarket's market label before its first `: `, the name the event page shows. When Polymarket gives no label, `player` is `null` and `player_unavailable_reason` is `missing_provider_title`. `line` is `null` on a yes/no prop.
* `expand=quotes` adds each outcome's `quote`, the object `GET /api/v1/games/{event_slug}/markets` returns. A prop we are not already streaming carries no book, with the new `book_unavailable_reason` value `not_streamed`, which only this endpoint returns.
* `coverage.sources_unavailable` names any game source for the league that did not answer for the read, in the `<sport>:<half>` form `GET /api/v1/games` uses, so a short page is never read as the whole board.
* The request costs 2, plus 1 with `expand=quotes`.
* The MCP tool `list_props` calls it. The SDK's `listProps()` and its types ship in the next `@0xinsider/sdk` release.

**Backward compatible.** A new endpoint and MCP tool. The quote object's `book_unavailable_reason` enum gains `not_streamed`, which `GET /api/v1/games/{event_slug}/markets` never returns.

**What to change:** Nothing. To read a league's props, call `GET /api/v1/props?league=nfl` instead of walking each game's markets.

**November 7, 2026**

market\_price\_moved no longer sends a second event inside one window during a release

The [`market_price_moved`](https://docs.0xinsider.com/api-reference/webhooks/market-price-moved) webhook and the `MarketPriceMoved` frame on [`GET /api/v1/stream`](https://docs.0xinsider.com/api-reference/endpoint/get-stream) now keep their delivery rule while 0xinsider releases a new server version. Inside one window, only a larger move in the same direction fires again, and it carries `previous_level_cents`.

Before this change, a release could send a second event for a market inside a window that was already open. That event carried `previous_level_cents: 0`, and some reported the opposite direction. Three releases on November 7, 2026 sent 9 such events.

**Backward compatible.** No field changes; fewer duplicate events arrive.

**What to change:** Nothing.

**November 7, 2026**

Position timeline returns every stored fill, not only large trades since July 17

[`GET /api/v1/trader/{address}/position-timeline`](https://docs.0xinsider.com/api-reference/endpoint/get-position-timeline) and [`GET /api/v1/traders/{trader}/position-timeline`](https://docs.0xinsider.com/api-reference/endpoint/get-trader-position-timeline) now return every fill 0xinsider stores for the wallet in the market, at any date. `running_amount` and `running_avg_price` count all of them.

Before this change, both routes returned just the large-trade fills recorded from July 17, 2026. A wallet whose fills were smaller or older got `200` with an empty list, which read as "no fills".

* `tx_hash` can be `null`. The hash comes from the large-trade alert for the fill, and a smaller fill has none.
* `id` is an opaque `fill_...` value. Ids from earlier responses (`pe_...`) do not match any row now.
* A `cursor` from an earlier response returns `400` with `error.reason` `cursor_expired`.
* A wallet with more than 20,000 fills in the market before the requested page returns `404` with `error.reason` `fill_history_too_large`. The API refuses instead of serving running totals it could not finish. Like `trader_not_tracked`, it is final; do not retry.
* The MCP tools `get_position_timeline` and `get_position_timeline_by_id` and the SDK types describe the same contract. The SDK adds `FillHistoryTooLargeError` from the next `@0xinsider/sdk` release.

**Breaking** for clients that type `tx_hash` as a string that is always set, store fill `id`s, or keep a timeline cursor across this change.

**What to change:** Treat `tx_hash` as nullable. Request the first page again without a cursor. Handle `404` `fill_history_too_large` as final.

**November 7, 2026**

Trader category records now list each league and esports title in scopes

[`GET /api/v1/trader/{address}/categories`](https://docs.0xinsider.com/api-reference/endpoint/get-trader-category-records) now returns a `scopes` array on each record: the wallet's record in each soccer league, each registered league of another sport, and each esports title it has a settled market in. A wallet's UEFA Champions League record is now one call: request `?category=soccer` and read the `ucl` entry.

Before this change, only the `Esports` record broke down further, through `games`. The `Soccer`, `Basketball`, `Tennis` and other records carried the bucket total alone, though Pick of the Day and the holder chips already printed league records.

* Each `scopes` entry is `{ scope_key, display_name, wins, decided, win_rate, status }`, busiest first, with the same 5-market floor and the same `measured` or `not_enough_data` status as its parent record.
* `display_name` is the name Pick of the Day and `category_win_rate_game` on the holder chips print, such as `UEFA Champions League`, `LaLiga`, `NBA`, or `LoL`. `scope_key` is the stable key, such as `ucl`, `la-liga`, `nba`, or `league-of-legends`.
* A scope's `decided` is a subset of its parent record's. The parent record stays the whole category.
* `scopes` is absent, never `null`, when the record has no scope with a settled market. A league is not a filter of its own: ask for its category and read `scopes`.
* On the `Esports` record, `scopes` lists the same titles as `games`, with the same counts. `games` stays unchanged.
* The MCP tool `get_trader_categories` returns `scopes` now. The SDK type `TraderCategoryScopeRecord` and the `scopes` field on `TraderCategoryRecord` ship in the next `@0xinsider/sdk` release.

**Backward compatible.** One optional field is added to each record; no field changes or goes away.

**What to change:** Nothing. To read a wallet's record in one league, read its entry in `scopes` by `scope_key`.

**November 7, 2026**

Large trades and dataset rows: clv now carries the fill's fee and the CLV after it

The `clv` object on every large trade now carries the fee the trade's fill paid and the closing line value after that fee. That covers [`GET /api/v1/large-trades`](https://docs.0xinsider.com/api-reference/endpoint/get-large-trades), [`GET /api/v1/large-trades/history`](https://docs.0xinsider.com/api-reference/endpoint/get-large-trades-history), [`GET /api/v1/large-trades/{id}`](https://docs.0xinsider.com/api-reference/endpoint/get-large-trade), the trades `/api/v1/events/feed/since` expands, and every row of a [whale dataset file](https://docs.0xinsider.com/api-reference/endpoint/download-whale-dataset.md).

Before this change, `clv_cents` was the only figure, gross of fees, and the API carried no fee for a fill. Polymarket charges only the taker of a fill, and the wallet behind a large trade is its taker. So on a market with fees, the fee lowers that trade's CLV by the same amount per share, whether it bought or sold.

* `taker_fee_cents`: the fee the fill paid, in cents per share, from the exchange's on-chain record of the fill. It includes any builder fee an app added and excludes the maker and taker rebates Polymarket pays later. It is set whatever `status` is, and `null` until that record is decoded (usually within a minute of the trade) and for trades before August 16, 2026.
* `clv_after_fees_cents`: `clv_cents` minus `taker_fee_cents`, set when both are set.
* `clv_cents` and [`GET /api/v1/trader/{address}/clv`](https://docs.0xinsider.com/api-reference/endpoint/get-trader-clv) stay gross of fees.
* The MCP tools `get_large_trades`, `get_large_trade` and `get_large_trades_history` describe both fields. The SDK's `clv` types carry them from the next `@0xinsider/sdk` release.
* A whale dataset submitted after this release is written with both fields. An identical submit builds a new file instead of reusing one written before.

**Backward compatible.** Two fields are added to the `clv` object; no field changes or goes away.

**What to change:** Nothing. To judge whether a trade beat the close after its cost, read `clv_after_fees_cents` instead of `clv_cents`.

**November 7, 2026**

ITF singles games now carry live state: phase in\_play on line moves, and final scores on ended games

ITF World Tennis Tour singles matches now have live game state. Polymarket publishes no score for them, so 0xinsider takes it from API-Tennis, the provider that already supplies tennis point detail, and states it only when one API-Tennis match names both players, the same tournament, and a date within a day of the kickoff.

* The [`market_price_moved`](https://docs.0xinsider.com/api-reference/webhooks/market-price-moved) webhook and the `MarketPriceMoved` stream frame carry `phase: in_play` and a `game_state` (status `inprogress`, period such as `S2`, each player's sets such as `6, 2`) for a move on an ITF singles match in play. Before this change they carried `phase: unknown` with `game_state_unavailable_reason: no_game_state` from kickoff on.
* [`GET /api/v1/stream`](https://docs.0xinsider.com/api-reference/endpoint/get-stream) sends `LiveScoreChanged` frames for these matches, with the same score and, while a game is in play, the `tennis_points` group. The `TennisPoints` object description now says so.
* A move after the match ends is no longer delivered, as for every other covered league: it is a settlement move.
* An ended ITF singles game on [`GET /api/v1/games`](https://docs.0xinsider.com/api-reference/endpoint/list-games) carries its final set scores, and `coverage.scores` reads `available`.
* A match API-Tennis does not list, or that more than one match could be, keeps `phase: unknown`. ITF doubles are not covered.

**Backward compatible.** No field is added or removed, and the values are the ones other tennis games already carry.

**What to change:** Nothing. A subscription with `phase: in_play` now receives ITF singles moves, and one that filtered out ITF moves because they read `unknown` can stop.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.