The data
One row is one second of one market. Simple to work with, honest about what happened.
Coverage
Symbols: BTC · ETH · SOL · XRP — each in 5-minute and 15-minute market lengths (8 streams total).
| Stream | Markets per day | Recording |
|---|---|---|
btc-5m · eth-5m · sol-5m · xrp-5m | 288 | 1 row per second, starts 60s before each market opens |
btc-15m · eth-15m · sol-15m · xrp-15m | 96 |
What's in each row
| Field | Meaning |
|---|---|
ts_unix | the second this row describes (UTC) |
slug | market name, e.g. btc-updown-5m-1793204700 |
phase | pre-open or live |
yes_bid / yes_ask (+ sizes) | best bid and ask on the YES side, with resting sizes |
no_bid / no_ask (+ sizes) | same for the NO side |
feed_age_ms | how fresh the data was when written |
window_start/end_unix | when the market opens and settles |
Empty cells mean no data was seen that second — we never make up values. Use feed_age_ms to decide how to handle them.
Market details file
Every market comes with a details file: market ID, both token IDs, the market question and settlement time — because Polymarket deletes these markets shortly after they settle.
Formats
Parquet files by month for bulk analysis, CSV and JSON through the API — the same columns everywhere.
Query it with DuckDB
SELECT slug, min(yes_bid), max(yes_bid), count(*)
FROM read_parquet('curated/v1/symbol=btc/timeframe=5m/**/*.parquet',
hive_partitioning=true)
WHERE ts_unix BETWEEN 1793200000 AND 1793300000
GROUP BY slug;