Runnable scripts for the PolyOrderbooks API using the official Python SDK on PyPI — historical L2 order books, prices, and liquidity metrics for Polymarket.
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
export POLYORDERBOOKS_API_KEY="pob_..."Get a key at polyorderbooks.com/signup. The free tier covers
3 days of history at 250ms resolution, which is enough to run everything here.
| Script | What it shows |
|---|---|
historical_orderbooks.py |
Find a market, pull 6 hours of L2 books, print best bid/ask per bucket |
python historical_orderbooks.py2026-08-20T01:51:00Z Yes bid 0.056 ask 0.057
2026-08-20T01:52:00Z Yes bid 0.056 ask 0.057
2026-08-20T01:53:00Z Yes bid 0.056 ask 0.057
2026-08-20T01:51:00Z No bid 0.943 ask 0.944
2026-08-20T01:52:00Z No bid 0.943 ask 0.944
2026-08-20T01:53:00Z No bid 0.943 ask 0.944
Yes and No pay out 1 between them, which constrains the two books:
yes_ask + no_ask >= 1 buying both costs at least what the pair pays out
yes_bid + no_bid <= 1 selling both cannot raise more than the obligation
Both hold at every snapshot and make a useful check on any pipeline built against this data.
Note that yes_bid + no_ask is not fixed at 1. The two outcomes trade in separate order books,
so the gap between them moves with the spread on each side.
Each bucket in data[outcome] looks like this:
{
"t": "2026-08-20T01:51:00Z",
"bids": [[0.056, 12500.0], [0.055, 8000.0]],
"asks": [[0.057, 9800.0], [0.058, 15000.0]]
}bids and asks are [price, size] pairs, best first — highest bid, lowest ask. Prices are
probabilities in [0, 1]. Depth varies per bucket, so avoid assuming a fixed number of levels:
bids = point.get("bids") or []
best_bid = bids[0][0] if bids else NoneTop-of-book tells you the market was at 0.195. The ladder tells you whether you could actually trade there. If only 13.6 is resting at the best ask, filling 100 units walks the book and averages 0.2086 — about 4.3% worse. Backtests built on midpoints miss this entirely.
def cost_to_buy(point, size):
"""Average fill price walking the ask ladder."""
filled = cost = 0.0
for price, available in point.get("asks") or []:
take = min(available, size - filled)
cost += take * price
filled += take
if filled >= size:
return cost / filled
return None # not fillable at any price on this book- Timestamps must be ISO-8601 or unix seconds. Passing JavaScript-style milliseconds
(
Date.now()) is rejected — divide by 1000 first. - Reuse the same
start_ts,end_ts, andresolutionwhen followingnext_cursor. resolutionis required on history endpoints. Allowed:250ms,1s,60s,1m,5m,10m,15m,1h,6h,1d— the finest available depends on your plan.- Errors return
{"error", "message"}; branch onerror, showmessage.
- Documentation — endpoint reference and response shapes
- Quickstart — a working request in a minute
- Working with L2 order books
- Python SDK on PyPI —
pip install polyorderbooks - MCP server — the same data in Claude and Cursor
- n8n workflows — slippage against real ladders
- All free datasets — including a research dataset with a DOI
Open an issue here for anything about these examples.
For the API itself — a plan, a coverage question, a window these examples do not reach — email contact@polyorderbooks.com. Bug reports are welcome and usually answered the same day.
MIT. Not affiliated with, endorsed by, or connected to Polymarket.