Skip to main content
These are the API changelog entries from March 25 to September 21, 2026, newest first. The newest changes are on the API changelog.
Pro includes 250,000 API requests a month; pay as you go covers the rest
Every authenticated /api/v1 request now counts against a monthly quota. Pro includes 250,000 requests per UTC calendar month, whatever your billing interval. Every authenticated response carries three new headers: X-Monthly-Quota-Limit, X-Monthly-Quota-Remaining, and X-Monthly-Quota-Reset (Unix seconds, the first instant of the next month).GET /api/v1/usage adds monthly_quota, with used, limit, remaining, reset_at, enforced_from, binding, and pay_as_you_go. Reading it spends nothing.From October 1, 2026, request 250,001 in a month answers 429 with error.code rate_limited and the new error.reason monthly_quota_exceeded, carrying Retry-After and error.retry_at set to the month’s reset. A refused request is not counted. The counter runs from September 21, and nothing is refused before October 1.Switch pay as you go on at 0xinsider.com/developers and requests over the quota keep answering. The excess bills at $0.20 per 1,000 requests on a monthly invoice to the card on the subscription, and binding reads false for that account.Backward compatible. A client that branches on error.code needs no change.What to change: If you retry on 429, read Retry-After first, because the monthly reset is days away rather than seconds. An email on September 21 announced 100,000 requests from November 1; the served contract is the one above.
OAuth registration caps at 10 per IP every 10 minutes, and metadata documents at 64 KiB
POST /oauth/register now has its own per-IP budget: 10 registrations per 600 seconds, checked after the shared OAuth cap of 60 a minute. The eleventh answers 429 with error temporarily_unavailable and Retry-After: 600. Every other OAuth temporarily_unavailable keeps Retry-After: 60.A client ID metadata document (an https client_id with a path) is now limited to 64 KiB of decoded body, checked while it downloads, and a document over the limit is refused and never cached. At most 8 uncached documents are fetched at once per backend process.Past that capacity, device authorization answers 429 with Retry-After: 30. The browser consent preview and decision keep their 200 ok: false shape with error temporarily_unavailable. A registered client or a cached document does not use that capacity.Backward compatible. Open registration is unchanged and by design: RFC 7591, public clients only, PKCE S256 required, and an exact redirect_uri match at authorize and at the token endpoint.What to change: Nothing. A client registers once and keeps its client_id, so no working client meets the cap.
List every graded holder of a market
Added GET /api/v1/market/{condition_id}/holders: the graded holder roster of any Polymarket market, the same list a Pick of the Day shows for its market. It returns every S, A, or B wallet with open shares on either outcome, taken from a complete provider holder scan, ordered by current_value_usd descending. A wallet holding both outcomes is listed once, on its net side.Each holder carries shares, Polymarket’s own current_value_usd for that outcome, grade, category_win_rate and category_win_record in the market’s category, badges, and last_traded_at.
  • Filters: outcome (yes, no, all), min_grade (S, A, or B, while C, D, and F answer 400), limit, and cursor (mh_ prefix).
  • The envelope carries market, scan (source, complete, and wallet_count, which counts every wallet the scan saw, graded or not), totals by side and grade, and an exact total.
  • The route takes the raw condition_id or the mkt_ id, and supports ETag and If-None-Match.
  • An incomplete or failed scan answers 503 with error.reason read_model_warming and Retry-After. You never get a short list instead.
The roster is rebuilt at most once a minute per market and shared by every caller, so a page is cheap and a cursor survives the rebuild. Until now GET /api/v1/positions ignored condition_id, and GET /api/v1/large-positions is a change feed with a $50,000 floor, so neither could answer this.Backward compatible. No existing route changes.What to change: Nothing.
Get a wallet's win record in every category, and in every esports game
Added GET /api/v1/trader/{address}/categories: one wallet’s record in every canonical category it has a settled market in, busiest first. These are the counts behind the Pick of the Day holder chips.
  • Each record carries category, wins, decided, win_rate, and status (measured or not_enough_data).
  • The envelope carries basis (settled_markets_all_sizes), min_decided_for_win_rate (5), and computed_at.
  • A record under that floor keeps its counts, with win_rate set to null.
  • A category with no settled market is left out, never returned as a 0% record.
  • A resolved trader with no record returns records: [] with 200. An unknown address returns 404.
  • ?category= filters to one bucket through the same category rollup the rest of the API uses: soccer, EPL, and champions league all reach Soccer, and football is American football.
The same day, the Esports record gained games[]: the wallet’s record per esports title (LoL, CS2, Dota 2, Valorant, and 7 more), each with game, series_slug, wins, decided, win_rate, and status under the same floor. The titles are named as the holder entries name them.games is absent on every other category and on an Esports record with no title history, and the Esports row keeps the whole bucket’s counts.Game records fill in from the first daily rebuild on September 22.This record differs from category_strengths on GET /api/v1/trader/{address}?expand=categories, which uses a different measured sample for ranking context. Both field descriptions identify the different bases; use this endpoint for the plain settled record.Backward compatible. No existing route changes.What to change: Nothing.
Esports holder entries name the game their win rate was measured in
Holder entries on GET /api/v1/pick-of-the-day and GET /api/v1/market/{condition_id}/holders add category_win_rate_game on an esports market. It names the game that category_win_rate and category_win_record were measured in: LoL, CS2, Dota 2, Valorant, Call of Duty, Honor of Kings, Mobile Legends: Bang Bang, Overwatch, Rainbow Six Siege, Rocket League, or StarCraft II.
  • It is present when the wallet has at least 5 resolved markets in that game. The rate and record are then that game’s rather than the whole Esports bucket’s.
  • It is absent when the rate is the wallet’s whole Esports record, on every non-esports pick, and beside every status that is not measured.
  • The payload-level category stays Esports either way.
