Bulk datasets

Tick-by-tick order books captured at sub-150ms intervals, with 100+ days of history. For multi-day or multi-asset pulls, skip the REST API and download Parquet files directly from Cloudflare R2. Free egress, no rate limit, same data as the live endpoints.

How it works

  1. List dataset day-rows with GET /v1/datasets — filter by token, timeframe, and date range.
  2. List a day's markets with GET /v1/datasets/{id}/slugs — each per-market Parquet comes back with a short-lived signed URL.
  3. Download a single market via GET /v1/datasets/{id}/slug/{slug} (302 redirect, or ?format=json for the URL), or use GET /v1/range to pull one slug across a date range.

File layout

Data is delivered as one Parquet file per market (per slug). Slugs are shaped <token>-updown-<5m|15m|4h>-<unixStartTs>, e.g. btc-updown-5m-1779234900. Each file contains trades, top-of-book (BBO), and 25-level depth together — there are no separate per-kind files.

  • Trades — every executed trade.
  • Top of book (BBO) — every best bid/offer update.
  • L25 depth — 25-level order book, stored as columns bid_price_0bid_price_24, bid_size_0bid_size_24, and the matching ask_price_* / ask_size_*. Included in the Parquet on both Free and Pro.

Two tiers are available: full (default — includes L25 depth) and topbook (compact: BBO + trades only, ~200× smaller). Select topbook with ?tier=topbook on the /slugs and /slug/{slug} endpoints. Topbook coverage can lag the full archive.

List datasets

GET/v1/datasets
FieldTypeDescription
tokenstringFilter by token — btc (Free) or eth (Pro).
timeframestringOne of 5m, 15m, 4h.
fromdateLower bound on the dataset's day.
todateUpper bound on the dataset's day.
limitnumberPage size for pagination.
offsetnumberRow offset for pagination.

Example

terminal
curl https://polyreplay.dev/api/v1/datasets \
  -H "Authorization: Bearer $POLYREPLAY_API_KEY" \
  -G \
  --data-urlencode "token=btc" \
  --data-urlencode "timeframe=4h" \
  --data-urlencode "from=2025-01-01" \
  --data-urlencode "to=2025-01-07"
200 OK
{
  "plan": "pro",
  "pagination": { "limit": 50, "offset": 0, "total": 7 },
  "filters": { "token": "btc", "timeframe": "4h", "from": "2025-01-01", "to": "2025-01-07" },
  "data": [
    {
      "id":              "8f2c3a1b-...",
      "token":           "btc",
      "timeframe":       "4h",
      "date":            "2025-01-01",
      "size_bytes":      12483920,
      "row_count":       184231,
      "market_count":    6,
      "row_count_trade": 41200,
      "row_count_bbo":   98031,
      "row_count_book25":45000,
      "window_pre_s":    900,
      "window_post_s":   300,
      "slugs_url":       "https://polyreplay.dev/api/v1/datasets/8f2c3a1b-.../slugs"
    }
  ]
}
Note
Day-rows are a Pro feature. On Free the data array is empty with pro_only: "day_files" and a free_policy object — grab individual markets through the /slugs and /slug/{slug} endpoints instead.

List a day's markets

GET/v1/datasets/{datasetId}/slugs

Returns every per-slug Parquet for that dataset day, each with a signed URL valid for 5 minutes. This listing does not consume the Free daily download cap. Add ?tier=topbook for the compact top-of-book variant.

terminal
curl https://polyreplay.dev/api/v1/datasets/8f2c3a1b-.../slugs \
  -H "Authorization: Bearer $POLYREPLAY_API_KEY"
200 OK
{
  "plan": "pro",
  "tier": "full",
  "dataset_id": "8f2c3a1b-...",
  "token": "btc",
  "timeframe": "5m",
  "date": "2026-02-01",
  "slug_count": 288,
  "expires_in_seconds": 300,
  "slugs": [
    {
      "slug":       "btc-updown-5m-1779346200",
      "size_bytes": 24576,
      "market_ts":  1779346200,
      "url":        "https://r2.polyreplay.dev/...signed..."
    }
  ]
}
Note
Pro keys list every market in any day. On Free, slugs outside the recent-window cap come back with url: null and locked: "pro".

Download a single market

GET/v1/datasets/{datasetId}/slug/{slug}

302-redirects straight to the per-slug Parquet on R2 (5-minute TTL), or add ?format=json to get the signed URL in JSON. Each call consumes one of the Free 150-downloads-per-24h budget. Add ?tier=topbookfor the compact variant (404 if that market hasn't been backfilled to topbook yet).

terminal
curl -L -o market.parquet \
  https://polyreplay.dev/api/v1/datasets/8f2c3a1b-.../slug/btc-updown-5m-1779346200 \
  -H "Authorization: Bearer $POLYREPLAY_API_KEY"

One slug across a date range

GET/v1/range

Signs the same slug across a span of days in one call. Required: token, timeframe, from, to (YYYY-MM-DD), plus format=slug-only and slug=<slug>. URLs are valid for 10 minutes; up to 60 dates per call. Free keys are clamped to the last 7 days.

terminal
curl https://polyreplay.dev/api/v1/range \
  -H "Authorization: Bearer $POLYREPLAY_API_KEY" \
  -G \
  --data-urlencode "token=btc" \
  --data-urlencode "timeframe=5m" \
  --data-urlencode "from=2026-02-01" \
  --data-urlencode "to=2026-02-28" \
  --data-urlencode "format=slug-only" \
  --data-urlencode "slug=btc-updown-5m-1779346200"
Note
Free keys are limited to BTC, recent-window caps (5m: 64, 15m: 32, 4h: 12 most-recent markets), 150 downloads per 24h, and /range clamped to the last 7 days. Pro adds ETH, full history, and unlimited downloads. L25 depth is in the Parquet on both plans.

Reading a file

Pandas, Polars, DuckDB — anything that speaks Parquet works directly.

python
import polars as pl

df = pl.read_parquet(url)
print(df.head())
# ts_us | market_id | asset_id | outcome | bid_price | bid_size | ask_price | ask_size

For the L25 snapshots, the 25 levels are stored as separate columns (bid_price_0bid_price_24, bid_size_0bid_size_24, same for asks). See the book reference for the level semantics.