How Polymarket CLOB L2 books work in AymarData: bids, asks, outcomes, spreads, snapshots, deltas, and how to query them via API.
Polymarket order book data explained
Polymarket’s Central Limit Order Book (CLOB) quotes prices for each outcome token as probabilities between 0 and 1. AymarData records those L2 books so you can reconstruct depth at any timestamp for research, monitoring, and backtests. This guide explains fields, outcomes, snapshots vs deltas, and how to query them.
Browse live coverage on the Polymarket data hub. For definitions of depth, spread, and mid, see the glossary.
Outcomes and tokens
Binary markets expose two complementary outcomes (for example Up/Down or Yes/No). Each outcome has its own token id and its own book. When you call the API:
marketcan be a family slug, full window slug, condition id, or token idoutcomeselects which side of a two-sided market you want (up/down/yes/no)
Asking for Up without outcome is ambiguous when both tokens exist. Prefer explicit outcomes in production code.
export AYMARDATA_API_KEY=ad_live_YOUR_KEY
curl -s "https://api.aymardata.com/v1/book" \
-H "Authorization: Bearer $AYMARDATA_API_KEY" \
-G \
--data-urlencode "source=polymarket" \
--data-urlencode "market=btc-updown-15m" \
--data-urlencode "outcome=up" \
--data-urlencode "depth=6"
Choosing identifiers in practice
| Identifier style | Best for | Caution |
|---|---|---|
Family slug (btc-updown-15m) | Latest / monitoring | May resolve to the active window only |
| Full window slug | Historical backtests | Prefer this once discovered via /v1/markets |
| Token / condition id | Debugging venue-native ids | Always still pass outcome when books are two-sided |
Discover windows on family pages such as BTC Up/Down 15m or via GET /v1/markets.
Snapshot shape
A book response looks like:
{
"source": "polymarket",
"market": "btc-updown-15m-1792681200",
"token_id": "33339798372916037220…",
"outcome": "Up",
"ts_exchange": "2026-10-22T15:10:00.000Z",
"ts_recv": "2026-10-22T15:10:00.000Z",
"bids": [{"price": 0.52, "size": 18420}, {"price": 0.51, "size": 22100}],
"asks": [{"price": 0.53, "size": 15210}, {"price": 0.54, "size": 19840}],
"spread": 0.01
}
| Field | Meaning |
|---|---|
bids | Buy interest for this outcome, best first (highest price) |
asks | Sell interest, best first (lowest price) |
spread | Best ask − best bid when both sides exist |
ts_exchange | Exchange event time when available |
ts_recv | When AymarData received / reconstructed the view |
token_id | Venue token for this outcome book |
outcome | Normalized outcome label for the selected book |
Prices are typically in probability units. Sizes are in outcome-token units as recorded from the venue feed. See Data schemas for canonical field notes.
Empty sides and missing spread
If either side is empty, spread may be absent or null depending on normalization. Treat that as a data-quality or liquidity event: do not invent a mid. One-sided books are common near open, near resolution, or in thinly traded families. Downstream code should branch explicitly:
def safe_mid(book: dict) -> float | None:
if not book.get("bids") or not book.get("asks"):
return None
return (book["bids"][0]["price"] + book["asks"][0]["price"]) / 2
Depth and truncation
depth limits levels per side. Omit it (or set a high value within plan limits) for the full book when studying liquidity far from the touch. Top-of-book strategies often use depth=1 or depth=5; market-impact models need more.
import os, requests
API = "https://api.aymardata.com/v1"
H = {"Authorization": f"Bearer {os.environ['AYMARDATA_API_KEY']}"}
def top_of_book(market: str, outcome: str, at: str | None = None):
params = {"source": "polymarket", "market": market, "outcome": outcome, "depth": 1}
if at:
params["at"] = at
r = requests.get(f"{API}/book", headers=H, params=params, timeout=30)
r.raise_for_status()
b = r.json()
bid = b["bids"][0] if b["bids"] else None
ask = b["asks"][0] if b["asks"] else None
return bid, ask, b.get("spread")
Depth pitfalls for replay
If you later apply deltas on top of a truncated snapshot, levels outside the truncated range will not exist in your local maps and updates to those levels will look like inserts of incomplete state. For replay seeds, request enough depth for your model (often the full book within plan limits). See Order book deltas vs snapshots.
Snapshots vs deltas
AymarData stores checkpoints (periodic full snapshots) and deltas (incremental level updates). Point-in-time GET /v1/book?at= rebuilds from the nearest checkpoint plus applied deltas. For continuous replay:
- Fetch a book at
t0 - Stream
GET /v1/book/deltasfromt0tot1 - Apply updates in
seqorder;size: 0removes a level
def deltas(market: str, outcome: str, start: str, end: str):
r = requests.get(
f"{API}/book/deltas",
headers=H,
params={
"source": "polymarket",
"market": market,
"outcome": outcome,
"start": start,
"end": end,
},
timeout=60,
)
r.raise_for_status()
return r.json()["data"]
When to call which endpoint
- Latest monitoring —
GET /v1/bookwithoutat - Sparse historical grid —
GET /v1/bookwithat=every N seconds - Tick path / queue models — snapshot seed +
/v1/book/deltas - Multi-day offline —
/v1/fileson Pro/Enterprise (Bulk)
Spread, mid, and implied probability
- Spread — cost of crossing the book for one unit at the touch; wide spreads often mean thin liquidity or contested views.
- Mid —
(best_bid + best_ask) / 2when both sides exist; a common proxy for implied probability. - Microprice — size-weighted mid that tilts toward the heavier side; useful when one side dominates.
def microprice(book: dict) -> float | None:
if not book["bids"] or not book["asks"]:
return None
bid, ask = book["bids"][0], book["asks"][0]
denom = bid["size"] + ask["size"]
if denom <= 0:
return None
return (ask["price"] * bid["size"] + bid["price"] * ask["size"]) / denom
Complementary outcomes often trade near p_up + p_down ≈ 1 after fees, but deviations happen. Do not assume perfect complementarity when building arb monitors — check both books and fees. A monitor that only looks at Up mids will miss Down-side inventory and fee-adjusted edge.
How AymarData records Polymarket books
Collectors subscribe to venue book updates, normalize prices/sizes, assign sequence numbers, and write checkpoints on a schedule so historical at= queries stay efficient. Trades are stored separately via /v1/trades. Settlements land on /v1/settlement when markets resolve.
Coverage start dates appear on each market family page. Thin or newly listed families may be marked noindex until enough events accumulate — the API remains available for subscribers.
Common queries
Latest book for monitoring
curl -s "https://api.aymardata.com/v1/book" \
-H "Authorization: Bearer $AYMARDATA_API_KEY" \
-G \
--data-urlencode "source=polymarket" \
--data-urlencode "market=eth-updown-15m" \
--data-urlencode "outcome=down" \
--data-urlencode "depth=10"
Historical book for research
Add at=2026-10-22T14:30:00Z (ISO-8601 UTC). Prefer wall-clock times that fall inside the market window. Sampling before history_from or after resolution usually yields empty or unhelpful books.
Join with crypto reference
Pull Binance BTCUSDT or perp mark around the same timestamp to study Up/Down mid vs spot. Related guides: How to backtest Polymarket Up/Down and How to get Polymarket historical data.
Dual-outcome health check
def complementarity(market: str, at: str):
up = requests.get(
f"{API}/book", headers=H,
params={"source": "polymarket", "market": market, "outcome": "up", "at": at, "depth": 1},
timeout=30,
).json()
down = requests.get(
f"{API}/book", headers=H,
params={"source": "polymarket", "market": market, "outcome": "down", "at": at, "depth": 1},
timeout=30,
).json()
mu, md = safe_mid(up), safe_mid(down)
return {"up_mid": mu, "down_mid": md, "sum": None if mu is None or md is None else mu + md}
Sums far from 1.0 after fee buffers can indicate stale sides, wide markets, or transient dislocation worth investigating — not automatic arb.
Working with spreads under stress
Short-dated Up/Down windows often open with wide spreads and tighten as flow arrives, then widen again near resolution when inventory risk rises. A backtest that only samples mid-window mids will miss that shape. When you build features, record best bid size, best ask size, and depth-sum within N ticks of the touch in addition to mid and spread. Those size features explain more fill variance than spread alone.
If you compare Polymarket implied probability to a Binance-derived fair value, convert everything to the same units and the same clock. A 50ms receive skew is irrelevant for a 15-minute contract’s research signal, but it matters if you pretend to measure lead–lag at the touch. Document whether your notebook uses ts_exchange or ts_recv and stick to one choice per study.
Fee and rebate assumptions also change how you read a book. A one-cent spread can be round-trip expensive once venue fees and gas-adjacent costs (where relevant to your execution stack) are included. Keep fee parameters outside the data layer so the same AymarData pulls can support optimistic and conservative P&L views.
Research checklist before you trust a series
- Confirm the family appears on the Polymarket hub and that
history_fromcovers your study window. - Pull both outcomes at one timestamp and inspect complementarity; large gaps deserve a note, not silent averaging.
- Spot-check three timestamps against
/v1/book/deltasreconstruction if you will later run path models. - Verify settlement exists for labeled windows before training classifiers on resolution.
- Align at least one crypto reference series from Binance or Hyperliquid if the contract underwrites a crypto event.
- Write down depth, outcome, and fee assumptions in the notebook header so future you does not reverse-engineer them from charts.
Following that checklist takes minutes and prevents the most common “the API lied” false alarms, which usually turn out to be ambiguous market ids, missing outcomes, or shallow depth seeds.
Pitfalls
- Mixing Up and Down sizes into one ladder
- Using last trade as “fair” when the book is one-sided
- Ignoring
seqwhen applying deltas - Comparing mids across venues without aligning clocks
- Treating
spreadalone as liquidity — size at the touch matters more for fill simulation - Seeding replay with shallow
depththen applying far-touch deltas - Assuming family slugs always mean the window you intended historically
- Averaging Up and Down mids as if that recovers a fair without checking both ladders
- Ignoring that thin families may be API-available yet still sparse for microstructure work
FAQ
Is this the same as Polymarket’s public API?
AymarData normalizes and stores history for backtesting and agents. Venue APIs are great for live trading UIs; historical reconstruction and multi-source joins are why teams use a data vendor. Compare fairly in Polymarket API vs AymarData.
Can AI agents read books?
Yes — via REST or the MCP server. See Backtesting with an AI agent via MCP.
Where do trades and settlements fit?
Books describe quotes; /v1/trades describe prints; /v1/settlement labels the winner. Strategy P&L usually needs all three.
Next steps
Docs: book endpoint · Pricing · Polymarket data
Educational content, not trading or investment advice. Past data doesn't predict future results.