Game records fill in from the first daily rebuild on September 22. Until then every esports entry carries the bucket record, as before.Backward compatible. The field is new and optional, so existing clients are unaffected.What to change: Label the rate with category_win_rate_game when it is present, and with category when it is not.
Pick of the Day archive publishes the price behind return_per_100 and both CLV operands
GET /api/v1/pick-of-the-day/archive rows add backed_price on exactly the rows that carry return_per_100, and clv_entry_price and clv_close_price on exactly the rows that carry clv_pct. On a win, 100 / backed_price equals return_per_100. A pending pick, and any pick whose backed side is withheld, still carries none of them, so no gate moved.clv_explanation on a measured row now names the entry before the close: Entry 0.635, close 0.705: CLV = (0.705 / 0.635 - 1) x 100 = +11.0%. The previous form printed the close first, and the arithmetic is unchanged.What backed_price means is now stated everywhere it appears: the Polymarket CLOB order book midpoint at release, written once at publication and never changed, not an executed fill. A buyer pays the ask rather than the midpoint, so an executed stake usually returns a little less than return_per_100.An API subscriber’s audit had concluded the return was computed off the close; it is not, and both operands now sit next to the result.What to change: If you parse clv_explanation, take the first number as the entry price.
The commitment ledger carries the payload of settled pre-sealing picks
GET /api/v1/pick-of-the-day/ledger now returns payload on a settled uncommitted entry: the market, side, and price of a pick published before sealing began, in the same 8 fields and the same key order as an opened payload. It is not hashed, and the entry stays pre_commitment: true.Before this change, an uncommitted entry carried no payload at all. 248 of the 250 picks published by September 21 predate sealing, so without their payloads a mirror could only take them on trust.payload is null while the pick is pending, and on a settled pick whose stored row is missing one of those columns.Backward compatible. Sealed and opened entries are unchanged.What to change: Nothing.
Positions accept condition_id, and a deep page returns as fast as the first
GET /api/v1/positions now honors condition_id, raw or as the mkt_ id, to scope the feed to one market. Combine it with min_size=0 for every reconciled position in that market. An unknown id returns [].Before this change, the parameter was parsed and then silently ignored. The get_positions MCP tool takes the same parameter.A deep cursor page now returns as fast as the first. Page 11 of category=Baseball at limit=100 took 3,707 ms and often timed out at 30 s; it now takes 866 ms. Rows and cursors are unchanged.
Remote MCP and @0xinsider/mcp 2.1.0 add get_sports_edge_observations
POST /api/v1/mcp and the stdio package @0xinsider/mcp 2.1.0 add get_sports_edge_observations, which calls GET /api/v1/sports-edge-observations. tools/list now returns 35 tools.
  • cohort is required: wider_holder, in_play, or emerging_pile.
  • category, limit (1 to 100, default 20), and cursor are optional.
  • The result carries the route’s snapshot_as_of, degraded, and funnel next to data, has_more, and next_cursor. degraded: true marks a page you should not read as healthy evidence.
Backward compatible. Existing tools are unchanged.What to change: Nothing.
Pick of the Day expands coverage to later games
GET /api/v1/pick-of-the-day can now include later games on the New York calendar date.Before this change, daily coverage could omit those games. A day can still have no published selection.Backward compatible. No field changed, and scheduled_picks and the archive keep their shape.What to change: Schedule your next request from the 404 pick_not_released response’s error.retry_at instead of assuming the day has ended at a fixed UTC hour.
New endpoint: every Pick of the Day's pre-game commitment, opened once it settles
Added GET /api/v1/pick-of-the-day/ledger: one entry per published (pick_date, pick_rank), ascending, in one of three states. The route needs an API key on a Pro account.
  • sealed is a live pick. It carries commitment_hash, commitment_algo (sha256(canonical_json(payload)||nonce)), sealed_at, kickoff, and permalink, and nothing that states a side or a price.
  • opened is a settled pick. It carries those fields plus commitment_nonce, the canonical payload the hash was taken over (market, side, backed_price as a decimal string, kickoff, date, rank), outcome, resolved_at, matchup, and category.
  • uncommitted is a pick with no commitment, either published before the scheme existed or reaching kickoff unsealed. It is named rather than left out, and carries pre_commitment: true.
