AymarData

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:

  • market can be a family slug, full window slug, condition id, or token id
  • outcome selects 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 styleBest forCaution
Family slug (btc-updown-15m)Latest / monitoringMay resolve to the active window only
Full window slugHistorical backtestsPrefer this once discovered via /v1/markets
Token / condition idDebugging venue-native idsAlways 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
}
FieldMeaning
bidsBuy interest for this outcome, best first (highest price)
asksSell interest, best first (lowest price)
spreadBest ask − best bid when both sides exist
ts_exchangeExchange event time when available
ts_recvWhen AymarData received / reconstructed the view
token_idVenue token for this outcome book
outcomeNormalized 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:

  1. Fetch a book at t0
  2. Stream GET /v1/book/deltas from t0 to t1
  3. Apply updates in seq order; size: 0 removes 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/book without at
  • Sparse historical grid — GET /v1/book with at= every N seconds
  • Tick path / queue models — snapshot seed + /v1/book/deltas
  • Multi-day offline — /v1/files on 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) / 2 when 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

  1. Confirm the family appears on the Polymarket hub and that history_from covers your study window.
  2. Pull both outcomes at one timestamp and inspect complementarity; large gaps deserve a note, not silent averaging.
  3. Spot-check three timestamps against /v1/book/deltas reconstruction if you will later run path models.
  4. Verify settlement exists for labeled windows before training classifiers on resolution.
  5. Align at least one crypto reference series from Binance or Hyperliquid if the contract underwrites a crypto event.
  6. 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

  1. Mixing Up and Down sizes into one ladder
  2. Using last trade as “fair” when the book is one-sided
  3. Ignoring seq when applying deltas
  4. Comparing mids across venues without aligning clocks
  5. Treating spread alone as liquidity — size at the touch matters more for fill simulation
  6. Seeding replay with shallow depth then applying far-touch deltas
  7. Assuming family slugs always mean the window you intended historically
  8. Averaging Up and Down mids as if that recovers a fair without checking both ladders
  9. 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.