> ## 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: October 29 to November 1, 2026

> 31 API, SDK, CLI, MCP, OAuth, and feed changes from October 29 to November 1, 2026, each with its effect on clients. Newer changes are on the API changelog.

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

**November 1, 2026**

Market holders totals split each side's USD by grade

[`GET /api/v1/market/{condition_id}/holders`](https://docs.0xinsider.com/api-reference/endpoint/get-market-holders) now returns `totals.yes_grade_usd` and `totals.no_grade_usd`, each with `s`, `a` and `b`. They split `yes_usd` and `no_usd` by wallet grade, over the whole roster before your `outcome` and `min_grade` filters. The `get_market_holders` MCP tool returns the same fields, and `@0xinsider/sdk` types them as `MarketHoldersSideGradeUsd`.

Before this change, `totals` had the wallet count per grade on each side but no USD per grade. To learn where the S-grade money sat, you had to page the whole roster and add up `current_value_usd` yourself.

Each value is rounded to the cent after summing, so `s + a + b` can differ from the side total by up to 2 cents.

**Backward compatible.** The fields are additive.

**What to change:** Nothing is required. Read `totals.yes_grade_usd.s` instead of summing pages. A strict schema that rejects unknown keys must accept the new fields.

**November 1, 2026**

CLI 3.7.0 adds api list, game and holder commands, and suggests a mistyped operation id

The [CLI](https://docs.0xinsider.com/integrations/cli.md) in `@0xinsider/mcp` 3.7.0 adds `api list`, which prints every operation `api call` runs with its method, path, and path and query parameter names. It works offline without a credential, as JSON or with `--table`.

* New commands: `games list`, `games get <event_slug>`, `market holders <condition_id>`, `market candles <condition_id>`, `markets sharp-flows`, `traders context <wallet>`, `traders categories <wallet>`, `traders grade-at <wallet>`, `large-positions list`, `edge-hunter`, and `pick archive`. Each one prints the API response unchanged.
* An unknown `api call` operation id now names the closest registered ids and points to `api list`. `0xinsider api call getGames` suggests `listGames` and `getGame`.
* `api call` now runs the batch lookups `batchGetTraders` and `batchGetMarketFlow`, with the request body in `--body '<json>'`. It also runs `listWebhooks`, `getWebhook`, and `getTraderExportSnapshot`, the same reads the MCP tools serve. Neither webhook read returns a signing secret.
* Shell completions now complete each command's second word and the operation id after `api call`.

Before this change, those reads worked only through `api call <operation-id>`, nothing listed the ids, and a near-miss printed `This is not a registered non-administrative JSON read operation.` with no suggestion.

**Backward compatible.** Every existing command, alias, option, and exit code is unchanged. `api call` still refuses every write, the other webhook routes, streams, MCP, other exports, and Markdown routes.

**What to change:** Nothing. Update to `@0xinsider/mcp` 3.7.0 once it is published, then run `0xinsider api list --table` to see what `api call` accepts. Scripts that match the old refusal message should match exit code `2` instead.

**November 1, 2026**

Team logos carry logo\_mark\_measured, and provider-hosted logos are now measured on white

Team logos now carry `logo_mark_measured`, a boolean beside `logo_mark_dark` and `logo_mark_light`. It is `true` when both verdicts are measurements of this exact logo URL, so their `false` means the mark is legible on that background. It is `false` when the logo has not been measured yet, so a `false` verdict beside it can still mean "not measured".

* [`GET /api/v1/pick-of-the-day`](https://docs.0xinsider.com/api-reference/endpoint/get-pick-of-the-day): each team in `sports_context` (`yes_team`, `no_team` and `event_subject_team`). Like `logo_mark_light` there, it is always sent.
* [`GET /api/v1/edge-hunter`](https://docs.0xinsider.com/api-reference/endpoint/get-edge-hunter): each entry in a row's `participants`. Like `logo_mark_light` there, it is sent only when `true`.

Every vendored crest is measured. Provider-hosted logos are now measured on a white background as well as a dark one, so `logo_mark_light` can be `true` for them too. A provider logo becomes measured within a few hourly cycles of first appearing.

**Backward compatible.** The field is additive. `logo_mark_light` can now be `true` for a provider-hosted logo.

**What to change:** Nothing is required. If you draw an outline behind a logo whose verdicts are unknown, skip it when `logo_mark_measured` is `true`. A strict schema that rejects unknown keys must accept the new field.

**November 1, 2026**

Team logos carry logo\_mark\_light, for crests that vanish on a white background

Team logos now carry `logo_mark_light`, a boolean beside `logo_mark_dark`. It is `true` when measurement shows the crest's artwork disappears on a white background, so it needs a dark plate there. It is `false` when no current measurement marks the logo light, which includes every provider-hosted logo.

* [`GET /api/v1/pick-of-the-day`](https://docs.0xinsider.com/api-reference/endpoint/get-pick-of-the-day): each team in `sports_context` (`yes_team`, `no_team` and `event_subject_team`). Like `logo_mark_dark` there, it is always sent.
* [`GET /api/v1/edge-hunter`](https://docs.0xinsider.com/api-reference/endpoint/get-edge-hunter): each entry in a row's `participants`. Like `logo_mark_dark` there, it is sent only when `true`.

The verdict is tied to the exact bytes of a vendored crest. On November 1, 2026, 458 of 19,955 vendored crests measured light.

**Backward compatible.** The field is additive.

**What to change:** Nothing is required. To keep a light crest legible on a light background, draw it on a dark plate when `logo_mark_light` is `true`. A strict schema that rejects unknown keys must accept the new field.

**November 1, 2026**

Edge Hunter keeps a quiet far-from-kickoff market's holder page for up to 30 minutes

On [`GET /api/v1/edge-hunter`](https://docs.0xinsider.com/api-reference/endpoint/get-edge-hunter) with `view=observations`, a pregame market more than an hour before kickoff still has its holders read again once its page is 10 minutes old, but only if the market has traded since that page. While it has had no trade, the snapshot keeps reusing the page until it is 30 minutes old. Such a row carries the page's original time in `holder_snapshot_at` and `provider_read_source` `cached`, as any reuse does.

Before this change the page was read again every 10 minutes whether or not anything had traded. Markets within an hour of kickoff, `in_play` rows, and the markets only `admission=most_held_48h` takes are read as before.

**Backward compatible.** No field is added, removed, or renamed. `holder_snapshot_at` on a quiet market more than an hour before kickoff can now be up to 30 minutes old instead of 10.

**What to change:** If you check `holder_snapshot_at` against a 10- or 15-minute bound on these rows, allow 30 minutes for a market more than an hour before kickoff.

**November 1, 2026**

A holder's category average price is counted once a day, with its win record

`category_avg_price` on each holder of [`GET /api/v1/market/{condition_id}/holders`](https://docs.0xinsider.com/api-reference/endpoint/get-market-holders), and on each `display_holders` entry of [`GET /api/v1/pick-of-the-day`](https://docs.0xinsider.com/api-reference/endpoint/get-pick-of-the-day), now comes from the same daily count as `category_win_rate` and `category_win_record` beside it. Before this change the price moved as markets settled while the record moved once a day, so a holder could carry a price that counted markets its record did not count yet.

The definition does not change: the wallet's average entry price over its markets in the category that resolved to a winning side, of \$20 or more where it bought one side at a known price.

**Backward compatible.** No field is added, removed, or renamed. The price can now trail a settlement by up to a day, as the record already did.

**What to change:** Nothing.

**October 31, 2026**

Category win rates count every settled market, including ones a wallet sold out of early

A wallet's category win rate and record now count every market where its position is settled: the wallet sold out of it, or the market resolved. Wins and losses are still the sign of realized P\&L. Before this change a market counted only once it resolved to a winning side, so a sale out of a market still trading waited for that market, and a void or unresolvable market never counted. The wallet's profile already counted every settled market, so the two now agree.

* [`GET /api/v1/trader/{address}/categories`](https://docs.0xinsider.com/api-reference/endpoint/get-trader-category-records): every category and game record.
* [`GET /api/v1/market/{condition_id}/holders`](https://docs.0xinsider.com/api-reference/endpoint/get-market-holders): `category_win_rate` and `category_win_record` on each holder, and the 5-market floor behind `category_win_rate_status`.
* [`GET /api/v1/pick-of-the-day`](https://docs.0xinsider.com/api-reference/endpoint/get-pick-of-the-day): the same fields on `display_holders` entries, and `qualifying_expert.win_rate` and `n_resolved`.
* The descriptions of `markets_resolved` and the `resolved_*` win-rate counts on [`GET /api/v1/trader/{address}/context`](https://docs.0xinsider.com/api-reference/endpoint/get-trader-context) and [`GET /api/v1/trader/{address}/pnl`](https://docs.0xinsider.com/api-reference/endpoint/get-trader-pnl) now say "settled markets". Their values do not change.

**Backward compatible.** No field is added, removed, or renamed. Counts and rates move for wallets that sold out of markets still trading or held positions on void markets. Calibration fields, such as edge and average entry price, still count only markets with a winning side.

**What to change:** Nothing. If you label these counts "resolved markets", "settled markets" is the accurate name.

**October 31, 2026**

Browser CORS stops listing Mcp-Session-Id and X-Mcp-Error-Code as readable headers

Public `/api/v1` responses no longer name `Mcp-Session-Id` or `X-Mcp-Error-Code` in `Access-Control-Expose-Headers`. No response has sent either header since [remote MCP](https://docs.0xinsider.com/api-reference/endpoint/remote-mcp.md) dropped sessions earlier the same day, so the list now names only headers the API sends.

* Before this change, every public API response listed both names among the headers browser JavaScript may read.
* Every other readable header is unchanged. The [Browser CORS](https://docs.0xinsider.com/api-reference/introduction.md#browser-cors) table lists them.
* `Mcp-Session-Id` stays allowed on the request, so an older client's preflight still passes.

**Backward compatible.** A browser read of either header returned `null` before this change and still does.

**What to change:** Nothing.

**October 31, 2026**

OpenAPI response schemas now match what each endpoint sends

The response schemas in the [OpenAPI document](https://0xinsider.com/api/v1/openapi.json) are now checked against the server's own response types, so each one lists what its endpoint sends. Before this change, they were kept by hand, and a few said less or more than the server sends. No response changes.

* Grades list their values, `S`, `A`, `B`, `C`, `D` and `F`, where the schema said only "string": `grade` on [`GET /api/v1/leaderboard`](https://docs.0xinsider.com/api-reference/endpoint/get-leaderboard), `trader.grade` on [`GET /api/v1/large-trades`](https://docs.0xinsider.com/api-reference/endpoint/get-large-trades) and [`GET /api/v1/events/feed/since`](https://docs.0xinsider.com/api-reference/endpoint/get-event-replay-since), `grade` in `top_positions` on [`GET /api/v1/market/{condition_id}/flow`](https://docs.0xinsider.com/api-reference/endpoint/get-market-flow), and `trader_grade` and `grade_distribution[].grade` on [`GET /api/v1/reports`](https://docs.0xinsider.com/api-reference/endpoint/get-reports) and its snapshots.
* Objects that were untyped now name their keys: report `categories[]` (`category`, `large_trade_count`, `whale_trade_count`, `category_volume`) and `grade_distribution[]` (`grade`, `count`), and `direct_streaming`, `async_job` and `rate_limit` in `large_export_policy` on [`GET /api/v1/trader/{address}/export`](https://docs.0xinsider.com/api-reference/endpoint/get-trader-export-snapshot).
* Three properties the server never sent leave the schema: `total` on [`GET /api/v1/combos/fills`](https://docs.0xinsider.com/api-reference/endpoint/list-combo-fills), `proof_pending_picks` on the no-entitled-picks body of [`GET /api/v1/pick-of-the-day`](https://docs.0xinsider.com/api-reference/endpoint/get-pick-of-the-day), and `meta.cache_age_s` on [`GET /api/v1/events/feed/since`](https://docs.0xinsider.com/api-reference/endpoint/get-event-replay-since).
* Where an endpoint sends fewer values than its type allows, such as a webhook's own `status`, the schema node carries `"x-narrowed": true`. Validators and code generators ignore `x-` extensions.

**Breaking** for a client generated from the document that references one of the three removed properties: the generated type no longer has it. No response byte changes, and those properties were never present.

**What to change:** Nothing at runtime. If generated code references `total`, `proof_pending_picks`, or `meta.cache_age_s` on those bodies, delete the reference.

**October 31, 2026**

Sandbox MCP answers the transport contract the live endpoint now serves

The [sandbox](https://docs.0xinsider.com/sandbox.md) `POST /api/v1/mcp` at `https://0xinsider.com/sandbox` now gives the same status codes, headers, and JSON-RPC errors as the live endpoint after the October 31 move to stateless MCP. Before this change, the sandbox still answered the old transport contract.

* The sandbox sends no `Mcp-Session-Id` and no `X-Mcp-Error-Code`, and it ignores an `Mcp-Session-Id` you send. Browser preflight now allows `Mcp-Method` and `Mcp-Name`.
* Revision `2026-07-28` is served. A request at that revision needs the `Mcp-Method` header, and `Mcp-Name` on `tools/call`. Without them it returns `-32020`. Its `_meta` must carry `protocolVersion` and `clientCapabilities`, or it returns `-32602`.
* An unsupported `MCP-Protocol-Version` on a POST returns `400` with the REST error body, `error.code` `bad_request`. `GET`, `PUT`, `PATCH`, and `DELETE` return `405` with `Allow: POST` and `error.reason` `method_not_allowed`.
* A message without an `id` returns `202`. A response message returns `400` with `-32600`.
* These all return `400` with `-32600`: a `null`, fractional, or boolean `id`; `params` that is not an object; a JSON array body; a repeated `method` or `id` key; or `jsonrpc` other than `2.0`.
* A `tools/call` whose `arguments` is not an object returns `-32602`. An unknown tool returns `-32601`.
* A method the server does not implement, such as `resources/list`, returns `-32601` with the bare method name as its message. An unknown method keeps the `Method not found:` prefix.

The sandbox still ignores credentials, so it never returns the live endpoint's `401`. Parse-error messages for malformed JSON use the sandbox's own wording.

**Breaking** for sandbox clients that read `Mcp-Session-Id`, `X-Mcp-Error-Code`, or the old JSON-RPC body on `GET` or on an unsupported `MCP-Protocol-Version`.

**What to change:** Nothing if your client works against the live endpoint. Otherwise make the same changes as the live endpoint's October 31 entry below.

**October 31, 2026**

Remote MCP drops sessions, serves revision 2026-07-28, and reshapes a few transport errors

[`POST /api/v1/mcp`](https://docs.0xinsider.com/api-reference/endpoint/remote-mcp) now runs on the official Rust MCP SDK in stateless mode. Tools, their input schemas, their results, credentials, and quota counting do not change. A few transport details do.

* Sessions: the server no longer sends `Mcp-Session-Id`, and it ignores one you send. Before this change, `initialize` minted one and every response echoed it, though nothing depended on it.
* Revisions: `2026-07-28` is now served, alongside `2025-11-25`, `2025-06-18`, `2025-03-26`, and `2024-11-05`. A `2026-07-28` request needs the `Mcp-Method` header and gets `-32020` without it. Browser preflight now allows `Mcp-Method` and `Mcp-Name`.
* An unsupported `MCP-Protocol-Version` on a POST returns `400` in the REST error envelope, `error.code` `bad_request`, instead of JSON-RPC `-32600`. A GET ignores the header. An `initialize` whose `MCP-Protocol-Version` header names a different revision from its `params.protocolVersion` returns `400` with `-32600`; send the header only after `initialize`, or send the same value.
* [`GET /api/v1/mcp`](https://docs.0xinsider.com/api-reference/endpoint/remote-mcp-stream) with a credential still returns `405` with `Allow: POST`. The body is now the REST error envelope with `error.reason` `method_not_allowed`, not JSON-RPC `-32004`. `DELETE` and `PUT` return the same `405`.
* No response carries `X-Mcp-Error-Code`. Read the code from the JSON-RPC body.
* A message without an `id` returns `202` whatever its method. Before this change, an unknown method without an `id` returned `400`.
* A method the server does not implement, such as `resources/list` or `prompts/list`, still returns `-32601`, but the message is now the bare method name rather than `Method not found: <method>`. An unknown method keeps the prefix. Match on the code, not the text.
* A notification method sent with an `id` returns `200` with `-32601`, not `400` with `-32600`. A response message returns `400` with `-32600` in the body, not an empty `400`.
* A `tools/call` whose `arguments` is not an object returns `-32602`, not a tool result with `isError`. An `arguments` of `null` is read as the empty object.
* A request `id` of `null`, an object `id`, `params` that is not an object, a JSON array body, or a body with two `method` keys returns `400` with `-32600`. Before this change, a `null` `id` was echoed and non-object `params` on `initialize` were ignored.

**Breaking** for clients that read `X-Mcp-Error-Code`, the `-32004` body on GET, or the `-32600` body for an unsupported `MCP-Protocol-Version`.

**What to change:** Read transport errors from the JSON-RPC body, and stop sending `Mcp-Session-Id`. Conforming MCP clients need no change.

**October 31, 2026**

Esports markets Polymarket files under no series now count toward their game

Polymarket files some esports events under no series. A market in one of those events used to count toward the `Esports` record alone. It now counts toward the game its event slug names, such as `cs2-` or `valorant-`.

* `games` on [`GET /api/v1/trader/{address}/categories`](https://docs.0xinsider.com/api-reference/endpoint/get-trader-category-records) can show higher `wins` and `decided`, and a game the wallet did not have before. The counts update at the next daily rebuild.
* `category_win_rate_game` on [`GET /api/v1/market/{condition_id}/holders`](https://docs.0xinsider.com/api-reference/endpoint/get-market-holders) can now name a game on such a market.
* A market with a series keeps that series. A market whose slug names no game, or names a league that another sport shares, still counts toward `Esports` alone.
* The `Esports` record itself does not change.

**Backward compatible.** No field, type, or `series_slug` value changes.

**What to change:** Nothing. A client that stores per-game counts should expect them to rise once.

**October 31, 2026**

Market snapshots date a settlement by Polymarket's own settlement time

`market.resolved_at` on [`GET /api/v1/market/{condition_id}/snapshot`](https://docs.0xinsider.com/api-reference/endpoint/get-market-snapshot) now holds the time Polymarket settled the market, for a market that settles from this release on. Before this change, it held the time 0xinsider recorded the settlement. That ran about 2 minutes after Polymarket's on a typical market, and days or weeks after it on a market 0xinsider settled late.

* The value is Polymarket's close time for the market, or its resolution time for an archived market that Polymarket's market listing no longer returns. It is never later than the time 0xinsider recorded the settlement.
* A market settled without a Polymarket time keeps the time 0xinsider recorded, as before.
* A market settled before this release keeps the value it has.
* `market.status` and `market.closed` do not change.
* The MCP tool `get_market_snapshot` returns the same value.

**Backward compatible.** The field and its type do not change; for a new settlement the time it reports is earlier.

**What to change:** Nothing. A client that read `resolved_at` as the time 0xinsider learned of a settlement should read it as the time the market settled.

**October 31, 2026**

Live tennis doubles carry API-Tennis set scores in tennis\_points.set\_games

`sports.live_score.tennis_points` on [`GET /api/v1/market/{condition_id}/snapshot`](https://docs.0xinsider.com/api-reference/endpoint/get-market-snapshot), and `live_score.tennis_points` in `LiveScoreChanged` frames on [`GET /api/v1/stream`](https://docs.0xinsider.com/api-reference/endpoint/get-stream), now reach live ATP and WTA doubles matches and can carry a new optional field, `set_games`. It lists API-Tennis's games in every set played so far, first set first, each as `[first, second]` in `live_score.scores` order, such as `[[6, 1], [4, 6], [9, 5]]`. A deciding match tie-break is listed as games, so `[9, 5]` is a match tie-break at 9-5.

Polymarket publishes no score for doubles: `live_score.scores` reads one set at 0-0 for the whole match. Before this change, `tennis_points` was only sent when its current-set games matched that score, so a doubles match lost the group after its first game. A doubles group is now sent when both pairs' names, the tour, the date, and API-Tennis's live flag match one match.

* `set_games` is present only while `live_score` carries no published score. Show it as the set score, with `points` and `serving_side` for the current game.
* With `set_games` present, `set_number` and `games` still describe the current set, but they are not checked against `live_score`.
* A singles match is unchanged and never carries `set_games`. If Polymarket ever publishes a nonzero game for a doubles match, the group is dropped and singles checking applies again.
* `live_score.scores` is unchanged. It is still Polymarket's value, 0-0.

**Backward compatible.** `set_games` is new and optional, and no existing field changes type or meaning for singles.

**What to change:** Nothing. To show a live doubles score, read `tennis_points.set_games` when it is present, and keep enforcing `expires_at`.

**October 31, 2026**

Live tennis doubles scores on the stream name both pairs

`LiveScoreChanged` frames from [`GET /api/v1/stream`](https://docs.0xinsider.com/api-reference/endpoint/get-stream) for a tennis doubles match now name the two pairs in `live_score.scores[]`. Before, a frame from Polymarket's live feed could name two players of the same pair as the two sides, such as `Facundo Bagnis` against `Emilio Gomez` for `Bagnis/Gomez` against `Drzewiecki/Matuszewski`, and one side could carry a singles `ranking`.

Each side's `team` is now the pair's code from the event slug, in slug order, such as `BAGNGOM` and `DRZEMAT` for `atp-doubles-bagngom-drzemat-2026-10-30`, and `full_name` is the pair's name, such as `Bagnis/Gomez`. A doubles side carries no singles `ranking`. This applies while Polymarket's doubles score reads `0-0`, which it does for every doubles match today.

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

**What to change:** Nothing. A client that matched a doubles side to a player by `team` should match the pair by `full_name`.

**October 31, 2026**

Holder entries name the league on every sport, not only soccer

Holder entries on [`GET /api/v1/pick-of-the-day`](https://docs.0xinsider.com/api-reference/endpoint/get-pick-of-the-day) and [`GET /api/v1/market/{condition_id}/holders`](https://docs.0xinsider.com/api-reference/endpoint/get-market-holders) now carry `category_win_rate_game` on the other sports too. It names the league that `category_win_rate` and `category_win_record` were measured in: `NBA`, `WNBA`, `NFL`, `College Football`, `NHL`, `MLB`, `UFC`, `ATP`, `WTA`, and the other registered leagues.

Before this change, the field was sent only on esports and soccer markets, and every NBA rate was the wallet's whole Basketball record.

* It is present when the wallet has at least 5 resolved markets in that league. The rate and record are then the league's rather than the whole `category` bucket's, so the counts in `category_win_record` can be smaller than before.
* It is absent when the rate is the wallet's whole sport record, on a competition without its own league record (NBA Summer League, the World Baseball Classic, and others), and beside every status that is not `measured`.
* `category_avg_price` stays the sport's price, and the payload-level `category` stays the sport, such as `Basketball`.
* The MCP tools `get_market_holders` and `get_pick_of_the_day` return the same value.

League records fill in from the first daily rebuild after this release. Until then these entries carry the sport's record, as before.

**Backward compatible.** The field already exists and stays optional; it now appears on more entries.

**What to change:** Nothing, if you already label the rate with `category_win_rate_game` when it is present and with `category` when it is not. A client that reads an NBA rate as a Basketball rate should check the field first.

**October 31, 2026**

Team names read in full on picks, and live scores carry a display name

`full_name` and `matchup_title` in `sports_context` on [`GET /api/v1/pick-of-the-day`](https://docs.0xinsider.com/api-reference/endpoint/get-pick-of-the-day) now hold the name to show for a team, the same one the website shows. College football joins the school and the mascot, so `UCF` reads `UCF Knights`. NBA and NHL teams read their club name, so `Thunder` reads `Oklahoma City Thunder`. Every other league keeps the name it already had.

`label` does not change. It stays the spelling on the market outcome, such as `UCF`, so use `label` to match a team to an outcome. `pick_outcome_label`, `matchup`, and every field of a published pick stay frozen, and a settled pick keeps the names it settled with.

`live_score.scores[]` under `sports` on [`GET /api/v1/market/{condition_id}/snapshot`](https://docs.0xinsider.com/api-reference/endpoint/get-market-snapshot) can now carry an optional `display_name`. It is the same name to show, and it is omitted when it equals the entry's `full_name`, or its `team` when that entry has no `full_name`. `team` and `full_name` keep their values on a score entry.

**Backward compatible** for clients that show these fields. The types are unchanged and `display_name` is new and optional. A client that compares `full_name` or `matchup_title` to a stored value sees a new value on games not yet settled.

**What to change:** To match a team to an outcome, read `label`, not `full_name`. To show a live score, read `display_name`, then `full_name`, then `team`.

**October 30, 2026**

MarketPriceChanged stream frames carry each outcome's display price

`MarketPriceChanged` frames on [`GET /api/v1/stream`](https://docs.0xinsider.com/api-reference/endpoint/get-stream) can now carry two optional fields, `display_price` (the Yes outcome) and `no_display_price` (the No outcome). Each is the price Polymarket displays for that outcome token, as `{ "price": "0.565", "basis": "midpoint", "ts": "1793386388438" }`:

* `price` is a decimal string between 0 and 1.
* `basis` is `midpoint` when the spread is at most \$0.10, otherwise `last_trade`.
* `ts` is the provider time of that price, in epoch milliseconds, as a string.

Each outcome is priced from its own token. `no_display_price` is never computed as 1 minus the Yes price. A field is omitted when no priced frame for that token landed in the frame's window, so an omitted field says nothing about the token; keep the price you hold. A frame can now carry only these fields, with `last_price`, `no_last_price`, `best_bid` and `best_ask` all `null`.

**Backward compatible.** Both fields are new and optional, and the existing fields keep their meaning.

**What to change:** Nothing. To show the same price as Polymarket, read `display_price` and `no_display_price`, and keep a newer `ts` over an older one for each outcome. A client that skipped frames whose four existing price fields were all `null` should accept them now.

**October 30, 2026**

Soccer holder entries name the league their win rate was measured in

Holder entries on [`GET /api/v1/pick-of-the-day`](https://docs.0xinsider.com/api-reference/endpoint/get-pick-of-the-day) and [`GET /api/v1/market/{condition_id}/holders`](https://docs.0xinsider.com/api-reference/endpoint/get-market-holders) now carry `category_win_rate_game` on a soccer market too. It names the league that `category_win_rate` and `category_win_record` were measured in, by the name Pick of the Day prints: `LaLiga`, `Premier League`, `Serie A`, `Bundesliga`, `Ligue 1`, `UEFA Champions League`, `UEFA Europa League`, `MLS`, and the other leagues a pick names.

Before this change, the field was sent only on esports markets, and every soccer rate was the wallet's whole Soccer record.

* It is present when the wallet has at least 5 resolved markets in that league. The rate and record are then the league's rather than the whole `Soccer` bucket's.
* It is absent when the rate is the wallet's whole Soccer record, on a competition without its own league record (UEFA Nations League, international friendlies, Eredivisie, and others), and beside every status that is not `measured`.
* `category_avg_price` stays the `Soccer` price, and the payload-level `category` stays `Soccer`.
* The MCP tools `get_market_holders` and `get_pick_of_the_day` return the same value.

League records fill in from the first daily rebuild after this release. Until then every soccer entry carries the Soccer record, as before.

**Backward compatible.** The field already exists and stays optional; it now appears on more entries.

**What to change:** Nothing, if you already label the rate with `category_win_rate_game` when it is present and with `category` when it is not. A client that assumed the field means an esports game should read it as a game or a league.

**October 30, 2026**

A tennis match frozen in play for 3 hours reads as suspended, not live

[`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 report a tennis match as suspended when Polymarket still lists it in play but nothing has changed for 3 hours. That means no live score feed for the match, and no change to its period or score. Such a match reads `status.state` `suspended`, `status.match_status` `suspended`, and `status.live` `false`.

Before this change, these endpoints reported such a match as `live`. Polymarket does not always mark a suspended tennis match `SUS`. On October 30, 2026, two doubles matches suspended the evening before still read `live` at 0-0, more than 19 hours after their listed start.

* `status.period` and the score stay as Polymarket sent them.
* The match reads `live` again when the live score feed sends it a state, or when its period or score changes.
* Suspended games whose period reads `SUS` are unchanged.
* The `status.state` and `status.live` descriptions now name this case.

**Backward compatible.** No field is added, removed, or retyped, and `suspended` was already a documented value of `status.state` and `status.match_status`.

**What to change:** Nothing, if your client already handles `suspended`. If you list live games with `status=live`, also request `status=suspended` to see tennis matches that stopped during play.

**October 30, 2026**

Account identity reports integration keys as integration\_key, and their scope errors say how to fix them

[`GET /api/v1/me`](https://docs.0xinsider.com/api-reference/endpoint/get-account-identity) now returns `credential_kind: "integration_key"` for a named integration key created on [Developers](https://0xinsider.com/developers). Before this change, an integration key reported `oauth_grant`, so `0xinsider whoami` showed it as an OAuth grant, and so did any client that read the field.

A request outside an integration key's scopes still returns `403 insufficient_scope` with the same `param` and `WWW-Authenticate` challenge. Its `error.message` now says that an integration key's scopes are fixed and tells you to create a key with the missing scope on Developers. Before, it said to re-authorize, which an integration key cannot do. The remote MCP tools and the `GET /api/v1/stream` closing frame use the same wording.

* `api_key` and `oauth_grant` keep their meaning, and OAuth grants keep the re-authorize message.
* `scopes` is unchanged: `null` for a developer key, the scope array for an OAuth grant or integration key.

**Additive.** `credential_kind` gains one value; no field is added, removed, or retyped. The TypeScript SDK's `credential_kind` type adds `"integration_key"`.

**What to change:** If your client switches on `credential_kind`, handle `integration_key`. Do not match on `error.message`; branch on `error.code` and `param`.

**October 30, 2026**

Explore markets keeps open\_interest current for 30 minutes after a call

[`GET /api/v1/markets/explore`](https://docs.0xinsider.com/api-reference/endpoint/explore-markets) and the MCP `explore_markets` tool now keep each listed market's `open_interest` current for 30 minutes after a call. The first call after 30 minutes with no call can return `open_interest: null` for markets whose value has not been read yet. The values fill in over the next few minutes, and later calls in the window carry them.

Before this change, the server read open interest for these markets every few minutes all day, whether or not anyone called the route.

* `oi_change_pct` is `null` whenever `open_interest` is, as before.
* Every other field is unchanged.

**Backward compatible.** `open_interest` was already nullable, and no field is added, removed, or retyped.

**What to change:** Nothing, if you already handle `null`. If your first call needs open interest, call the route again a few minutes later.

**October 29, 2026**

A grade needs 5 sports or esports markets

A wallet now needs at least 5 sports or esports markets to hold a `grade`. Below that, `grade` is `null`.

Before this change, the floor counted markets in every category, so a wallet with 1 to 4 sports or esports markets could still hold a letter.

* `grade` changes wherever it is returned, including large trades, positions and their `min_grade` filters.
* Letters are shares of the graded wallets, so a smaller group moves other letters. On a copy of production from October 28, 27,534 of 164,786 graded wallets lost their letter, all of them `C`, `D` or `F`. Another 7,760 changed letter, including 35 `S` and 180 `A` wallets that went down one.
* In production, wallets graded `S` or `A` went from 1,475 on October 29 to 1,289 on October 30.

**Backward compatible.** Field names, types, allowed values and presence rules are unchanged; what the values measure changes.

**What to change:** If your logic reads a list of `S` or `A` wallets, refresh it. If you store grades, expect a one-time change for some wallets.

**October 29, 2026**

Pick of the Day keeps a team's full name in sports\_context after kickoff

[`GET /api/v1/pick-of-the-day`](https://docs.0xinsider.com/api-reference/endpoint/get-pick-of-the-day) now names each team in a pick's `sports_context` by its full Polymarket name before, during, and after the game. This applies to a pick on a market whose outcomes are "Yes" and "No", such as "Will US Lecce win?", where `yes_team` and `no_team` are the game's two teams.

Before this change, once the game had a score, `label` and `full_name` on `yes_team` and `no_team`, and `matchup_title`, switched to the team's short name. "US Lecce" read "Lecce", and "ACF Fiorentina" read "Fiorentina". `event_subject_team` could also go missing for such a team.

* A team with no short name is unchanged.
* A pick on a market whose outcomes are the team names keeps those names, as before.

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

**What to change:** Nothing. If you matched a pick's team by the short name it showed during a game, match by `provider_id` instead.

**October 29, 2026**

A wallet that trades only combos says it has no sports record, not a pending one

[`GET /api/v1/trader/{address}`](https://docs.0xinsider.com/api-reference/endpoint/get-trader) and [`POST /api/v1/traders/batch`](https://docs.0xinsider.com/api-reference/endpoint/batch-get-traders) now say why a wallet that trades only combos has no sports and esports record. The record counts single markets, so a wallet whose sports and esports trading is all in combos never gets one. A wallet counts as one when its last full trader sync found no single sports or esports market, 0xinsider stores none of its markets, and 0xinsider holds at least one of its combos.

Before this change, these wallets read as if their record were still being computed, and they never got one.

* The `sports_record` group in `data_quality` is `partial`, with owner `traders.last_synced`, `as_of` set to that sync, and a reason that names combos. Before, it was `unavailable`.
* `pnl.in_scope`, `stats.markets_traded`, and `stats.win_rate` stay absent. The API serves no other P\&L figure in their place.
* With `expand=trust`, `trust.markets_traded` and `trust.win_rate` stay unavailable, and their reason names combos.
* [`GET /api/v1/trader/{address}/pnl`](https://docs.0xinsider.com/api-reference/endpoint/get-trader-pnl) returns `series_status: "ready"` with empty sections, `realized: null`, and `freshness_at` set to that sync. Before, it returned `pending`.

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

**What to change:** Nothing. If you retried such a wallet until `pnl.in_scope` appeared, stop when `sports_record` is `partial` with owner `traders.last_synced`: the record will not appear while the wallet trades only combos.

**October 29, 2026**

A wallet that traded only other categories reads 0 sports markets, not pending

[`GET /api/v1/trader/{address}`](https://docs.0xinsider.com/api-reference/endpoint/get-trader) and [`POST /api/v1/traders/batch`](https://docs.0xinsider.com/api-reference/endpoint/batch-get-traders) now return `stats.markets_traded` as `0` for a wallet that traded only markets outside sports and esports. [`GET /api/v1/trader/{address}/pnl`](https://docs.0xinsider.com/api-reference/endpoint/get-trader-pnl) and [`GET /api/v1/trader/{address}/export`](https://docs.0xinsider.com/api-reference/endpoint/get-trader-export-snapshot) return `ready` with no day for it. Two earlier entries of October 29 covered a wallet that traded no market at all; a wallet now also counts when its last completed sync read its Polymarket positions and found no sports or esports market.

Before this change, these wallets read as if their record were still being computed, and they never got one. On October 29, 2026, 17 wallets matched.

* Each field reads as those entries describe: `stats.markets_traded` is `0`, `pnl.in_scope` and `stats.win_rate` are absent, and the `sports_record` group is `partial` with owner `traders.last_synced`.
* `/pnl` returns `series_status: "ready"` with empty sections and `realized: null`, and the export returns `pnl_series_status: "ready"` with no day.
* With `expand=trust`, `trust.markets_traded.source.field` is now `last_synced` for every wallet that traded no sports or esports market, including the wallets those entries cover. Before, it was `markets_traded`, Polymarket's count, which is above 0 for these 17.
* A wallet with Polymarket combo trades is not counted here.
* A wallet whose sports and esports record is not computed yet is unchanged: it still omits `stats.markets_traded` and reads `pending`.

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

**What to change:** Nothing, unless you read `trust.markets_traded.source.field`: for a wallet with no record it is now `last_synced`.

**October 29, 2026**

A P\&L correction no longer moves a trader's synced\_at or sends trader\_synced

[`GET /api/v1/trader/{address}`](https://docs.0xinsider.com/api-reference/endpoint/get-trader) and [`POST /api/v1/traders/batch`](https://docs.0xinsider.com/api-reference/endpoint/batch-get-traders) now keep `synced_at` at the wallet's last full sync when 0xinsider corrects the wallet's settled P\&L. A correction re-reads the wallet's closed positions from Polymarket and recomputes its statistics. It does not read the wallet's trades or open positions.

Before this change, a correction set `synced_at` to the time it finished. A wallet could then read as synced minutes ago while its trades were last read days before.

* `synced_at` and `last_active`, and the `as_of` of the `sync` and `volume` groups in `data_quality`, keep the time of the last full sync.
* The `sports_record` group in `data_quality` still moves when a correction changes the wallet's sports and esports record.
* The [`trader_synced`](https://docs.0xinsider.com/api-reference/webhooks/trader-synced) webhook and its deprecated spelling [`whale_trader_synced`](https://docs.0xinsider.com/api-reference/webhooks/whale-trader-synced) no longer fire for a correction. They fire when a full sync finishes.

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

**What to change:** Nothing. To notice a corrected P\&L, compare the `sports_record` group's `as_of`, not `synced_at`.

**October 29, 2026**

Trader export reads ready, not pending, for a wallet that traded no market

[`GET /api/v1/trader/{address}/export`](https://docs.0xinsider.com/api-reference/endpoint/get-trader-export-snapshot) now returns `source_range.pnl_series_status` as `ready` for a wallet that traded no market. Such a wallet never gets a sports and esports daily series. The export file's `export_metadata.pnl_series_status` follows, with `pnl_freshness_at` set to the sync that found no market.

Before this change, these wallets read `pending` for good, as if the series were still being computed. On October 29, 2026, 1,274 wallets matched. A wallet counts when [`GET /api/v1/trader/{address}`](https://docs.0xinsider.com/api-reference/endpoint/get-trader) returns `stats.markets_traded` as `0` for it, the same rule that endpoint and [`GET /api/v1/trader/{address}/pnl`](https://docs.0xinsider.com/api-reference/endpoint/get-trader-pnl) use since October 29, 2026.

* The dates stay `null`, `counts.pnl_days` stays `0`, and the file's `pnl` section stays empty.
* With nothing synced, `completeness.status` is `empty`, as before.
* A wallet with markets whose series is not computed yet is unchanged: it still reads `pending`.
* A file built before this change keeps its old value until it expires, one day after it was built.

**Backward compatible.** No field is added, removed, or retyped, and `pnl_series_status` keeps its two values.

**What to change:** Nothing. If you retried a wallet until `pnl_series_status` became `ready`, the retry now ends with an empty series.

**October 29, 2026**

Pick of the Day chooses a pick only inside the 4 hours before kickoff

[`GET /api/v1/pick-of-the-day`](https://docs.0xinsider.com/api-reference/endpoint/get-pick-of-the-day) now chooses a pick only when its game starts within the next 4 hours, on the wallets and the price at that moment. A game further off is not chosen early, so `scheduled_picks` no longer lists a slot for it hours ahead.

Before this change, a pick could be chosen as early as the start of the product day and wait in `scheduled_picks` until 4 hours before kickoff. A slot chosen that way before this change stays in `scheduled_picks` and publishes at its `release_at`, after the usual publication checks.

* `scheduled_picks` still lists a slot whose release is held: one chosen before this change, one chosen before 3:00 AM Eastern, or one whose kickoff the provider moves later.
* Publication timing is unchanged: no earlier than 4 hours and no later than 30 minutes before kickoff.

**Backward compatible.** No field is added, removed, or retyped. `404 pick_not_released` can last until the day's first game is within 4 hours of kickoff.

**What to change:** Nothing, if you schedule reads from `retry_at` and `release_at`. Do not treat an empty `scheduled_picks` as a day with no picks to come.

**October 29, 2026**

A synced wallet that traded no market reads 0 markets, not a pending record

[`GET /api/v1/trader/{address}`](https://docs.0xinsider.com/api-reference/endpoint/get-trader) and [`POST /api/v1/traders/batch`](https://docs.0xinsider.com/api-reference/endpoint/batch-get-traders) now return `stats.markets_traded` as `0` for a wallet that traded no market. Such a wallet never gets a sports and esports record. A wallet counts as one when Polymarket counted no market for it at its last completed sync, 0xinsider stores none of its markets, and no Polymarket sports or esports board lists it.

Before this change, these wallets read as if their record were still being computed, and they never got one. On October 29, 2026, 1,275 wallets matched.

* `stats.markets_traded` is `0`. `pnl.in_scope` and `stats.win_rate` stay absent, because the wallet has no record.
* The `sports_record` group in `data_quality` is `partial`, with owner `traders.last_synced` and `as_of` set to the sync that found no market. Before, it was `unavailable`.
* With `expand=trust`, `trust.markets_traded` is `computed` from `traders.markets_traded` and dated by that sync.
* [`GET /api/v1/trader/{address}/pnl`](https://docs.0xinsider.com/api-reference/endpoint/get-trader-pnl) returns `series_status: "ready"` with empty sections, `realized: null`, and `freshness_at` set to that sync. Before, it returned `pending` for these wallets.
* A wallet with markets whose record is not computed yet is unchanged: it still omits `stats.markets_traded` and reads `pending`.

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

**What to change:** Nothing. If you retried a wallet until `stats.markets_traded` appeared, the retry now ends with `0`.

**October 29, 2026**

Trader export provider\_lifetime\_volume is always null and deprecated

[`GET /api/v1/trader/{address}/export`](https://docs.0xinsider.com/api-reference/endpoint/get-trader-export-snapshot) now returns `reconciliation.provider_lifetime_volume` as `null` for every wallet. The export file's `summary.volume_reconciliation.provider_lifetime_volume` is `null` too, and the reference marks the field deprecated.

Before this change, the field held Polymarket's volume for the wallet across every market, often observed weeks earlier. Since October 25, 2026, the volume beside it counts sports and esports markets only, so the two could not be compared.

* The key stays, with the same type, so existing clients keep parsing the object.
* `exported_activity_volume`, `exported_market_cost_basis`, and every other field are unchanged.
* A file built before this change keeps its old value until it expires, one day after it was built.
* The TypeScript SDK marks `ExportVolumeReconciliation.provider_lifetime_volume` `@deprecated`, and the `get_trader_export_snapshot` MCP output schema marks it deprecated.

**Breaking** for clients that read `provider_lifetime_volume` as a number. Its name and type are unchanged.

**What to change:** Stop reading `provider_lifetime_volume`. Read `exported_activity_volume` for the wallet's sports and esports volume.


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