To check an opened entry yourself, take the bytes of payload exactly as you received them, append commitment_nonce decoded from hex, and sha256 the result. It equals commitment_hash, because the payload is served byte for byte as it was hashed.A commitment is never written after kickoff, and never rewritten by an outcome correction.Backward compatible. The pick and archive routes are unchanged.What to change: Nothing.
Large positions report each outcome on its own and list every holding
On GET /api/v1/large-positions, total_size_usd and position_unrealized_pnl are now the provider’s value and unrealized P&L for the one outcome named by outcome_label and token_id. null means the provider supplied no value, not zero. event_total_value_usd is now null when the value of any other outcome in the same market is unknown.Before this change, a wallet holding both outcomes of a market had both outcomes’ combined value and P&L labelled as one outcome.Four fields are added, two of them required:
  • combined_value_usd and combined_unrealized_pnl are the wallet’s whole-market totals. They equal the per-outcome fields unless is_two_sided is true, and the feed ranks by the combined value.
  • is_two_sided is true when the wallet holds both outcomes.
  • holdings[] lists every outcome the wallet holds, each with outcome_index, outcome_label, token_id, share_count, avg_entry_price, current_price, value_usd, and unrealized_pnl. holdings[0] restates the top-level per-outcome fields.
What to change: If you read total_size_usd as the wallet’s exposure in a two-sided market, read combined_value_usd instead.
Volume fields are USD cash volume, never Polymarket leaderboard shares
Three /api/v1 fields had been carrying Polymarket’s share counts as dollars, and they now report USD cash volume. Polymarket’s v2 leaderboard reports volume in both-sides shares, and its own schema says the figure is never USD.On GET /api/v1/leaderboard/trending:
  • window_volume_usd is now both-sides cash volume in USD, taken from Polymarket’s user-volume read, and it leaves the required set. It is omitted when Polymarket served no volume for that wallet, never returned as zero.
  • window_volume_shares is added and carries the leaderboard’s share figure.
  • Polymarket tracks volume in whole UTC days, so the USD window is the whole-day span that covers the window you asked for.
  • The default min_volume_usd=5000 floor now compares dollars.
GET /api/v1/trader/{address} stats.total_volume, GET /api/v1/leaderboard volume, and the trader export’s provider_lifetime_volume are now full-history both-sides USD cash volume from a verified observation, and are omitted or null without one. A wallet’s figure is typically lower than before, because the previous number was a share count.What to change: If you treated window_volume_usd as always present, handle its absence. Field names and types are unchanged.
Grades weigh the last 90 days, and counterparty participants carry last_traded_at
The wallet grade that every /api/v1 response carries now weighs the wallet’s last 90 days beside its lifetime record. A wallet with no trade for 30 days holds no better than B, and no better than C past 120 days.Before this change, the only recency rule was a 365-day cliff, and the median B-graded wallet had last traded 56 days earlier.GET /api/v1/whale-trades/{id}/counterparties/executions and .../makers add last_traded_at on the taker and on each maker: the latest recorded trade time, across all markets, for the tracked trader whose grade is shown. It is omitted when that time is unavailable.Backward compatible. The grade field and its values are unchanged, and last_traded_at is new and optional.What to change: Nothing, but a client that filters on grade will see some wallets move down.
OAuth registration now stores and enforces grant_types
POST /oauth/register now stores grant_types and enforces it. Omitting the field registers ["authorization_code", "refresh_token"]. Using a grant the client did not register answers unauthorized_client (400) at consent, at POST /oauth/device/code, and at POST /oauth/token.Before this change, the registration response echoed a grant_types the server neither stored nor checked.
  • The list must name authorization_code or urn:ietf:params:oauth:grant-type:device_code.
  • It may not be empty, and it may hold nothing outside grant_types_supported. Each failure answers invalid_client_metadata.
  • The response reports the stored list. response_types is ["code"] only when the client holds authorization_code and registered a redirect URI, and [] otherwise.
  • A client without refresh_token gets a token response with no refresh_token field, and repeats its flow after the hour.
Two authorizations completed at once for the same user and client now leave one live grant. A race could previously leave two.Backward compatible. Clients registered before this change, and every client ID metadata document, carry every supported grant.What to change: Register the grants a new client will use, because a grant it did not register is refused.
Pick of the Day holders carry the counts behind the category win rate
Holder entries on GET /api/v1/pick-of-the-day add category_win_record, the two counts that category_win_rate is the ratio of. It is present only when category_win_rate_status is measured, and absent from payloads created before the field shipped.
  • wins: resolved markets in the pick’s category that the wallet closed with a profit.
  • decided: markets the wallet closed with a profit or a loss. A market that resolved at zero realized P&L counts in neither.
The same day, the basis widened. The rate and the record now count every resolved Polymarket market the wallet traded in the category, at any position size, and both are rebuilt daily. Until this change only positions of $20 or more counted, so a wallet’s rate can move.The floor of 5 resolved markets is unchanged.Backward compatible. The field is optional.What to change: Nothing.
@0xinsider/mcp 2.0.0 adds a product CLI with browser login
@0xinsider/mcp 2.0.0 turns the existing executable into a product CLI. It adds login (approved in a browser, through the device authorization flow in the entry below), whoami, refresh, and logout, plus read commands backed by the SDK. The CLI page lists every command.The read commands paginate JSON up to a bound, keep the full response envelope, and exit nonzero when the result is incomplete. 2.0.1, released the same day, refreshes an expired or rotated access token between pages, so a long paginated read no longer fails partway.
  • 2.x needs Node 22 or newer.
  • Stored sessions need macOS or Linux.
