Polymarket internals
What the Polymarket endpoints and the archive record: market and token ids, recorded and catalog markets, event rows, order-book snapshots, top-of-book quotes, the on-chain settlement and the trade tape behind the trader routes.
The Polymarket CLOB here is recorded by Vike, not a proxy of Polymarket's own API, and settlement comes from the chain. These are the rules that hold across the endpoints and the bulk archive.
Identifiers
Two different ids appear in this namespace and they are not interchangeable.
condition_id— a0x-prefixed hex string identifying a market. It is what/markets/{condition_id}and/settlements/{condition_id}take in the path.token_id— a long decimal string identifying one outcome token. A binary market has two, returned intoken_ids. It is what the per-token endpoints take:/market_stats,/ohlcv,/orderbookand/events.
Get both from /markets.
Recorded and catalog markets
/markets lists the markets Vike records, and the recorder discovers
active markets only, so that directory is a subset of every market Polymarket has listed.
/markets/{condition_id} answers beyond it: a market the
directory does not hold is looked up in Polymarket's full market catalog, and every row the route returns
carries source, "recorded" or "catalog". A catalog row has tick_size and neg_risk as null, because
the catalog has neither, and active/closed derived by the same rule as a recorded row. Whether a token
has a recorded tape is what /market_stats answers.
Settlement rows take their market fields (slug, token_ids, outcomes, question) from that same full
catalog, not from the recorded directory. market_known says whether the catalog has the market at all:
when it is false those fields are empty, and the on-chain fields are still exact.
Coverage: the recording starts 2026-07-25
The recorded book, which /events,
/orderbook, /market_stats
and /ohlcv read and the bulk archive
publishes, starts on 2026-07-25. The book event tape and the top-of-book quotes are not pruned, so that first
day stays available as new days are added.
When the request alone shows why an answer is empty, the response carries the reason in an X-Vike-Note
header and the body is unchanged:
| Request | X-Vike-Note |
|---|---|
/events, /orderbook or /ohlcv whose window ends (to_ms, ts, to) before 2026-07-25 | Polymarket L2 data starts 2026-07-25 |
/ohlcv with from after to (when to is also before 2026-07-25, only the 2026-07-25 note is sent) | from is after to |
/markets/{condition_id} or /settlements/{condition_id} with a value that is not 0x and 64 hex digits | condition_id is 0x followed by 64 hex digits; this value is not one |
Any other empty answer carries no header: an unknown id, a window outside one token's recorded span, a filter nothing matches.
Event rows
These notes are about the rows /events returns.
bids and asks are JSON-encoded strings, not arrays
The bids and asks fields hold JSON text, not arrays:
"bids": "[[0.782,0.0]]" must be
parsed a second time to become [[0.782, 0.0]], and an event that carries no book side sends the
empty string "" rather than null or [].
Unpopulated numeric fields are 0, not null
A zero in an event row is usually absent data. A price_change row carries
"best_ask": 0 and "best_bid": 0 — that is "this event type does not populate the field", not
"the best bid is zero". status is "" for the same reason. Read fields according to
event_type; do not treat a zero here as a price.
Book snapshots repeat rows and may lack a side
These notes are about /orderbook.
The same (side, level) can arrive several times — de-duplicate before summing size
The repeats are byte-identical rows, they are not extra depth, and a client that sums the raw array
overstates the book several-fold: de-duplicate on (side, level) first. The multiplier is
per-token and is not stable over time, so do not code against a fixed factor: twelve tokens
sampled in one minute on 2026-09-03 returned factors of 1 (no duplication at all), 2, 5, 9 and 11,
and the token that returned 6 copies per level on 2026-09-02 returned 9 the next day. Every row in
a response shares a single as_of_ts: the recorder writes several snapshot rows that carry the
same venue timestamp, and /orderbook returns all of them, so the repeats are one moment
fanned out, not successive ones. Read the depth of a side as its max(level), never as a row count.
A snapshot's rows come in a fixed order. Asks, when a snapshot has them, come first in the array (the
side column sorts alphabetically, ask before bid), then the bids, each ascending by level.
Do not assume a response contains both sides: group by side and handle an absent one, which
matters to any client computing a spread.
Top-of-book quotes
The l1_quotes stream is top of book derived from the recorded book: bid, ask, bid_size and
ask_size per token, one row per change. It is published as archive
files only; no endpoint returns its rows. The feed is event-driven, so a stretch with no rows means the top
of book did not move, not that recording stopped. Two trader routes price from it:
/wallets/{address} marks an open position at the token's
latest quote within the last seven days, and the mispricings of
/smart_money measure cohort entries against the current mid.
Settlement is chain truth
Settlement is chain truth; the market row may disagree
/settlements/{condition_id} is the on-chain resolution, not the market's own lifecycle flags, and the two agree in one
direction only. /markets/{condition_id}
derives closed from on-chain resolutions plus the venue's own closed flag, so a market with a settlement row
reads closed: 1 there. The reverse does not hold: a market the venue closed without the
chain ever resolving it — a cancellation or an oracle void — is closed: 1 in the directory and
[] from the settlements route. So [] means "the chain resolved
nothing", never "still open". For "how did this end", the settlement is the authority.
For many markets at once, /settlements/bulk returns the
same row, oldest resolution first, up to 5,000 to a page, filtered by resolution time (from_ms inclusive,
to_ms exclusive) and slug_prefix. Walk it by passing each page's next_cursor back as cursor until
has_more is false. A market with no known slug never matches a prefix, and a market the chain never
resolved is not listed.
The trader routes read the trade tape, not the book
/wallets/{address},
/smart_money and
/leaderboard run the same code as the MCP tools
polymarket_wallet, polymarket_smart_money and polymarket_leaderboard. Their volumes, trade counts
and P&L come from aggregates of the on-chain trade tape (wallet P&L, the smart-money cohort, the hourly
wallet leaderboard), not the recorded L2 book; only the open-position marks and the mispricing mids come
from the top of book. So a volume or a trade count from them is not comparable to one from /market_stats,
/ohlcv or /events. That tape starts on 2026-01-01, earlier than the recorded book.