Max-only Edge Hunter replaces 4 retired sports signals and observations endpoints
GET /api/v1/edge-hunter now returns the ranked board shown on Edge Hunter. It requires Max; a Pro credential returns 403 forbidden with error.reason: max_plan_required.Before this change, sports signals and observations were separate paginated APIs. These 4 paths now return 410 Gone with error.code and error.reason set to endpoint_retired:/api/v1/sports-edge-signals/api/v1/sports-edge-observations/api/v1/sports/pre-game-sides/api/v1/sports/pre-game-side-observations
object: "edge_hunter" and returns data.rows, with up to 30 full rows across all sports. horizon_hours defaults to 48 and is clamped to 1 through 48; the grade floor is fixed at B, and data.total counts matches before the row cap.The response includes market and participant identities, backed-side analytics, and current quotes when available. It has no cursor, category filter, observation cohort, or observation funnel.The client packages and official agent instructions use the new API:- SDK
0.20.0addsgetEdgeHunterandedgeHunter.get(), and removes the old sports signals and observations helper methods. - The MCP and CLI runtime
3.0.0addsget_edge_hunter; the 4 old MCP tools are no longer advertised. - The official agent plugin’s
polymarket-sharp-moneyskill, version1.0.2, now calls Edge Hunter and requires Max for board requests.
/api/v1/edge-hunter with a Max credential, or get_edge_hunter over MCP. Read data.rows and the new board fields, remove unsupported filters and cursors, and update to SDK 0.20.0 or MCP 3.0.0.Refresh installed official skills with npx skills@latest update 0xinsider-api-access polymarket-sharp-money. Stop retrying 410 endpoint_retired.Market snapshot starts a live book on the first read of a market off the sports boards
GET /api/v1/market/{condition_id}/snapshot always streams the order book for two groups of markets. The first is every market on the 0xinsider sports boards. The second is every sports market with $10,000 or more of liquidity, from 15 minutes before kickoff until it resolves. A market in either group whose book has no bid or no ask, such as a finished game waiting to resolve, still reports cache_miss.Any other open market, such as a non-sports market, a futures market, or a sports market long before kickoff, now gets its book when you read it. Before this change the cache also held every other open market with $10,000 or more of liquidity. The first snapshot of such a market starts a live subscription and reports no book:outcomes[].top_of_book.statusisunavailable, withreason: "cache_miss".outcomes[].current_priceisnull, because it is the midpoint of that book.freshness.top_of_bookand thetrustexpansion report the book unavailable.
cache_miss.Backward compatible. Each of these values was already possible.What to change: Nothing, if you already handle a missing book. If you get cache_miss for an open market, read the snapshot again after a few seconds.Tennis headshot images are served at most 128 px wide
The tennis
headshot URL that GET /api/v1/pick-of-the-day returns on a competitor now serves an image at most 128 px wide. The image keeps its aspect ratio and is never enlarged. Before this change the URL served the stored portrait, up to 1024 px wide.- The URL and its
vquery parameter are unchanged. - The response is
image/png. A portrait that was stored as JPEG used to be served asimage/jpegand is now PNG too. An image already 128 px or smaller is served as stored, with its stored type. - The
ETagis now the image hash followed by-w128, for example"<sha256>-w128". A cached copy that holds the old tag (the bare hash) gets a200with the new image, not a304.
Report large trades name Yes and No outcomes instead of null
GET /api/v1/reports/daily, /weekly and /monthly now send "Yes" or "No" in outcome on report.top_large_trades[] and report.top_whale_trades[] when the traded market is a Yes/No market. Other markets keep their own labels, such as a team name.Before this change outcome was null for many Yes/No markets created since August 2026, because 0xinsider stored their labels as empty. Report snapshots built before this change keep the value they were built with.Backward compatible. outcome was already nullable, and it can still be null when no label is known.What to change: Nothing. If you replaced a null outcome with your own “Yes”/“No”, you can stop.Trader context returns a retryable 503 instead of a 500 when its breakdown read times out
GET /api/v1/trader/{address}/context and GET /api/v1/trader/{address}/context.md now return 503 with error.reason: "read_model_warming" and Retry-After: 2 when the position_summary read runs out of time. That read covers every position the wallet has held, so a wallet with a long history can exceed its time limit when its data is not in the database cache. The API tries the read a second time before it answers, and the second try usually completes because the first one already loaded part of the data.Before this change, the same case returned 500 with error.code: "internal_error". On October 10, a client that retried two seconds after that 500 got a 200 both times it happened.error.codeisrate_limit_unavailable, the code every503on this API carries. Branch onerror.reason.- The context is never returned without its
position_summaryfor this reason, and a partial breakdown is never returned. - Any other database failure still returns
500.
5xx response.What to change: Retry a 503 with error.reason: "read_model_warming" after the Retry-After seconds. Do not count it toward a rate-limit backoff.Position timeline's trader_not_tracked message names the warm tier only
GET /api/v1/trader/{address}/position-timeline and GET /api/v1/traders/{trader}/position-timeline now end the trader_not_tracked message with only warm-tier traders are tracked. The warm tier is the set of wallets 0xinsider syncs most often.Before this change the message ended with only HOT and WARM traders are tracked. 0xinsider has moved every former hot-tier wallet to the warm tier and removed the hot tier, so the set of wallets with a timeline did not change.error.codestaysnot_foundanderror.reasonstaystrader_not_tracked.- The message still names the trader’s own tier, for example
(sync_tier=cold). - The OpenAPI descriptions, the SDK’s
TraderNotTrackedErrordocumentation, and theget_position_timelineMCP tool description say the same.
error.reason instead.Large trades name the real clock behind the volume data_quality group
GET /api/v1/large-trades and GET /api/v1/large-trades/history now send owner: "market_live_stats.volume_request_observed_at" on the volume entry of data_quality.field_groups. The deprecated /api/v1/whale-trades aliases send the same value.Before this change the entry named markets.volume_request_observed_at. That column is a retired copy. The as_of and status on the entry always came from market_live_stats, the table that holds each market’s volume and the time it was read, so only the name changes.Backward compatible for clients that read as_of and status. The ETag of a large-trades page changes once, because data_quality is part of it.What to change: Nothing, unless you match the owner string. If you do, match market_live_stats.volume_request_observed_at.Pre-game sides and observations now need Max; Pro keys get 403 max_plan_required
GET /api/v1/sports/pre-game-sides and GET /api/v1/sports/pre-game-side-observations now need a credential on the Max plan. A Pro key gets 403 with code: forbidden, reason: max_plan_required, and a message that links the pricing page.Before this change, any Pro key could read both routes. They carry the same data as Edge Hunter, which the site has shown only to Max since October 8, 2026.- The deprecated aliases
GET /api/v1/sports-edge-signalsandGET /api/v1/sports-edge-observationsanswer the same way. - The MCP tools
get_pre_game_sides,get_pre_game_side_observations,get_sports_edge_signals, andget_sports_edge_observationsreturn the same error to a Pro key. max_plan_requiredis a new value oferror.reason.error.codestaysforbidden.- The next
@0xinsider/sdkrelease addsMaxPlanRequiredError, aForbiddenErrorsubclass with anupgradeUrl. Earlier versions throwForbiddenErrorwithreasonset tomax_plan_required. - Every other Pro endpoint keeps working with the same key.
reason: max_plan_required as a permanent refusal for that route, not as a deleted account.Trader context.md prints one market win rate instead of two
GET /api/v1/trader/{address}/context.md now prints one Market win rate line, in the Performance section. When the position breakdown is present it is position_summary.resolved_win_rate, followed by its won and decided counts, for example - Market win rate: 3.4% (39 won of 1155 decided resolved markets). Otherwise it is trader.stats.win_rate.Before this change the document printed two lines for the same rule: Market win rate from trader.stats.win_rate, and Resolved-market win rate in the position breakdown. The stored figure could lag settled markets recorded after the wallet’s last full sync, so the two disagreed. On October 7, 34 of 400 ranked wallets differed by more than 0.5 points.- The
Resolved-market win rateline is removed from the position breakdown. freshness_noteonGET /api/v1/trader/{address}/context, and the same sentence in the Markdown document, now say that resolved counts and win rate can include settled markets recorded after the last full sync.- The JSON body is otherwise unchanged.
trader.stats.win_rateandposition_summary.resolved_win_rateare both still sent.
Resolved-market win rate line out of the Markdown document.What to change: Read the win rate from the Market win rate line, or from position_summary.resolved_win_rate in the JSON route.Leaderboard realized_pnl is left out, never another P&L basis, without a native figure
GET /api/v1/leaderboard realized_pnl is now always the wallet’s own realized P&L, including credited maker and taker rebates. A wallet without that figure omits the field.Before this change, the documentation said such a wallet would carry an older stored realized P&L with a different basis. On October 10 every ranked wallet had the figure, so no response changed.realized_pnlkeeps its name, type and cent truncation.- The field was already optional. Treat its absence as “not available”, not as zero.
realized_pnl.@0xinsider/mcp 2.14.5 states the current tagline and bundles the combo SDK methods
@0xinsider/mcp 2.14.5 describes itself on npm and in its README as “Polymarket analytics & infrastructure for sports and esports markets”. The SDK copy bundled in the package (dist/sdk/) now includes client.combos.fills, client.combos.summary, and client.combos.get for the combo routes added on October 8, 2026. Its types follow the response changes listed in the entries since 2.14.4.Before this change, the npm description and README read “advanced tools for sports and esports markets, powered by Polymarket”, and the bundled SDK had no combo methods.- The 48 MCP tools, the CLI commands, and their output are unchanged. Neither calls the new methods.
- The MCP Registry entry stays at 2.14.4, which serves the same tools.
Trader pnl.total and leaderboard pnl come from Polymarket's latest leaderboard observation
GET /api/v1/trader/{address} pnl.total and GET /api/v1/leaderboard pnl now carry Polymarket’s all-time leaderboard P&L from its latest observation of the wallet. When that leaderboard has no current observation ranking the wallet, the field is left out.Before this change, both fields read a stored copy that only some wallets kept current. For about 63,000 wallets it still held a figure from before Polymarket’s current leaderboard data, and both fields served it as current; 853 of the 10,363 leaderboard entries were affected.pnl.totalandpnlchange value for those wallets, or are left out until Polymarket reports them.- The trader response’s
data_qualityadds aleaderboard_pnlgroup, dated by when Polymarket last reported the wallet. Thesyncgroup no longer coverspnl.total. - A trader read without a current observation asks Polymarket for one, so the field returns on a later read.
pnl.total is present whenever pnl.net_total is, or that require data_quality.status to be fresh: a wallet without a current observation now reports partial.What to change: Treat pnl.total and the leaderboard pnl as optional. Use pnl.net_total for a wallet’s total, and match data_quality groups by name, ignoring unknown ones.Pick of the Day expands its public market coverage
GET /api/v1/pick-of-the-day can now include selections from more markets.Before this change, some of those markets were never considered for a pick. Every publication check still applies, and 15 is the daily maximum, not a promised count. The private scanner feed is unchanged.Backward compatible. Request and response fields are unchanged. Already selected and historical picks keep the rules they were selected under.What to change: Nothing.Pick of the Day expands public and private market coverage
GET /api/v1/pick-of-the-day and the private scanner feed can now include selections from more markets.Before this change, fewer markets were covered. The response still does not promise a pick count, and 15 is the daily maximum at this release.Backward compatible. Request and response fields are unchanged. Already selected and historical picks keep the rules they were selected under.What to change: Nothing.Market snapshot volume_24h_usd now carries Polymarket's 24-hour volume, with its read time
GET /api/v1/market/{condition_id}/snapshot now fills liquidity.volume_24h_usd with Polymarket’s 24-hour volume for open markets, and adds liquidity.volume_24h_as_of, the time 0xinsider read that figure.Before this change, liquidity.volume_24h_usd was null for every market, and the field description said so.liquidity.volume_24h_usdcounts the shares takers bought in the 24 hours beforevolume_24h_as_of, each at its $1 settlement value. It is a share count, not cash, likeliquidity.volume_usd.- It stays
nullwhen Polymarket does not report one, which it does not for closed markets. liquidity.volume_24h_as_ofis an ISO 8601 time,nullexactly whenvolume_24h_usdis.
liquidity.volume_24h_as_of is a new field. liquidity.volume_24h_usd keeps its name and type and now carries a number where it was always null.What to change: Nothing required. Read volume_24h_usd for a market’s recent activity, and use volume_24h_as_of to judge how current it is.Positions drop every position on a market that has resolved
GET /api/v1/positions now returns positions on open markets only. A position leaves the feed when Polymarket resolves its market, on every read: the default feed, category, min_grade, condition_id, and wallet.Before this change, the feed also listed positions on resolved markets, often at their last price before resolution. Many of them had already been redeemed or sold. On October 9, 2026, 706 of the 2,000 largest rows were on resolved markets.- A
condition_idread of a resolved market now returns an empty list. - A
walletread now returns the wallet’s positions on open markets only, so a wallet whose markets have all resolved returns an empty list. - A void market, settled at 50/50, counts as resolved.
Position timeline returns fills from July 17, 2026 on; older wrong-side fills are deleted
GET /api/v1/trader/{address}/position-timeline and GET /api/v1/traders/{trader}/position-timeline now return fills from July 17, 2026 on. Every returned fill is the wallet’s own trade.Before this change, the timelines also returned fills from December 12, 2022 to April 28, 2026. A retired ingester recorded those fills with the other party’s side, so each showed the opposite action, or the other outcome_side at 1 minus the price. They were deleted on October 9, 2026, and the warning about them is gone from the action description.Breaking for clients that read timeline fills before July 17, 2026: those fills are no longer returned, and running_amount and running_avg_price no longer include them. No field changes.What to change: Drop cached timeline fills dated before July 17, 2026, and recompute any total built from them.copy_score's loss penalty compares the largest loss with capital deployed
quant_metrics.copy_score on GET /api/v1/trader/{address} and POST /api/v1/traders/batch now subtracts its 10-point loss penalty only when the wallet’s largest loss on one settled market is more than 30% of the capital it deployed across all its positions.Before this change, the penalty compared that loss with the wallet’s lifetime profit or loss. A wallet whose lifetime result was near zero or negative crossed the 30% line almost every time: on October 9, 2026, 126,113 of 200,447 wallets carried the penalty, against 13,475 under the new rule. 112,823 wallets gain up to 10 points, and 185 lose up to 10.The copy_score description in the OpenAPI document and the SDK types states the new rule. When the recorded loss on one market is larger than all the capital the wallet deployed, the inputs disagree and no loss penalty applies.Backward compatible. No field, type, or range changes. Each wallet’s score moves to the new rule the next time its metrics are computed, within 6 hours of the release.What to change: Nothing. If you stored copy_score values, do not compare scores from before this change with later ones.Market snapshot and explore volume fields now state their unit and that one is always null
The OpenAPI document and SDK types now describe
liquidity.volume_usd and liquidity.volume_24h_usd on GET /api/v1/market/{condition_id}/snapshot, and volume and rep_volume on GET /api/v1/markets/explore. The values do not change.Before this change, those four fields had no description, and volume_usd and volume_24h_usd read as dollar amounts.liquidity.volume_usd,volume, andrep_volumeare Polymarket’s volume figure for the market, as last stored: the total shares bought by takers, each counted at its $1 settlement value. They are share counts, not cash; 100 shares bought at $0.40 add 100, not 40. A stored figure can lag Polymarket’s current one.rep_volumeis thevolumeof the group’s highest-volume market,markets[0].liquidity.volume_24h_usdis alwaysnull. 0xinsider does not store a 24-hour volume.
volume_usd, volume, or rep_volume as dollars traded, label them as Polymarket’s volume figure in shares. Stop reading volume_24h_usd; it never carries a value.Pre-game volume, timeline action, and positions win_rate docs match the served values
The OpenAPI document and SDK types now describe
volume on GET /api/v1/sports/pre-game-sides, action on GET /api/v1/trader/{address}/position-timeline, and trader.win_rate on GET /api/v1/positions as the values the API already returns.Before this change, those three descriptions did not match the served values:volumeon pre-game sides and onGET /api/v1/sports/pre-game-side-observationswas described as USD. It is Polymarket’s traded-volume figure: cumulative taker volume in shares, each counted at its $1 settlement value. It is not the cash that changed hands; 100 shares bought at $0.40 add 100, not 40.actionon the position timeline was described as the taker’s side. It is the wallet’s own side of the fill. Fills dated before April 29, 2026 come from a retired ingester that recorded the other party’s side, so treat them as unreliable until they are removed.trader.win_rateon positions was described as a percentage from 0 to 100. It is a ratio from 0 to 1, the same scale as the trader profile.
volume as dollars traded, label it as Polymarket’s volume figure in shares. If you showed trader.win_rate as a percentage, multiply it by 100 first. If you rely on timeline fills dated before April 29, 2026, do not trust their side.The games catalog adds K League 1 coverage
GET /api/v1/games now includes K League 1 games in coverage.leagues as kor, under sport=Soccer. Use sport=Soccer&league=kor to list them, then GET /api/v1/games/{event_slug} for one game’s linked markets.Before this change, K League 1 had no dedicated scope in the games catalog.Coverage follows Polymarket’s published games. K League 2 is a separate league and is not included in this scope. Live status and scores follow the provider; a passed kickoff does not make a game live.Backward compatible. The league coverage is additive. Existing request parameters and response fields are unchanged.What to change: Nothing. Use league=kor when you want only K League 1 games.Games and pre-game observations add EuroLeague coverage
GET /api/v1/games now includes EuroLeague games in coverage.leagues as euroleague, under sport=Basketball. Use sport=Basketball&league=euroleague to list them, then GET /api/v1/games/{event_slug} for one game’s linked markets.Before this change, EuroLeague had no dedicated scope in the games catalog or the upcoming basketball supply for pre-game observations.GET /api/v1/sports/pre-game-side-observationsincludes upcoming EuroLeague games in its basketball supply.funnel.sports[].board_upcoming_unavailable_scopescan containeuroleague-basketballwhen that upcoming source is unavailable. The OpenAPI enum and generated SDK types include the value.- EuroLeague live status and scores still follow the provider. The upcoming scope does not infer that a game is live from its kickoff time.
euroleague-basketball. Use league=euroleague when you want only EuroLeague games.OpenAPI nullable enums accept the null values the API already returns
GET /api/v1/large-trades and other responses with nullable enum fields now have OpenAPI 3.1 declarations that accept the null values those responses already return.Before this change, the schemas used nullable: true while their type or enum still excluded null. Standard response validators could reject valid API responses.- Large-trade
suspicion_trackandtrader.grade_at_tradeallownull. - Already-nullable
platformfields in market search, exploration, flow, and reports allownull. - Already-nullable grade, pre-game side, webhook reason and outcome, and account recovery fields allow
null. - The unavailable pick response keeps
supersedes_pick_idrestricted tonull.
Combo fills and details avoid repeated lookups when a response is not cached
GET /api/v1/combos/fills and GET /api/v1/combos/{condition_id} now avoid repeated lookups when building a response that is not cached.Before this change, fill selection repeated some checks for the same combo, and detail requests read the same legs more than once.Responses keep the same fields, exact amounts, result rules, filters, pagination, access requirements, and cache freshness. Database or cache service pauses can still delay a response; this update does not establish that every request completes within 250 milliseconds.Backward compatible. No response field or request parameter changes.What to change: Nothing.The REST API adds combo fills, a live-combo count, and individual combo details
GET /api/v1/combos/fills, GET /api/v1/combos/summary, and GET /api/v1/combos/{condition_id} now return recorded Polymarket combo activity with paid API access.Before this change, the external REST API had no routes for combos.- The fill list returns wallets, legs, execution amounts, and combo results. It accepts result, leg count, stake, multiplier, grade, and sport filters, with newest-first or ranked pagination.
- Shares, collateral, fees, and execution prices retain exact decimal strings. The list excludes fills priced above
0.95and returns full rows without the website’s free delay. - The summary returns only the stored live-combo count and its
computed_at. An unavailable count isnull, not0. - One combo’s detail returns its legs, totals, latest 100 taker fills, lifecycle amounts when recorded, and data coverage. Combo results describe the YES conjunction, not a wallet’s realized P&L.
potd-trader v0.4.1 sets up Kalshi execution with one interactive command
potd-trader v0.4.1 adds a one-command installer and kalshi setup wizard that asks for credentials, a private key file, spending limits, and reviewed contract mappings in your terminal.Before this change, Kalshi setup required a release checkout, manual configuration edits, and separate commands for account checks, mapping, and dry runs.- The installer keeps Python and the trader isolated from your global installation.
- New setups default to demo with orders stopped. Existing stopped setups retain their account, mappings, and ledger.
- The wizard checks the account and picks, guides the existing contract review, and runs a dry run. Enabling orders still requires your environment’s explicit confirmation.
potd-trader v0.4.0 adds demo-first Kalshi execution with reviewed contract mappings
potd-trader v0.4.0 adds a kalshi command group for separate demo and production setups, manually reviewed contract mappings, dry runs, order controls, and reconciliation.Before this change, the standalone trader submitted orders only on Polymarket. The Pick of the Day signal and API remain Polymarket-backed.kalshi initcreates a separate configuration with demo mode and orders disabled.kalshi maprequires review of both contracts and your own Kalshi price ceiling. Source authorization and slippage limits remain additional price guards; Polymarket modeled returns are not transferred.- Kalshi orders use whole contracts and immediate-or-cancel submission. Daily reservations include worst-case fees; uncertain submissions stay blocked until reconciled.
- Changed contract PDFs or reported target positions or resting orders prevent new submissions. Use a dedicated account with no concurrent manual or other-tool trading; provider account reads can lag.
Pre-game sides includes holdings across linked events for the same game
GET /api/v1/sports/pre-game-sides and its deprecated alias GET /api/v1/sports-edge-signals now include positions across a game’s provider-owned parent and companion events when calculating directional_confidence, one_way_holder_count, hedged_holder_count, and one_way_graded_usd.Before this change, separate event slugs could omit opposing or unresolved holdings in the same game and overstate how one-way a wallet’s positions were.Backward compatible. Field names, types, and meanings are unchanged.What to change: Nothing.Pre-game sides recognizes team direction from named spread outcomes
GET /api/v1/sports/pre-game-sides and its deprecated alias GET /api/v1/sports-edge-signals now identify the team a spread position backs when both outcome labels name distinct teams. directional_confidence, one_way_holder_count, and hedged_holder_count reflect those positions across the game’s markets.Before this change, named spread outcomes were ignored. A position could leave directional_confidence as null, or make a wallet holding the same team’s moneyline and spread appear to back different teams.Generic Yes/No answers and unresolved outcomes remain unclassified. Position freshness checks and the safeguards for wallets holding both outcomes still apply.Backward compatible. Field names, types, and meanings are unchanged.What to change: Nothing.Pick of the Day removes editorial_note from the API, SDK, and MCP responses
GET /api/v1/pick-of-the-day, the TypeScript SDK’s PickOfTheDay type, and the MCP get_pick_of_the_day tool no longer expose editorial_note.Before this change, a pick could include an editorial note. The field and its website display have been removed.Breaking for clients that read editorial_note.What to change: Remove editorial_note from your response models and displays. Do not require the field in stored responses or future reads.Trader export markets describe a settled position by what it bought
The
markets rows in a JSON or NDJSON file from POST /api/v1/trader/{address}/export now describe a settled position by the shares it bought, not by the shares left after the market settled. A new field, settled_side, names the outcome a settled position took: yes, no, or both when it bought both outcomes. It is null on an open row.Shares left after settlement cannot tell you a side. A wallet redeems its winning shares and keeps its losing ones, so a settled row usually shows 0 and 0, or only the losing outcome.avg_priceon a settled position that bought both outcomes is nownull. Before this change it was the Yes entry price, or the entry price of the outcome with shares left, which is usually the one that lost.up_boughtanddn_boughton a settled row are now the shares each outcome bought: that outcome’s cost divided by its average entry price. They arenullwhen a position bought both outcomes and its cost per outcome is not recorded. Before this change they repeatedup_sharesanddn_shares, which are 0 and 0 on nearly every settled row.- Open rows do not change.
settled_side is a new field, and no field is removed or renamed. avg_price, up_bought, and dn_bought were already nullable.What to change: Read settled_side to tell a position on both outcomes from a one-sided one. If you read up_bought and dn_bought on settled rows, expect real share counts where you saw zeros.Positions trader.pnl and trending all_time_pnl_usd carry the profile's total P&L
trader.pnl on GET /api/v1/positions and all_time_pnl_usd on GET /api/v1/leaderboard/trending now carry the wallet’s total P&L as its 0xinsider profile shows it: Polymarket’s position P&L, fees included, plus maker and taker rebates. It is the same figure GET /api/v1/trader/{address} returns as pnl.net_total. The remote MCP tools get_positions and get_trending_wallets return the same values.Before this change, both fields carried pnl.total, Polymarket’s all-time leaderboard figure, which can differ from the profile’s total by a wide margin and even in sign.- Either field is omitted when Polymarket’s figure for the wallet is not available. The leaderboard figure is never sent in its place.
- Neither field affects which rows a page holds or their order.
GET /api/v1/positionsstill orders bycurrent_value_usd, and the trending list still follows Polymarket’s order. - A trending list built before this change keeps its old
all_time_pnl_usduntil it is rebuilt. A request that finds the list at least 2 minutes old starts a rebuild.
pnl.total from the trader endpoint, compare them with pnl.net_total instead.category_strengths counts markets a wallet bought on both sides
category_strengths on GET /api/v1/trader/{address}?expand=categories and on POST /api/v1/traders/batch now counts every settled position of $20 or more in each category, including a market the wallet bought on both sides. n_markets, wins, losses, win_rate, total_pnl and its alias scaled_total_pnl change for a wallet that holds such markets. rank and total_in_category move with them, and a wallet can enter a category it was below the 5-market floor in.Before this change, a position counted only when it had one side with an entry price between 0 and 1, so a market bought on both sides was left out, and its profit or loss and its result with it. On a 0.2% sample of settled positions of $20 or more on October 7, 33.8% were bought on both sides.The edge over the price paid on the category leaderboards still scores only positions with one priced side: a market bought on both sides has no single side to score.Backward compatible. The response shape is unchanged. Values move as each wallet’s categories are recomputed over the following days.What to change: Nothing. If you store category_strengths values, re-read them once the recompute has reached your wallets.Pick of the Day selections can become available earlier
GET /api/v1/pick-of-the-day can now publish automatic picks earlier in the day. Before this change, automatic picks appeared closer to kickoff. A published pick keeps its market, side, and frozen price.Read the returned release_at rather than calculating it from kickoff. retry_at and Retry-After on a 404 with pick_not_released still give the next time to ask.Backward compatible. Response shapes do not change; picks can appear earlier in the day.What to change: Nothing, if you follow retry_at. If you schedule reads from kickoff times, read earlier: a pick can be available hours before its game.Trader adds pnl.net_total, the total P&L the 0xinsider profile shows
GET /api/v1/trader/{address} and POST /api/v1/traders/batch now return pnl.net_total, the wallet’s total P&L as its 0xinsider profile shows it. It is Polymarket’s position P&L, fees included, plus maker and taker rebates, cut to whole cents, and it is omitted when Polymarket’s figure is not available. With expand=trust, trust.net_total_pnl describes its source, and the remote MCP tools get_trader and batch_get_traders return the same field.Before this change, the only total was pnl.total, Polymarket’s all-time leaderboard figure. It is not realized plus unrealized P&L, and on October 7, 2026 it differed from the profile’s total by more than $1 for 7,851 of 8,449 ranked wallets.pnl.totalkeeps its value and is now documented as Polymarket’s leaderboard figure.pnlonGET /api/v1/leaderboardis the same figure, also unchanged.- The
Total P&Lline ofGET /api/v1/trader/{address}/context.mdnow readspnl.net_total, so it matches the profile and the JSON body. trader.pnlonGET /api/v1/large-positionsnow carries the same total aspnl.net_total. It was documented as realized P&L but carried the leaderboard figure, and it is omitted when the wallet has no total.- The
syncgroup indata_qualitynow coverspnl.net_total.
pnl.net_total and every unchanged field. trader.pnl on large positions changes value for most wallets.What to change: Read pnl.net_total wherever you show a wallet’s total P&L. If you compared large-positions trader.pnl with pnl.total, compare it with pnl.net_total.Large-trade market_volume_share now divides shares by shares
GET /api/v1/large-trades, GET /api/v1/large-trades/history, their /api/v1/whale-trades aliases, and the MCP get_large_trades and get_large_trades_history tools now compute market_volume_share as the trade’s share count over the shares its market has traded. Polymarket records market volume in shares, so both sides of the ratio are now in the same unit.Before this change, the numerator was size_usd, so every value read the true share multiplied by the trade’s price. Over the 30 days to October 7 the old value was a median 1.74 times too small, and about 4.3 times too small for trades priced under 30 cents. sort=market_volume_share ranked favourites above longshots of the same size: half of its top 50 changes.- Every published value is larger, by a factor of
1 / price. The field still reads between 0 and 1, and its name and type are unchanged. min_market_volume_sharefilters andsort=market_volume_shareranks by the corrected value, so the same threshold now returns more trades.- The share is withheld when the recorded market volume is smaller than the trade’s share count. Over those 30 days this withheld one trade the old rule had published.
- A
sort=market_volume_sharecursor minted before this change continues against the new values, so a ranking paged across the change can skip trades.
market_volume_share with stored values or thresholds tuned to the old numbers. The response shape is unchanged.What to change: Re-read any stored market_volume_share values, and restart a sort=market_volume_share ranking from its first page. A min_market_volume_share threshold tuned to the old values now admits more trades; raise it if you want pages of a similar size.OpenAPI quota text and examples state the 500,000 Pro and 2,000,000 Max allowances
The OpenAPI document now states the monthly quota the API enforces: 500,000 requests per UTC month on Pro and 2,000,000 on Max, with pay as you go up to 4 times the plan’s allowance. The
@0xinsider/mcp 2.14.4 package README states the same allowances, and 2.14.4 requires @modelcontextprotocol/sdk 1.32.1 or later, past advisory GHSA-6qxp-vccf-f47h.Before this change, parts of the document still described the allowance from before October 1, 2026:- The
429descriptions ofGET /api/v1/sports/pre-game-sides,GET /api/v1/sports/pre-game-side-observations,GET /api/v1/pick-of-the-day/ledger/{pick_id},POST /api/v1/webhooks/{id}/verification-attempts, andGET /api/v1/webhooks/{id}/verification-attempts/{attempt_id}said Pro includes 250,000 requests, with a 1,000,000 pay-as-you-go ceiling. X-Monthly-Quota-Limitandmonthly_quota.limitdescribed only Pro’s allowance and showed250000. They now name both plans, and the examples show500000.monthly_quota.ceilingsaidlimit, or 1,000,000 with pay as you go on. It now says 4 timeslimit.- The
monthly_quota_exceedederror reason now says the account used its plan’s included requests, not the requests Pro includes.
X-Monthly-Quota-Limit or monthly_quota.limit instead of a fixed number.Pick of the Day groups Wimbledon by ATP/WTA and all cricket as Cricket
GET /api/v1/pick-of-the-day and the archive now return ATP or WTA in display_category for tennis with a known tour, including Wimbledon and doubles. Men’s and women’s cricket competitions return Cricket. Tennis with an unknown tour remains Tennis.Before this change, tennis could carry a tournament label such as Wimbledon, and cricket could carry a competition label such as The Hundred Women.Historical display labels are corrected too. The response shape, category, pick identities, results, prices, and returns remain unchanged.Breaking for clients that match the previous tournament or cricket competition labels.What to change: Accept ATP, WTA, and Cricket, and refresh stored historical display_category values before rebuilding grouped statistics. Keep using category for the canonical sport bucket.The MCP server card is also served at /.well-known/mcp.json
GET https://0xinsider.com/.well-known/mcp.json now returns the MCP server card, the same JSON document as GET https://0xinsider.com/.well-known/mcp. Some MCP discovery tools and site audits look for the card only at the .json path, where they received an error before this change./.well-known/mcp is unchanged and remains the canonical address. The MCP transport (POST) stays on /.well-known/mcp and https://api.0xinsider.com/api/v1/mcp.Backward compatible.What to change: Nothing.Trader grade-at observations report model_version grading, not grading-v4
GET /api/v1/trader/{address}/grade-at now returns observation.model_version as grading where it returned grading-v4. The field names the model family that wrote the grade, and 0xinsider has one wallet grading model, so the version suffix is gone. Every stored observation reads the new value, including the ones recorded before this change.The grade, the timestamps, model_build_sha, and every other field are unchanged. null still means the model is unknown.Breaking for clients that compare model_version with the exact string grading-v4.What to change: Accept grading. To tell builds of the model apart, read observation.model_build_sha.Pick of the Day updates daily availability and retry guidance
GET /api/v1/pick-of-the-day changes when automatic picks first become available for a New York date. Its retry fields now name the next available read time.Before this change, the next read time could be earlier than the day’s first available automatic pick.- Before a day’s first available pick, and after a skipped day, schedule your next read from the
404pick_not_releasedresponse’serror.retry_atorRetry-After. - Read each pick’s actual publication time from
published_at. - The response fields,
release_at, and the404body shape are unchanged.
error.retry_at or Retry-After.Pick of the Day moves automatic publication closer to kickoff
GET /api/v1/pick-of-the-day changes automatic publication timing so picks appear closer to their games. Picks still arrive throughout the day, up to the release’s limit of 15.Before this change, automatic picks could appear earlier in the day.- The response fields,
release_at, and the404pick_not_releasedresponse are unchanged. - A scheduled pick still waits for its returned
release_at.
error.retry_at or Retry-After.Pick of the Day pick_rank can reach 20 after a cancelled pick; picks stay at 15
GET /api/v1/pick-of-the-day can now return a pick_rank above 15 on a day with a withdrawn pick. A pick withdrawn because its game was cancelled keeps its rank but no longer uses one of the day’s 15 picks, so the next pick takes the next free rank, up to 20.Before this change a cancelled pick held one of the 15 slots, so that day ended with 14 picks.pick_ranknow runs from 1 to 20 inpicks,scheduled_picks,locked_picks,proof_pending_picks, the archive, and the ledger, and the OpenAPI document and the Pick of the Day MCP tool’s output schema say so.pick_countis still at most 15, and a day still carries at most 15 picks.pick_rankis deprecated. Usepick_idfor identity andpublication_orderfor order.
picks as a list.What to change: If you validate pick_rank against a maximum of 15, or index a fixed 15-slot array by rank, raise the bound to 20 or key by pick_id.Live-score source_revision values jump to millisecond scale and stay increasing
GET /api/v1/market/{condition_id}/snapshot now sends sports.live_score.source_revision and sports.live_score.tennis_points.source_revision as 13-digit integers, about 1,790,000,000,000 today. Each new revision is still greater than every earlier one for the same group, including after a restart of our cache.Before this change both values were small counters, about 2,000,000 and 50,000. A cache restart could move them backwards for up to 15 minutes, and a client that kept only strictly newer revisions froze the live score for that time.- The values are still integers that order updates. They are not timestamps; do not read them as a time.
- Every value fits in a JavaScript safe integer and in a signed 64-bit integer. It does not fit in a 32-bit integer.
- The first update after the change jumps from the small counter to the large value. A “newer than” check accepts it.
source_revision in a 32-bit integer column or variable, widen it to 64 bits. Nothing else.MCP 2.14.3 cancels HTTP work and bounds documentation resource reads
@0xinsider/mcp 2.14.3 now forwards MCP request cancellation to every tool and resource HTTP read. Documentation resources also have a 15-second deadline covering headers and the complete response body.Before this change, cancelled API reads continued until their existing deadline, and documentation reads had no deadline. Caller cancellation now has the local error code request_cancelled; the server’s own API deadline retains request_timeout.Cancelling a request stops the local HTTP read. It does not roll back a POST that already committed or guarantee that the remote server stopped its work. Tool names, arguments, results, and automatic retry behavior are unchanged.Backward compatible.What to change: Upgrade the local package to 2.14.3 once it is published. No MCP configuration changes are required.TypeScript SDK 0.18.0 types empty picks and withheld identity fields
The generated declarations in
@0xinsider/sdk 0.18.0 now describe the current API contract. API response bodies and SDK request behavior are unchanged by this release.Before this change, the published SDK declarations omitted these response cases:getPickOfTheDaycan returnstate: "none"with no entitled picks. Narrow onstatebefore reading fields from a full pick.- Archive and uncommitted-ledger entries can omit
matchupandcategory. Check whether each field is present before rendering it. - Sealed ledger entries withhold
kickoff. Read the recorded kickoff from an opened entry when it becomes available. entry_authorization.policy_versionsupports an additional opaque value. Validate it against the versions supported by your client while retaining your authorization checks.MarketSnapshot.sports.live_score.tennis_points, when present, follows the namedTennisPointsshape. Custom response objects must use that shape.
PickOfTheDayNoEntitledPicks, TennisPoints, and TennisRanking by name.Breaking for TypeScript clients that depend on the previous narrower declarations.What to change: Update the affected narrowing, presence checks, and response types before upgrading to 0.18.0. Existing runtime requests remain valid.TypeScript SDK optional fields explicitly permit existing undefined values
The TypeScript SDK,
@0xinsider/sdk, now explicitly permits undefined in optional paging progress, parsed stream-frame, and data freshness fields that already return it. This makes the declarations usable with TypeScript’s exactOptionalPropertyTypes option.Before this change, those declarations used optional properties without explicitly describing their existing undefined values. Callers with the stricter compiler option could therefore reject otherwise valid SDK values.Return values and API response bodies retain their existing shapes. The SDK also checks indexed access and control flow during development; missing optional request fields are omitted before calling Fetch.Backward compatible. This adds accurate type information without changing valid requests or returned data.What to change: Nothing. Enable exactOptionalPropertyTypes in your own project when you are ready to check absent properties separately from explicit undefined.Referral-only paid API and feed access ends when the earned grant ends
Paid operations in the REST API, MCP, and the live event feed now require an unexpired referral grant when referral rewards are the account’s only source of Pro access. A refund removes the unearned portion of that grant. Independently paid subscriptions and separately authorized complimentary access retain their access.Before this change, an expired referral grant could leave a never-paying account authorized for Pro indefinitely. Already awarded referral access could also survive a refund of the qualifying payment.The response schema and authentication headers are unchanged. Once temporary paid access ends, paid API operations return the existing subscription-required refusal, and an open paid feed disconnects on its normal authorization recheck.Breaking for clients that depended on referral access continuing beyond its earned period.What to change: Handle the subscription-required response by prompting the account owner to subscribe. Reconnect paid feeds only after the account has valid paid access.
Soccer games identify home, draw, and away markets
GET /api/v1/games and GET /api/v1/games/{event_slug} now identify each soccer result market and report when the game offers a draw.For a soccer game with home-win, draw, and away-win markets:markets[].sideidentifies each market ashome,draw, oraway.draw_offeredistruewhen the game offers a draw market.
side, and the game reported draw_offered: false even when a draw market was present. The SDK’s listGames and getGame methods and MCP’s list_games and get_game tools return the same corrected fields.Backward compatible. Existing optional fields now carry the classified values; no field or type migration is required.What to change: Nothing. Use markets[].side and draw_offered to identify result markets instead of guessing from their labels.Pick of the Day archive excludes withdrawn picks and their statistics
GET /api/v1/pick-of-the-day/archive now excludes withdrawn recommendations from picks, days, hit_rate, and the cumulative series. A later market settlement does not return a withdrawn pick to the archive.Before this change, a withdrawn released pick remained in the archive and could contribute to its statistics after settlement. Withdrawn picks are also excluded from current and dated recommendations.The public ledger keeps every published commitment under its original pick_id. Its identity and proof bytes remain unchanged, and market settlement still determines the recorded result. Request and response fields are unchanged.Breaking for clients that use the archive as a complete list of published commitments.What to change: Refresh cached archive responses and use the public ledger when you need every published commitment. No field or type migration is required.Pick of the Day excludes cancelled games from active recommendations
GET /api/v1/pick-of-the-day now excludes a pending pick from active recommendations when its game is explicitly cancelled. New selections also check the provider’s game status before publication.Before this change, a cancelled game could be selected or remain in the current day’s recommendations.The archive and ledger retain the released pick’s identity and publication record. Market settlement still determines its recorded result.Backward compatible. Request and response fields are unchanged. The endpoint can return fewer picks or the existing 404 with error.reason: "pick_not_released" when no active pick remains.What to change: Nothing. Keep handling an empty day with Retry-After or error.retry_at, and store each pick_id rather than assuming a fixed pick count.