Backward compatible. Both binary names, the default stdio MCP server, serve, and init keep their behavior.What to change: Nothing. If you upgrade to 2.x, check that you are on Node 22 or newer.
Typed category evidence on the trader route and on Pick of the Day holders
GET /api/v1/trader/{address}?expand=categories adds category_records[] and category_skill_model beside the existing category_strengths. Each record holds forward-only evidence for one category, built from observed Polymarket taker fills.A record carries:
  • status: live, insufficient, stale, unknown, or degraded.
  • canonical_category and as_of.
  • independent_event_count and resolved_condition_count.
  • edge_mean, edge_sd, edge_se, edge_lower_95, and brier_event_avg.
  • model_version and taxonomy_version.
A row whose status is insufficient, stale, unknown, or degraded is still returned, and it withholds the scores. The absence of a category is not proof of skill. status is category eligibility, not a letter grade, and the wallet’s global grade is unchanged.category_skill_model reports how ready the model is overall, read from the same snapshot.Holder entries on GET /api/v1/pick-of-the-day also gained optional category context in this release. Internal selection evidence was later removed from customer pick responses; refer to the current endpoint for the supported holder fields.Backward compatible. The new fields are optional, and the trader route omits them unless you ask for expand=categories.What to change: Nothing.
Pick of the Day holders carry wallet badges
Holder entries on GET /api/v1/pick-of-the-day add 5 badge fields. They are stamped at serve time from the wallet’s current trader record, so they are never frozen with the pick.
  • wallet_age_days: the wallet’s age in days.
  • is_new_wallet: true when the wallet’s first trade was under 30 days ago.
  • markets_traded: how many distinct markets the wallet has traded.
  • is_bot: true at 10,000 or more distinct markets traded. That is a breadth rule, not proof of automation.
  • x_username: the X handle on the wallet’s Polymarket profile, without the @.
All 5 are present together, and only for a wallet that carries at least one badge. All 5 absent means the wallet has no badge, or the body was cached before the fields shipped.Backward compatible. The fields are optional.What to change: Nothing.
GET /api/v1/me returns the account and credential a request authenticated as
Added GET /api/v1/me. It returns the account the request authenticated as, in an account envelope: the account ID, the credential ID, the credential kind, and the approved scopes. A developer key reports null scopes.The response carries no email address and no token. The route uses the same paid-access check, read scope, usage accounting, and rate limits as every other read. The product CLI’s whoami command reads it.Backward compatible. It is a new endpoint.What to change: Nothing.
OAuth adds the device authorization grant (RFC 8628)
OAuth now supports the device authorization grant, so an agent with no browser and no redirect receiver can hold a scoped, revocable 1-hour token instead of a pasted long-lived API key. Discovery at /.well-known/oauth-authorization-server now lists device_authorization_endpoint as https://api.0xinsider.com/oauth/device/code, and adds urn:ietf:params:oauth:grant-type:device_code to grant_types_supported.POST /oauth/device/code takes the form fields client_id, optional scope (default read), and optional resource. It returns device_code, user_code, verification_uri, expires_in: 600, and interval: 5.
  • The person opens verification_uri, signs in, enters the user_code, reviews the account and the scopes, then approves or denies. Only an active Pro account can approve.
  • Your client polls POST /oauth/token with grant_type, client_id, and device_code.
  • authorization_pending, slow_down, access_denied, and expired_token all come back as 400. Each poll that arrives early adds 5 seconds to the interval, and a device_code that has already been used answers invalid_grant.
  • The exchange gives the same scoped grant, refresh, and per-client revocation as the authorization-code flow.
  • A device-only client can register at POST /oauth/register without redirect_uris, and asks for response_types: [].
Backward compatible. The authorization-code flow is unchanged.What to change: Nothing.
Remote MCP adds get_sports_edge_signals, plus a Markdown document per market
POST /api/v1/mcp and the stdio package add get_sports_edge_signals, which dispatches to GET /api/v1/sports-edge-signals. tools/list now returns 34 tools, and @0xinsider/mcp 1.3.1 serves the same catalog.The stdio get_trending_wallets now caps limit at 50 and keeps the gaps in the daily P&L curve, which matches V1 and the remote tool. Before this change it advertised a limit of 100 and filled those gaps with zeros.Added GET /api/v1/market/{condition_id}/context.md, a self-contained text/markdown evidence document for one market. It needs an API key, and it is rendered from the same typed data as the market snapshot, including that snapshot’s trust fields.
  • It carries the market’s identity, the outcome labels and tokens, cached quotes, liquidity, live sports, and per-source freshness or the reason a source is unavailable.
  • Text that came from a provider is encoded as indented JSON, so it stays inert.
  • The path accepts a raw condition_id or an mkt_ id. An unknown market returns 404.
  • There is no ETag. Use the snapshot route for conditional reads.
Backward compatible. Existing tools and routes are unchanged.What to change: Nothing.
An API key in a ?token= query parameter is refused on every route
An API key sent as a ?token= query parameter is now refused on every route, with 401 invalid_api_key and the new error.reason api_key_in_query. Before this change, POST /api/v1/mcp and its GET event stream still accepted a key that way.The reason: a live key does not expire, and a URL is written to access logs, proxies, shell and browser history, and client config dumps.
  • When a request carries both the header and ?token=, the header is used and the request succeeds.
  • initialize, ping, and tools/list still need no credential. A request that puts a key in the URL is refused before it reaches them.
  • No working integration used the query form. Between September 4 and September 17, 2026 it recorded no accepted request.
