Schema Feed Article lines no longer carry a speakable selector
GET /feeds/research.jsonl, /feeds/learn.jsonl, and /feeds/news.jsonl now serve each Article without a speakable property. The research and learn results of GET /ask and each page’s JSON-LD drop it too.Before this change, every Article carried speakable.cssSelector ["h1", "[data-speakable]"]. No research, learn, or news page renders a data-speakable element, so validator.schema.org reported NO_MATCHES_FOUND on each of those pages.Backward compatible. schema.org makes speakable optional, and headline carries the same text as the page’s h1.What to change: Nothing. A consumer that read speakable reads headline instead.Explore's change_pct_24h measures a full 24 hours, or is null
GET /api/v1/markets/explore now computes change_pct_24h and no_change_pct_24h from the price exactly 24 hours ago, to within 2 minutes. price_points and no_price_points start at that price, so the first point and the change describe the same window.Before this change, the series started at the first whole hour after that moment, so each change covered 23 to 24 hours. Measured on October 20, 2026 across every market with a published change, the starting price was off by more than 2 cents in about 1 in 150 reads, and by up to 39.5 cents on a fast-moving market.- A market younger than 24 hours now has
change_pct_24h: nullinstead of a change over its shorter life. Itsprice_pointsstill return. - A resolved market whose price series stopped when trading closed also has
change_pct_24h: null. Its old figure measured the last in-play price, not the resolution, and on October 20, 2026 it was up to 80 cents away from Polymarket’s own 24-hour change. score_components.price_move_signalisnullwhenever both changes are, sodiscover_scoregives such a market no credit for a price move.- The
explore_marketsMCP tool returns the same values.
price_points[0], it now matches change_pct_24h.Remote MCP refuses a bad Origin or protocol header before it reads the body
POST /api/v1/mcp with an API key or OAuth token again checks the Origin and MCP-Protocol-Version headers before it reads the request body. A disallowed Origin answers 403, and an unsupported MCP-Protocol-Version answers 400 with JSON-RPC error -32600, whatever the size of the body. Neither refusal counts against your request limits.Before this change, from October 18, a keyed POST read up to 1 MiB of its body before these checks:- A body over 1 MiB got the
Failed to read request bodyerror instead of the403or400. - A slow body delayed the refusal until it arrived.
- A refused
tools/callcounted against the per-minute allowance, the monthly quota, and pay-as-you-go usage.
Edge Hunter observations keep a same-event market when the 168-market limit is full
GET /api/v1/edge-hunter with view=observations now keeps a place for a most-held wider_holder market inside its event’s limit of 4 only when the limit of 168 markets in all has room for it. When the soonest kickoffs and in-play markets fill the 168, the most-held market gives up that place, and another market of the same event can take it.Before this change, the most-held market kept the place even when the 168 were full. A market of the same event among the soonest kickoffs was then left out and counted in coverage.capacity_limited_markets, and the most-held market was left out as well.Backward compatible. No field, parameter, or enum value changes. The limits stay 8 markets per group and sport, 4 per event, and 168 in all.What to change: Nothing.Holder win rates carry the wallet's average price paid, category_avg_price
GET /api/v1/market/{condition_id}/holders and the display_holders entries of GET /api/v1/pick-of-the-day now return category_avg_price beside a measured category_win_rate. It is the wallet’s average entry price in the same category, from 0 to 1, so 0.98 is 98 cents.Before this change, a holder carried only the win rate and its record. A wallet that buys outcomes that are already near-certain wins almost every time, so a 99% win rate with no price could read as skill. One MLB holder won 1,532 of 1,543 markets, and 1,361 of them were bought at 98 cents or more.- The sample is the wallet’s settled markets in the category of $20 or more where it bought one side at a known price. It is the sample behind the Edge column on the sport leaderboards, and it can differ from the
category_win_recordsample, which counts every trade size. - The field is present only when
category_win_rate_statusismeasuredand that sample has at least 5 markets. It is absent otherwise, nevernull. - On an esports market or pick, the price is the
Esportscategory’s, also whencategory_win_rate_gameis present. There is no per-game price. - The MCP tools
get_market_holdersandget_pick_of_the_dayreturn the same field.
Games no longer report a period past the map that decided an esports series
GET /api/v1/games and GET /api/v1/games/{event_slug} now keep an esports series’ status.period on the map that decided it. When one side has won the majority of a best-of series, period reads that map, for example 2/3 for a best-of-3 won 2-0.Before this change, period could read the next map for about 20 seconds, for example 3/3 beside a 2-0 score in a best-of-3, before the game ended and it returned to 2/3. No such map is ever played.Backward compatible. No field, parameter, or enum value changes. The scores and every other status field are unchanged.What to change: Nothing.Combo fills: a result filter lists a combo only when recorded and current results agree
With
result set, GET /api/v1/combos/fills now lists a combo only when its recorded result and its current result are the same. Before, it checked only the current result. A combo whose two results differ still appears when you send no result filter, under its current status.On October 18, 2026, no listed fill had results that differed (0 of 1,541,021). The same change makes filtered pages faster: deep pages under result were taking up to 14 seconds and could return 503.Backward compatible. Response fields and their meaning are unchanged.What to change: Nothing.Edge Hunter observations name their selection, and most-held wider_holder markets show a day ahead and stay to kickoff
GET /api/v1/edge-hunter with view=observations adds admitted_by to every row. most_held marks the group of up to 8 markets per sport with the most graded holders, and every in_play row. soonest_kickoff marks the 8 soonest wider_holder kickoffs per sport. An emerging_pile row carries the value of its wider_holder row.The most-held wider_holder group added earlier on October 18 now looks 24 hours ahead instead of 12, and a market in it keeps its place until its game starts. Before this change, a market with more graded holders could take the place of one already shown, which then returned only among the 8 soonest kickoffs.- A market gives its place up when its holder read shows no observation: its graded holders split, no current graded holder is found, or its price is unavailable. A holder read that does not finish in time does not cost it its place.
- The limits stay the same: 8 markets per group and sport, 4 per event, and 168 in all. Markets beyond them still count in
coverage.capacity_limited_markets.
admitted_by is a new required field on observation rows; no field, parameter, or enum value is removed or renamed.What to change: Nothing. To compare with the most-held selection the feed made before October 17, keep the rows with admitted_by most_held. Expect those rows from up to a day before kickoff, each staying until its kickoff.Max allows 200 requests per minute
A Max account now allows 200 authenticated requests per minute, up from 100. Its batch item budget follows: 5,000 item units per minute, up from 2,500. Pro stays at 100 requests and 2,500 item units. See Rate limits.
RateLimit-LimitandX-RateLimit-Limitreport your plan’s minute allowance:200on Max,100on Pro.- On batch routes,
X-Batch-RateLimit-Limitandmeta.rate_limit.limitreport5000on Max and2500on Pro. GET /api/v1/usagereports the same minute allowance indata.rate_limit.limit.- The monthly quota does not change: 2,000,000 requests on Max and 500,000 on Pro. The usage and identity reads keep their shared allowance of 100 reads per minute, and the per-IP allowance stays 1,200 requests per minute.
RateLimit-Limit and RateLimit-Remaining speeds up on its own. A client that hard-codes 100 a minute can raise it to 200 on Max.Remote MCP counts only tools/call against your request limits
POST /api/v1/mcp now counts only tools/call against the per-minute request allowance, the monthly quota, and pay-as-you-go usage. Every other message is free, with or without a key.Before this change, every message that carried a key counted as one request. A client that opens a session for each tool call spent 5 requests on it, so the 100-request minute allowance held about 20 tool calls.- Free now:
initialize,ping,tools/list,notifications/initialized,notifications/cancelled, and the405answers toGET,HEAD, andDELETE. - A free message still checks the key. A wrong key answers
401, a lapsed plan402, and a locked account423, as before. - On a free message,
RateLimit-*describes the per-IP allowance of 1,200 requests per minute, the same as a message without a key. It carries noX-Monthly-Quota-*orX-Usage-Accountingheader. - Free messages no longer appear in your usage counts or on a pay-as-you-go invoice.
RateLimit-* from a tools/call response, not from the handshake.Edge Hunter wider_holder observations show the most-held markets hours before kickoff
GET /api/v1/edge-hunter with view=observations&cohort=wider_holder now evaluates up to 8 more markets per sport: those with the most graded holders among the rest that kick off within 12 hours. The 8 soonest kickoffs per sport are evaluated as before, so a market about to start stays evaluated until kickoff.Before this change, each sport evaluated only its 8 soonest kickoffs. In soccer they reached about 13 minutes ahead, so a soccer row first appeared a median of 14 minutes before kickoff.- The limits of 4 markets per event and 168 in all do not change. Markets beyond them still count in
coverage.capacity_limited_markets. - A market in the new group can drop out before its kickoff when a market with more graded holders takes its place. It returns once it is among the sport’s 8 soonest kickoffs.
- A
wider_holderoremerging_pilemarket that kicks off more than an hour later is read again only when its last read is 10 minutes old. Until then its row carries that read, with the read’s ownholder_snapshot_atandprovider_read_source: "cached". The 15-minute limit on an older read does not change. in_playdoes not change.
wider_holder rows, many of them hours before kickoff. Use holder_snapshot_at for the age of a row’s holder read.Pick of the Day moves the free selection when the free pick's game is cancelled
GET /api/v1/pick-of-the-day now keeps a free selection when the day’s free pick is withdrawn because its game was cancelled, or is never published because its game ended or was cancelled before its release time. Another pick of that day becomes the free selection, and its is_free_selection changes from false to true.Before this change, the day then had no free selection. This replaces the line of the earlier October 18 entry that said a withdrawn free selection keeps the designation alone.- The new free selection is the first pick in
publication_orderthat is still scheduled or published, is not withdrawn, and whose game has not started. It can be a pick you already read withis_free_selection: false. - If no pick qualifies, the next addition to that day becomes the free selection, provided its game has not started.
- The withdrawn pick keeps
is_free_selection: true. The current day andGET /api/v1/pick-of-the-day/archiveleave withdrawn picks out, so they still show at most one free selection a day.GET /api/v1/pick-of-the-day/ledgerkeeps every published pick, so a ledger day can show two picks withis_free_selection: true: the withdrawn pick and the one that replaced it. - Pro still opens the free selection plus the first 4 nonfree picks, so on such a day Pro opens up to 5 live picks where before it opened up to 4.
is_free_selectionnever changes fromtruetofalsewhile the API serves the pick. A scheduled pick that is set aside and later restored to the schedule can return withfalse, when another pick holds the free selection by then. The Pick of the Day MCP tools return the sameis_free_selection. Ledger proofs do not change, because a sealed pick does not includeis_free_selection.
is_free_selection: true and use the one that was not withdrawn.Pick of the Day moves the free selection when the designated pick is not released
GET /api/v1/pick-of-the-day now keeps a free selection when the day’s designated free pick is held back at its release time. Another pick of that day becomes the free selection, and its is_free_selection changes from false to true.Before this change, the day then had no free selection. On October 18, 2026, signed-in free accounts could open none of the day’s 16 published picks until we corrected it at 09:15 UTC.- The new free selection is the first pick in
publication_orderthat is still scheduled or published, is not withdrawn, and whose game has not started. It can be a pick you already read withis_free_selection: false. is_free_selectionnever changes fromtruetofalseon a pick the API serves.- Pro opens the free selection plus the first 4 nonfree picks, so on such a day Pro opens up to 5 picks where before it opened up to 4.
- If every remaining game has already started, the day has no free selection.
- A free selection that is withdrawn, rather than held back, keeps the designation.
GET /api/v1/pick-of-the-day/archive,GET /api/v1/pick-of-the-day/ledger, and the Pick of the Day MCP tools return the sameis_free_selection. Ledger proofs do not change, because a sealed pick does not includeis_free_selection.
pick_id, read is_free_selection again instead of keeping the first value you saw.Edge Hunter in-play observations never serve a cached holder read from before kickoff
GET /api/v1/edge-hunter with view=observations no longer serves an in_play row from a cached holder read taken before that market’s own kickoff, once that kickoff has passed. Such a market now gets a new holder read, or its last in-play read from up to 10 minutes earlier.Before this change, a holder read cached in the last minutes before kickoff could be served for up to 3 minutes after the game went live. That row’s position values were priced against the pre-game market, and its holder_snapshot_at was earlier than its game_start_time.- The check uses the market’s scheduled kickoff,
game_start_time. If a game starts late, a read cached between the scheduled and the real start can still be served. - A market can be
in_playbecause its parent event is live while its owngame_start_timeis still ahead. Its cached read is still served, because every read it can have is from before its own kickoff. - A row with
provider_read_source: "live"is a new read and is not affected.
in_play row with provider_read_source: "cached" whose game_start_time has passed now always has a holder_snapshot_at at or after that game_start_time.What to change: Nothing.Edge Hunter in-play observations can reuse a holder read up to 10 minutes old
GET /api/v1/edge-hunter with view=observations now keeps an in_play market when a refresh cannot finish its new holder read. The row serves that market’s last in-play holder read for up to 10 minutes, with that read’s original holder_snapshot_at and provider_read_source: "cached".Before this change, an in_play market whose holder read did not finish ended as holder_deadline_unavailable and was missing from that snapshot. On October 18, 2026, from 05:25 to 05:55 UTC, 172 of the 175 markets that ended that way were in_play.- The reused read is always one taken after the game went live and after that market’s own kickoff. A read from before kickoff is never reused for an
in_playrow. - The 10 minutes are counted when the snapshot is computed. After 10 minutes without a new read, the market ends as
holder_deadline_unavailableagain. - A provider error still ends the market as unavailable. Only a read that ran out of time falls back.
in_playrows still need aholder_snapshot_atno older than 30 minutes.wider_holderandemerging_pilerows keep their 15-minute limit.
in_play row’s holder_snapshot_at can now be up to 10 minutes old, where before it was at most about 3 minutes old.What to change: Nothing. If you need a fresher in-play holder read, compare holder_snapshot_at with your own limit.GET /api/v1/usage sends monthly_quota.unavailable_reason, and SDK 0.29.0 types it
GET /api/v1/usage now sends monthly_quota.unavailable_reason on every response that carries monthly_quota. It is "usage_price_reconciliation_required" when billing must reconcile the account’s usage price before pay as you go can be used, and null otherwise. In that state pay_as_you_go is false.Before this change, the route never sent the field. The entry of October 1, 2026 announced it, and the entry of October 14, 2026 removed it from the document because the route did not send it. The OpenAPI document lists it as always sent, and the TypeScript SDK 0.29.0 types it as "usage_price_reconciliation_required" | null.Backward compatible. The field is added; no field is removed or retyped.What to change: Nothing. To tell a billing problem from pay as you go being off, read unavailable_reason beside pay_as_you_go.Edge Hunter observations no longer mark a snapshot degraded for the 100-holder page
GET /api/v1/edge-hunter with view=observations no longer adds holder_scan_incomplete to degradation_reasons. A market with more than 100 holders on an outcome token still shows holder_scan_complete: false and still counts in coverage.incomplete_holder_markets, but it no longer sets degraded to true.Before this change, nearly every snapshot listed holder_scan_incomplete, because each market’s holders come from 1 page of up to 100 per outcome token and busy markets have more. On October 17, 2026, all 1,028 snapshots from 09:00 to 22:00 UTC were degraded, so the flag could not separate a failed refresh from a healthy one.degradedis nowtrueonly for an operational cause, such asholder_deadline_unavailable,provider_unavailableorlive_source_stale. Snapshots can still be degraded while holder reads miss their refresh deadline.holder_scan_incompletestays in thereasonenum, retired, and stays a funnel terminal.- The
get_edge_hunterMCP tool and the SDK’sedgeHunter.observations()return the same fields and change the same way.
degraded is false more often.What to change: Nothing. To check holder coverage, read each row’s holder_scan_complete or coverage.incomplete_holder_markets, not degraded.Pick of the Day leaves a void pick out of every money figure
GET /api/v1/pick-of-the-day/archive now leaves a pick that settled void out of every money figure. A void is a pick whose market settled with no winner, such as a cricket match with no result. It stays in picks and in the void counts, and it counts as neither a win nor a loss.Before this change, the archive valued a void at Polymarket’s 50-50 payout, stake_usd * 0.5 / backed_price (the October 17, 2026 entry below). A favorite bought at 0.655 then read as a loss of about $237 on a $1,000 stake, for a game that had no winner.picks[].stake_usd,return_usd,return_per_100,payout_display, andunit_scoreare omitted for a void, as they already were for a pending pick.picks[].backed_pricestays on a void.hit_rate.net_profit_usd,hit_rate.staked_usd,hit_rate.roi_pct,hit_rate.unit_score, anddays[].unit_scoreleave voids out.hit_rate.series[]adds no point for a void.hit_rate.pctanddecideddo not change, because a void never counted in them.- The
return_usdonGET /api/v1/pick-of-the-dayis omitted for a void pick in the same way.
stake / backed_price, a loss returns 0, and a void or a withdrawn pick is left out.Backward compatible. No field is added, removed, or retyped. Each omitted field was already optional.What to change: Nothing, unless you value a void yourself. Then leave it out of profit, total stake, ROI, and units.The public picks ledger leaves withdrawn picks out of its record, as the site does
The
index.json record in github.com/0xinsider/picks now leaves out every pick the site withdrew. Those picks are left out of wins, losses, voids, decided, and hit_rate, as well as the money figures. A new record.withdrawn and record.proven_record.withdrawn count them instead. The record now matches the site’s and the rule this changelog gave on October 17: skip every entry whose withdrawal_reason is not null.Before this change, the record counted a withdrawn pick in its wins, losses, and voids and left it out of the money only. On October 18, 2026 it read 348 wins, 213 losses, and 561 decided against the site’s 346, 212, and 558.- The partition is now
decided + voids + withdrawn = opened. - The withdrawn picks stay in
ledger/with their commitments and real outcomes, andverify.pystill verifies them. It prints their outcomes on a separatewithdrawnline. - The ledger API,
GET /api/v1/pick-of-the-day/ledger, is unchanged.
wins, losses, voids, decided, and hit_rate drop by the withdrawn picks.What to change: If you check decided + voids == opened on index.json, add withdrawn to the left side.A trader's sync_status no longer reads synced while a sync runs
GET /api/v1/trader/{address}, POST /api/v1/traders/batch, GET /api/v1/trader/{address}/context and the MCP get_trader tool now return the sync_status and synced_at the trader has at the time of the request.Before this change, a cached trader body could return sync_status: "synced" for up to 60 seconds after a sync had started, and the previous synced_at for up to 60 seconds after it had finished.- A cached body is still served while the trader’s
sync_statusandsynced_atare unchanged, withmeta.cachedandmeta.cache_age_sas before. - When either has changed, the body is rebuilt from current data and
meta.cachedisfalse.
sync_status, you can read it directly.Large trades before April 2, 2026 can carry a verified outcome and token_id
GET /api/v1/large-trades, GET /api/v1/large-trades/history, GET /api/v1/large-trades/{id}, their whale-trades aliases, and the whale dataset export can now return outcome, token_id and outcome_index for a trade recorded before April 2, 2026.Before this change, all three were null for every trade before that date. Before April 2, 2026, 0xinsider stored the wrong outcome for a large share of trades on markets whose outcomes are names, such as two teams. Those trades have since been corrected. A trade before that date now serves its outcome only when the outcome was checked against the fill’s own trade records and every record agreed. Any other trade from that period still serves null.- The verified outcomes are being recorded in one batch. Until that batch runs, every trade before April 2, 2026 still serves
null. - About 93% of the 394,000 large trades before April 2, 2026 qualify. The rest stay
null: their fill’s records show both outcomes, no record exists, or the records disagree with the stored outcome. - Trades from April 2, 2026 on are unchanged.
null outcome still means the side is unknown, not a default.Edge Hunter pre-game observations can reuse a holder read up to 15 minutes old
GET /api/v1/edge-hunter with view=observations now keeps a pre-game market in wider_holder and emerging_pile when a refresh cannot finish its new holder read. The row serves that market’s last holder read for up to 15 minutes, with that read’s original holder_snapshot_at and provider_read_source: "cached".Before this change, a holder read that did not finish ended the market as holder_deadline_unavailable until a later refresh read it again. On October 17, 2026, 35 markets a refresh ended that way on average, and wider_holder ranged from 5 to 44 rows between refreshes.in_playrows never reuse an older read.- A provider error still ends the market as unavailable. Only a read that ran out of time falls back.
- After 15 minutes without a new read, the market ends as
holder_deadline_unavailableagain. - Each market’s holders come from 1 page of up to 100 holders per outcome token. A token with more holders leaves
holder_scan_completeasfalse. The extra page reads that tried to complete such scans are removed; none of them ran in the previous 7 days.
holder_snapshot_at values can be up to 15 minutes old.What to change: Nothing. If you need a fresher holder read, compare holder_snapshot_at with your own limit.Pick of the Day ledger adds the published_after_kickoff withdrawal reason
GET /api/v1/pick-of-the-day/ledger and GET /api/v1/pick-of-the-day/ledger/{pick_id} can now return a second value in withdrawal_reason: published_after_kickoff.It marks a pick that was published at or after its game started, so it was never a pre-game pick. That includes a suspended match that resumed later, where the scheduled start moved to the resumption. The site’s record leaves such a pick out of every count and money figure, the same as a provider_game_cancelled pick.- The field’s values are now
provider_game_cancelled,published_after_kickoffornull. - A
provider_game_cancelledwithdrawal happens while the pick is pending. Apublished_after_kickoffwithdrawal can reach a pick that has already settled, so an entry’swithdrawal_reasoncan change fromnulltopublished_after_kickoffafter you first read it. - The withdrawn pick keeps its commitment and its real outcome in the ledger. The field is not part of the hashed
payload, so nocommitment_hashchanges.
withdrawal_reason against a closed list must accept the new value.What to change: If you rebuild the track record from the ledger, keep skipping every entry whose withdrawal_reason is not null, and re-read settled entries instead of caching their withdrawal_reason at settlement.Edge Hunter wider_holder observations follow the kickoff order, soonest first
GET /api/v1/edge-hunter with view=observations&cohort=wider_holder now evaluates each sport’s markets in kickoff order, soonest first. Among markets with the same kickoff, the market with the most graded holders comes first. A pre-game market that is evaluated stays evaluated until its game starts.Before this change, each sport’s 8 wider_holder slots went to the markets with the most graded holders in the whole 48-hour window. A market seen a day before kickoff could drop out when a later game with more holders entered the window, and be missing at its own kickoff.- The limits stay the same: at most 8 markets per sport and cohort, 4 per event, and 168 in all. Markets beyond them still count in
coverage.capacity_limited_markets. in_playkeeps its order, most graded holders first.- A market that enters the signals view’s admission leaves
wider_holder, as before.view=signalslists it only while its holder pile leans to one side, its backed side has a holder at or abovemin_grade, and its kickoff is withinhorizon_hours. The cohort description now says so.
wider_holder before kickoff, also read view=signals with horizon_hours=48.Pick of the Day ledger says which settled picks the site withdrew
GET /api/v1/pick-of-the-day/ledger and GET /api/v1/pick-of-the-day/ledger/{pick_id} now add withdrawal_reason to every settled entry. It reads provider_game_cancelled when the site withdrew the pick because Polymarket marked its game Cancelled, and null when the pick counts in the record.Before this change, the ledger did not say whether a pick was withdrawn. A withdrawn void and a counted void looked the same, so a client could not reproduce the site’s record from the ledger.openedentries always carrywithdrawal_reason.uncommittedentries carry it once the pick settles, and omit it while the pick is pending, the same way they omitmatchup.sealedentries do not carry it.- A withdrawn pick keeps its commitment and its real outcome in the ledger. The site’s record leaves it out of every count and money figure.
- The field is not part of the hashed
payload, so nocommitment_hashchanges.
stake / backed_price, a loss returns 0, and a void returns stake * 0.5 / backed_price. Leave a withdrawn pick out.Backward compatible. One field is added. No field is removed or retyped.What to change: Nothing, unless you rebuild the track record from the ledger. Then skip every entry whose withdrawal_reason is not null.Explore markets states how old each sharp-money split is, as sharp_money_as_of
GET /api/v1/markets/explore now sends sharp_money_as_of on every market: when the oldest stored smart-money wallet behind the split in smart_score, smart_count and smart_label last finished syncing, and null when there is no split or a wallet’s sync time is unknown. Some holdings in the split can come from a Polymarket holder read cached for up to 3 minutes, which this time does not describe.Before this change the split carried no age, so it read as current. Graded wallets sync on different schedules, and most splits rest on positions that are days old: on October 17, 2026 the oldest position behind a market’s split had a median age of 145 hours.Backward compatible. The field is new, and the split itself is unchanged.What to change: Nothing. To treat the split as recent, check sharp_money_as_of against your own bound; null never means current.Pick of the Day values a void pick at the 50-50 payout, not a refund
GET /api/v1/pick-of-the-day/archive now values a pick that settled void at Polymarket’s real payout. Polymarket settles a void market 50-50 and pays $0.50 a share, so a void returns stake_usd * 0.5 / backed_price.Before this change, the archive valued a void as if the stake were refunded. A pick bought at 0.64 then read as $0 profit, but the market paid $781.25 on a $1,000 stake, a loss of $218.75.picks[].return_usdisstake_usd * 0.5 / backed_pricefor a void, andpicks[].return_per_100is50 / backed_price. Both are omitted for a void with no frozen price.hit_rate.net_profit_usd,hit_rate.staked_usd,hit_rate.roi_pct, anddays[].unit_scorenow include a priced void. A void still counts invoid, never inwins,losses, ordecided, sohit_rate.pctdoes not change.hit_rate.series[]adds one point for a priced void. The point moves the profit and leaves the win rate unchanged.
stake * 0.5 / backed_price.Games that Polymarket has suspended now read as suspended, not live
GET /api/v1/games and GET /api/v1/games/{event_slug} now report a game that Polymarket has suspended, whose period reads SUS, with status.state suspended, status.match_status suspended, and status.live false. A request with status=live no longer returns such a game, and status=suspended does.Before this change, these endpoints reported a suspended game with status.state live and status.live true, because Polymarket keeps a suspended game’s in-play flag set. On October 17, 2026, two soccer games suspended on October 10 still read live.status.periodstill readsSUS, and the score is the last one Polymarket sent.- The game reads
liveagain when play resumes and its period moves on. freshness.sourcereadsupcomingfor a suspended game.- The
status.livedescription now names the two cases where the field differs from Polymarket’s own flag: a suspended game, and a game rescheduled to a later kickoff, both readfalse.
suspended was already a documented value of status.state and status.match_status.What to change: Nothing, if your client already handles suspended. If you list live games with status=live, also request status=suspended to see games that stopped during play.Market holders keep an empty answer for up to an hour while the market has no trades
GET /api/v1/market/{condition_id}/holders now keeps an answer whose scan found no wallet at all (scan.wallet_count is 0), on an open market before its end date, for up to an hour while no trade in that market is seen after the scan. A trade, the market closing, resolving or passing its end date, or the hour ending stops the hold, and the next request scans again. The kept answer carries meta.cached: true, and meta.cache_age_s can now reach 3,600 seconds for these markets.Before this change, every answer was recomputed at least once a minute, so meta.cache_age_s stayed under 60 seconds, over a Polymarket scan at most 180 seconds old.- Shares that reach an empty market without a trade (a split, a merge or a transfer) can show up to an hour late.
- An answer whose scan saw any wallet, graded or not, keeps the one-minute refresh.
meta.cache_age_s for the age of an empty answer; the first trade in the market refreshes it.