Breaking for a client that sends its key in the URL. The invalid_api_key code is unchanged, so a client that branches on error.code needs no change.What to change: Send the key as Authorization: Bearer <key>. A client that can only take a URL can run the @0xinsider/mcp stdio package with the key in its environment instead.
Refused requests report the per-IP rate-limit budget
A request that /api/v1 refuses before it checks the key’s own limit now carries the per-IP bucket’s RateLimit-* and X-RateLimit-* headers. Before this change those responses carried no budget, so a client retrying a rejected key could not see how close it was to the per-IP limit.The responses that now carry the headers:
  • 401 invalid_api_key.
  • 402 subscription_required.
  • 403 for a deleted account, and 403 insufficient_scope.
  • 423 for a locked account.
  • The authentication layer’s own 500 and 503 responses.
  • The MCP handshake, which needs no credential.
The numbers are always the per-IP bucket, the only budget a failed authentication spends. A caller refused because its IP is banned still gets no headers, and neither does a request whose client IP could not be resolved.The OpenAPI spec now documents these headers on every operation that sends them.Backward compatible. Existing clients are unaffected.What to change: Nothing.
Pick of the Day's CLV average is reported in percentage points
GET /api/v1/pick-of-the-day/archive adds hit_rate.clv_avg_pp and hit_rate.clv_avg_pp_display. Both hold the mean closing-line move in percentage points, (close - entry) * 100, for example "-0.1 pp".The existing clv_avg_pct is the mean of per-pick ratios, (close / entry - 1) * 100. A cheap entry moves that mean more than a favorite does. A pick at $0.05 that closes at $0.10 counts as +100%, while a pick at $0.90 that closes at $0.95 counts as +5.6%.The percentage-point average counts every pick on the same scale, and it is now the headline figure on 0xinsider.com.Both averages cover the same measured picks, and both are omitted until at least 5 of them exist. clv_avg_pct and clv_avg_display are unchanged.Backward compatible. The new fields are optional.What to change: Nothing. Read clv_avg_pp if you want the figure 0xinsider.com shows.
Large trades carry their share of the market's volume
GET /api/v1/whale-trades, GET /api/v1/whale-trades/history, and GET /api/v1/whale-trades/{id} add the optional field market_volume_share. It is the fill’s size_usd divided by its market’s volume at the moment the trade was recorded.size_usd on its own cannot tell a large bet in a thin market from a routine one in a deep market. A $10,000 fill is 0.00005 of a $200M market and 0.125 of an $80,000 one.
  • The value is not capped at 1, because a fill larger than the market’s stored trailing volume is the case that matters most.
  • It is absent when the market carried no volume figure, and on trades recorded before September 16, 2026.
  • It is never 0 as a stand-in for a missing value.
Backward compatible. The field is optional.What to change: Nothing.
A second venue removed: the API serves Polymarket only
0xinsider retired its second venue on September 8, 2026. Every V1 endpoint now serves Polymarket data only, and responses no longer contain that venue’s markets, trades, positions, or P&L.Breaking for a client that sends any platform value other than polymarket or all.What to change: Drop the platform parameter, or send polymarket. Earlier entries in this changelog describe the API as it shipped on their date.
Pick of the Day serves every ready pick while another pick's proof is pending
GET /api/v1/pick-of-the-day now answers 200 when at least one published pick has readable smart-money proof. picks carries the picks whose proof is readable, and the new proof_pending_picks array lists the rest with pick_rank, release_at, kickoff, and retry_at.Before this change, one published pick whose proof was not readable yet turned the whole day into 503 read_model_warming. On August 29 one pick’s holder snapshot lagged its publish, and the API withheld three fully proven picks for 14 minutes. The 503 now remains only when no published pick has readable proof.Backward compatible. The field is additive.What to change: Nothing. To fetch a pending pick, read again at its retry_at.
Remote MCP adds two Pick of the Day tools
POST /api/v1/mcp adds 2 tools, and both take no arguments. get_pick_of_the_day dispatches to GET /api/v1/pick-of-the-day, and get_pick_of_the_day_archive to GET /api/v1/pick-of-the-day/archive. tools/list now returns 33 tools.A tool error from any remote tool now carries the V1 error.code, error.reason, and error.retry_at when the handler set them. Before the day’s pick is released, get_pick_of_the_day therefore reads 404 Not Found (not_found/pick_not_released): ... Not available before <retry_at>., so an agent can schedule the next call instead of polling.The stdio package @0xinsider/mcp 1.2.0 serves the same 33 tools.Backward compatible. Existing tools and REST routes are unchanged.What to change: Nothing.
Pick of the Day's 404 retry_at accounts for newly available selections
GET /api/v1/pick-of-the-day now sets error.retry_at and Retry-After on a 404 pick_not_released to account for newly available selections as well as scheduled releases.Before this change, the value was the earliest scheduled slot’s release_at. A newly available pick could release earlier, so a client honoring the old value could miss its release.Until a pick releases, keep scheduling requests from the returned retry time.Backward compatible. The response shape is unchanged.What to change: Nothing. A client that schedules from retry_at keeps working.
Pick of the Day lists the day's picks that have not released yet
GET /api/v1/pick-of-the-day adds scheduled_picks: the day’s selected picks that have not released yet, in pick_rank order.Each slot carries exactly 3 fields: pick_rank, release_at, and kickoff. The shape holds no matchup, category, side, price, or holder data before release, so the schedule can be shown without revealing a pick early.The field is present only while at least one unreleased slot remains, so its absence means the day is fully released.Backward compatible. The field is optional.What to change: Nothing. To plan your next read, request once at the earliest release_at instead of polling.
@0xinsider/mcp 1.0.9 serves 29 tools locally, up from 6
@0xinsider/mcp 1.0.9 is on npm, the first release since 1.0.2 on April 14. The local stdio server goes from 6 tools to 29, the same set the hosted endpoint serves.
  • New tools: positions, position timeline (by wallet or by id), large positions, trending wallets, trader P&L, market explore, sharp-money flows, market snapshots, whale-trade history and single-trade lookup, batch trader and market-intel lookups, daily, weekly and monthly report snapshots, trader exports, webhook reads, and event replay.
  • get_smart_money_flows still works.
  • Whale-trade tools return recorded_signal_score.
  • init hides the API key as you type it, and prints a placeholder where a configuration to paste by hand would have shown the key.
The hosted POST /api/v1/mcp serves the same tools plus get_report and search_content, 31 in all.Backward compatible. The release adds tools.What to change: Nothing. Run npx -y @0xinsider/mcp init to set up or update. If an earlier init printed your key into output that was recorded or shared, rotate the key from Developers.
Large trades carry the signal score captured when the trade was recorded
GET /api/v1/whale-trades, GET /api/v1/whale-trades/history, and GET /api/v1/whale-trades/{id} now return the optional field recorded_signal_score next to the existing signal_score.
  • signal_score is the current score.
  • recorded_signal_score is the score captured with the trade record. Use it in alerts, audits, and historical comparisons.
  • The recorded score exists only for trades recorded after this release. Older trades return null.
Backward compatible. The field is optional.What to change: Nothing.
Pick of the Day reports the availability of stored trader context
GET /api/v1/pick-of-the-day added optional metadata describing whether stored trader context was available to read. Before this change, missing context did not distinguish an absent record from an unavailable one.The metadata described recorded context, rather than live trader statistics or entry permission. This release also documented the existing trader context and display category.Backward compatible at release. The metadata was optional. Internal selection diagnostics were later removed from customer responses; use the currently documented pick fields.What to change: Read the current endpoint reference for the customer response contract.
New observation-only endpoint for wider-holder and in-play sports markets
Added GET /api/v1/sports-edge-observations, an Insider-tier endpoint that reports evidence on sports opportunities that do not enter the funded Sports Edge slate. Every row carries observation_only: true, provider and holder freshness fields, and an explicit directional status.Pick one cohort, which is required:
  • wider_holder measures pre-game markets where the graded holders currently agree, even when no recent graded whale flow exists.
  • in_play measures only games the provider confirms are live, and only when the holder and directional evidence is fresh.
Use the returned provider state, freshness, and directional status to interpret each row. Every response carries a per-sport funnel that records emitted observations and the reasons other rows are unavailable.capacity_limited describes a bounded response. It does not by itself mean that the snapshot is degraded.Omit category to evaluate all 14 observation sports. That registry includes Table Tennis and Pickleball, classified from provider-confirmed tags and kept separate from Tennis. The table-tennis, table tennis, and pickleball filters all resolve through the canonical taxonomy, and neither sport is in the 12-sport funded projection.Reading the response:
  • Every 200 carries a required top-level degraded: boolean, the verdict for the whole snapshot. It covers both operational trouble and unknown completeness.
  • A healthy wider_holder request can reuse a snapshot for about 180 seconds. in_play never serves one older than about 30 seconds.
  • Conditional reads use a weak semantic ETag over the stable part of the page, which includes next_cursor and excludes the request-specific meta.
  • A malformed typed query value returns the standard JSON 400 bad_request envelope.
  • 503 with error.reason: "read_model_warming" means the route ran out of time before it had the data it needs. A later failure shows up instead as a degraded 200, with the funnel filled in where possible.
  • The API’s outer 30-second transport timeout still returns a 408 with an empty body, so check the status before you parse JSON.
These groups remain observations only and do not authorize entries or contribute to published picks.Backward compatible. It is a new endpoint, and it does not change or feed GET /api/v1/sports-edge-signals, Pick of the Day, or any order executor.What to change: Nothing.
Sports Edge covers Golf, Formula 1, NBA Summer League, CFL, and Boxing
GET /api/v1/sports-edge-signals and GET /api/v1/pick-of-the-day can now return eligible markets for Golf, Formula 1 and NASCAR, NBA Summer League, CFL, and Boxing.Polymarket sends only generic sports metadata for some of these markets. 0xinsider uses the provider tags to identify their sports categories. Racing and Boxing are now supported canonical buckets.Correction: an earlier version of this entry also named Table Tennis and Pickleball. The funded release did not admit either sport. Both are registered for observation-only evaluation through the endpoint added on July 14, and neither feeds funded signals or Pick of the Day.For the funded taxonomy changes in this release, an open market can adopt the corrected category on a provider refresh. Historical rows need a controlled refold, so they are not implied to have reclassified on their own.Backward compatible. No request or response shape changed, and no existing category was renamed.What to change: Nothing.
New endpoint: ranked pre-game sports markets with graded money on one side
Added GET /api/v1/sports-edge-signals, a ranked list of upcoming pre-game sports markets (moneyline and props) where graded S, A, and B smart money sits on one side. The server computes the whole list in one call.Each signal returns:
  • The side the graded money is on, and its CLOB token_id.
  • The grade distribution on that side: s_count, a_count, and b_count.
  • The dollar concentration: sharp_pct and backed_sharp_usd.
  • The market’s game_start_time, which is kickoff.
  • A calculated backing score, conviction_score.
Follow the returned order when presenting the markets, and read the measured backing alongside the scores.The route needs a bearer token and an Insider tier account. Its 200 carries an ETag, and it is cursor-paginated with the cursor fixed to one snapshot.Backward compatible. It is a new endpoint.What to change: Nothing.
Pick of the Day returns 404 outside today's pick, and adds game_started
GET /api/v1/pick-of-the-day changes in 2 ways.The new boolean field game_started is true once the backed game’s kickoff has passed, which means the snapshotted pre-game price is no longer actionable. It is absent on a legacy pick with no stored kickoff, and you should treat that as not started.The is_locked description was corrected at the same time. It is a pre-release embargo flag, so it is effectively always false on a served pick. Use game_started for kickoff.The endpoint now returns 404 when today has no published pick yet, instead of serving the most recent prior day’s pick, which was often already settled. The response shape is unchanged and 404 was already a documented response. This conforms the endpoint to its “today’s Pick of the Day” contract, so an automated consumer never acts on a finished game.Prior picks stay available through the archive endpoint.Backward compatible. game_started is optional, and the behavior change is non-breaking.What to change: Nothing.
Sandbox test-mode API keys removed; one live key class remains
Sandbox test-mode API keys (oxi_sk_test_...) are removed. The API now has a single key class, oxi_sk_live_..., sent as Authorization: Bearer oxi_sk_live_.... It requires an active Insider subscription and always returns live data.There is no fixture or deterministic-data mode any more. Every request reads live data.Breaking for a client still authenticating with an oxi_sk_test_... key. Those requests fail.What to change: Generate a live key from your account’s API keys page, and switch your clients over before this release.
Pick of the Day: a fixed 19:00 Europe/Berlin release and any sport
GET /api/v1/pick-of-the-day and its archive change behavior in 2 ways. No field was added, removed, or retyped, but 2 values now mean something different.release_at is now a fixed daily instant: 19:00 Europe/Berlin, every day, which is 19:00 CEST in summer and 19:00 CET in winter. It used to be a kickoff-relative time that moved with whichever game was picked. next_drop_deadline counts down to the same fixed instant.When no pick is available at the release slot, publication can happen later. On a thin day the actual publication can trail release_at by minutes to hours.The pick can now cover more sports and prop markets, expanding beyond the previously narrower set of categories.
  • Sports: soccer, tennis, baseball, basketball, hockey, MMA, cricket, golf, and esports.
  • Prop markets: totals, spreads, and exact scores.
  • Expect category values beyond Soccer in the archive from now on.
Backward compatible. Existing clients are unaffected.What to change: Nothing.
Whale trades expose the traded outcome and its CLOB token id
Each whale trade on GET /api/v1/whale-trades, /whale-trades/history, and /whale-trades/{id} now returns the traded outcome and that outcome’s Polymarket CLOB token_id. The outcome is the provider’s own label for the side the trade was on, for example Yes, No, or a team. The existing side, BUY or SELL, is unchanged.Before this change only BUY or SELL was exposed, so you could not tell which outcome a whale trade was on, or get that outcome’s on-chain token id.outcome is null only for a multi-outcome market, or a market that has not synced, with no stored label. A trade from the venue retired on September 8, 2026 carries its provider label in outcome, and only its token_id is null, because that venue had no CLOB token; see that entry.Backward compatible. Both fields are optional and nullable.What to change: Nothing.
Every V1 YES/NO/outcome now carries its Polymarket CLOB token id
Reads that expose an outcome, a side, or a net-flow direction now also return that outcome’s Polymarket CLOB token_id. You can go straight from a YES, NO, or outcome label to its on-chain ERC1155 asset id without a second lookup. Use it for order placement, per-token price history, and on-chain reconciliation.The value is a decimal string for a synced Polymarket market, and null when it is unavailable, for example on a market from the venue retired on September 8, 2026, or a market that has not synced yet. The key is always present, and it is never in a required set; see that entry.The endpoints that gained it:Backward compatible. The field is optional, so existing clients are unaffected.What to change: Nothing.
Pick of the Day archive adds a cumulative track-record series
GET /api/v1/pick-of-the-day/archive now returns hit_rate.series, the cumulative track record of the Pick of the Day. It is ready to chart without recomputing it on the client.Each element is one resolved pick, a win or a loss, in ascending pick_date order, and carries:
  • date: the pick’s publish date, as YYYY-MM-DD.
  • net_profit_usd: the running cumulative profit of a $100 stake on every resolved, priced pick through that date.
  • hit_rate_pct: the running hit rate (wins divided by decided) through that date, to one decimal.
The final point equals the top-level hit_rate.net_profit_usd and hit_rate.pct, so a chart of the series always ends exactly on the headline numbers. The array is empty until at least one pick has resolved.Backward compatible. Every existing hit_rate field is unchanged.What to change: Nothing.
Trader quant_metrics is now a documented, fixed field set
GET /api/v1/trader/{address} and POST /api/v1/traders/batch now return a curated, documented quant_metrics object when you pass ?expand=quant_metrics. Before this change it was an unconstrained pass-through of internal metrics.When expanded, the object always carries these 9 fields. Each is a number or null, and null means insufficient trade history, never 0.
  • copy_score and smart_score: scores from 0 to 100. smart_score summarizes measured performance, and copy_score summarizes how practical the wallet’s past strategy may be to follow.
  • sharpe_30d and sharpe_7d: risk-adjusted return over the trailing 30 and 7 days.
  • profit_factor: gross profit divided by gross loss, capped at 1,000.
  • edge_consistency: how stable the trader’s edge is over time, from 0 to 1.
  • sharpe_percentile, pf_percentile, and consistency_percentile: cross-sectional ranks against all traders, from 0 to 100.
Undocumented internal and experimental metrics that used to come through the pass-through are no longer returned.Every field is defined on the trader endpoint reference, and the Quant metrics guide explains what each score and metric means, its range, and how to read null.Breaking for a client that read a metric outside this list.What to change: Nothing, if you use only the 9 documented fields.
New endpoint: ranked market discovery by smart-money net flow
Added GET /api/v1/markets/smart-money-flows, a ranked discovery feed that answers where smart money is flowing before you know a condition_id.It ranks markets by absolute net flow from S, A, and B grade traders over a 1h, 4h, 24h, or 7d window, with platform, category, min_grade, and direction filters. Pagination uses an opaque cursor anchored to the first page’s as_of, so new whale trades do not reorder later pages.To look at a single market in detail, use GET /api/v1/market/{condition_id}/intel.Backward compatible. It is a new endpoint.What to change: Nothing.
Remote MCP read parity, usage and caching headers, and round-trippable IDs
Remote MCP read parity. POST /api/v1/mcp now exposes 21 tools through tools/list, covering the public V1 reads: batch reads, history, event replay, webhook reads, reports, market snapshots, and trader exports. Webhook mutations stay REST-only, and the endpoint is tools-only, with no resources and no prompts.Your own usage. Added GET /api/v1/usage, an authenticated read of your current per-minute limiter window and your UTC-day usage. It does not spend primary quota, but it has its own guard of 100 reads a minute.Cheaper polling, safe retries. Deterministic reads now support ETag and If-None-Match conditional GETs, so an unchanged read returns 304 with no body. Webhook create, update, delete, and rotate accept an Idempotency-Key.Discovery. Added GET /api/v1, an API discovery document, and GET /api/v1/openapi.json, a redirect to the canonical OpenAPI spec. Operation IDs are now codegen-friendly, with examples, code samples, and response-header metadata.Round-trippable IDs. Market Intel and Market Snapshot now accept either the mkt_... market ID or the raw condition_id. New GET /api/v1/whale-trades/{id} and GET /api/v1/insider-radar/{id} return a single wt_... or rf_... record. A prefix that is not a market prefix returns 400 bad_request.Backward compatible. Everything here is new or optional.What to change: Nothing.
Remote MCP exposes position and market discovery tools
POST /api/v1/mcp now exposes 9 tools through tools/list. The new ones are get_positions, get_position_timeline, and explore_markets.Each dispatches to the existing public REST handler, so an MCP client gets the same auth, pagination, and payload contracts as a direct REST caller.
New builder endpoints: batch, history, events, webhooks, reports, snapshots
Added task-shaped builder endpoints, so an integration needs fewer calls:Batch endpoints expose item-weighted cost headers. The new snapshot, history, and report payloads document their source, freshness, completeness, and reconciliation, so you can tell a provider-backed value from a cached, partial, stale, or unavailable one.
TypeScript client source added
Added TypeScript client guidance for the repo-owned client source in web/src/lib/api-client.The client source is drift-tested against OpenAPI. It handles bearer auth, path params, repeated query params, JSON bodies, and V1 error envelopes. It is not a published npm package yet.
Market Intel rejects prefixed market IDs
GET /api/v1/market/{condition_id}/intel now returns a clearer 400 bad_request when a caller passes a prefixed market.id value such as mkt_... instead of the raw condition_id.Call GET /api/v1/markets/search first, then pass the condition_id it returns into the Market Intel path.
Trader lookup accepts usernames and plain expand params
GET /api/v1/trader/{address} now accepts a known trader username in the path, as well as an Ethereum wallet address.You can request heavy trader fields with repeated expand query params. The older expand[] form still works for existing clients.
Leaderboard strategy fields can be null
GET /api/v1/leaderboard now returns traders that have no classified strategy.Treat strategy_type as nullable for a ranked trader with no strategy classification.
Explore Markets excludes untitled rows
GET /api/v1/markets/explore no longer returns untitled markets.The response set is now titled, user-facing markets only.
Trader realized P&L corrected
GET /api/v1/trader/{address} now returns the canonical realized P&L, read from the realized_pnl source-of-truth field.pnl.realized no longer derives realized P&L from an inconsistent approximation.
Explore Markets endpoint launched
Added GET /api/v1/markets/explore for cursor-paginated market discovery.It supports category, status, platform, and text filters. It also injects the primary market, so a grouped event includes its main market when one exists.
Initial V1 launch
Launched the first public developer API:The initial contract shipped with Bearer auth, expand[], cursor pagination, prefixed IDs, and rate-limit headers.
Numeric precision standardized
Money values and scores are truncated to 2 decimal places across V1 responses.Prices and rates are truncated to 4 decimal places.