Bulk archive
The full-L2 Polymarket Parquet archive — three streams, two Hive layouts, one UTC day per partition, with an open manifest and free samples that need no key.
The bulk archive is research-scale Polymarket history as Parquet, served as static files from
https://data.vike.io/archive/. It is the whole Polymarket universe — crypto up/down, sports,
politics — not a curated subset, and it is where full L2 book history is published in bulk:
every book event as recorded, rather than a window or an aggregate over one.
Read the download guidance before you point a tool at it
Archive downloads do not spend your hourly budget, but a Parquet reader pointed at an archive URL issues thousands of Range requests for one file, each a round trip over a bandwidth-capped connection. Download whole files and read them locally — Downloading is the page that shows how.
The three streams
Every partition holds up to three files, one per stream:
| Stream | What it is |
|---|---|
book_events.parquet | Full L2. Book snapshots, price_change deltas, trade prints, tick-size changes and status markers, in sequence. |
trades.parquet | The taker tape alone — the trade prints, without the book. |
l1_quotes.parquet | Derived top-of-book: bid, ask and both sizes, one row per change. |
trades and l1_quotes are derivable from book_events and are published separately because
they are two to three orders of magnitude smaller. If top-of-book is all you need, take
l1_quotes — a day of it is hundreds of megabytes against several gigabytes of full L2.
Two layouts
The archive is Hive-partitioned, and there are two partitionings of the same data. The manifest
carries both, under partitioning and family_partitioning.
By date — the primary layout, one UTC day per partition, every market in it:
/archive/venue=polymarket/date=YYYY-MM-DD/book_events.parquet
/archive/venue=polymarket/date=YYYY-MM-DD/trades.parquet
/archive/venue=polymarket/date=YYYY-MM-DD/l1_quotes.parquetBy family — an additive alternative that groups the crypto up/down families (asset × tenor) into their own much smaller files, so a single-family customer does not download a whole token-sorted day:
/archive/venue=polymarket/asset=btc/tenor=5m/date=YYYY-MM-DD/book_events.parquetAs of this writing the manifest lists asset values btc, eth, sol, xrp, doge, bnb, hype and
other, with tenor values 5m, 15m, 4h and other. asset=other/tenor=other is the
catch-all for every market that is not a recognised up/down family — sports, politics, one-off
events — so nothing is silently dropped by choosing this layout.
Two caveats. The manifest states the first outright: a family with zero rows in a stream on a
given day has no file for that stream, so a missing object is a legitimate answer, not an
error. The second is visible in the manifest's own entries: the family layout is additive, and
it does not currently cover every date the date= layout does. Enumerate families[] rather
than assuming a date exists in both.
Choosing a narrower file is the supported way to fetch less data. Reading a byte range out of a wide one is not — see Downloading.
The manifest is the authority
curl -s https://data.vike.io/archive/manifest.json -o manifest.jsonmanifest.json needs no key. It is regenerated daily and carries generated_at, both
partitioning patterns, the inline column schemas, the auth scheme, and one entry per partition
with byte size and row count. Freshness says what to trust.
As of generated_at 2026-09-02T00:38:00Z:
Coverage (date= layout) | 39 consecutive UTC days, 2026-07-25 through 2026-09-01, no gaps |
Coverage (asset=/tenor= layout) | 29 of those days, 2026-07-26 through 2026-09-01 |
| Total size | ~248 GB across all streams and dates |
book_events | 4.2–7.7 GB per day (median 5.8 GB); median ~364M rows/day |
l1_quotes | 328–669 MB per day (median 464 MB); median ~42.8M rows/day |
trades | 12–19 MB per day (median 14 MB); median ~1.14M rows/day |
| Largest family file | asset=other/tenor=other book events, ~2.9 GB/day median |
| Smallest family files | the 4h tenors, 1.6–11.5 MB/day median |
The three oldest partitions carry byte sizes but a null row count — treat rows as optional
when parsing, not as a guaranteed field.
Column schemas
The manifest carries these inline, under schema. They are spelled in the source store's own type
vocabulary — Decimal(p,s), Enum8(...), LowCardinality(String) — not in Parquet's, so what
your reader hands back does not always match the spelling character for character. The two places
where that matters are noted below.
book_events
| Column | Type | Notes |
|---|---|---|
token_id | String | The outcome token. A market has one per outcome. |
condition_id | String | The market. 0x-prefixed. |
ts | Int64 | Event time, epoch milliseconds UTC, as published. |
local_ts | Int64 | Recorder receive time, epoch ms. Never earlier than ts. |
seq | UInt64 | Feed sequence number. |
event_type | Enum8('book','price_change','trade','tick_size_change','status') | |
is_snapshot | UInt8 | 1 on a full-book row, 0 on a delta. |
side | Enum8('none','buy','sell') | |
price, size | Decimal(9,4), Decimal(18,6) | The changed level, on a delta. |
best_bid, best_ask | Decimal(9,4) | Present in the schema; use l1_quotes for top-of-book. |
bids, asks | String | JSON arrays of [price, size] pairs. |
tick_size | Decimal(9,4) | |
status | LowCardinality(String) | Empty on ordinary rows. |
Two things about that table are easy to get wrong, both checked against the current free sample:
bids/askscarry only the side that changed. On abooksnapshot row both hold the whole ladder. On aprice_changeonly the column matchingsideis populated and the other is an empty string —side = buyfillsbids,side = sellfillsasks.tradeandstatusrows populate neither.- The two
Enum8columns arrive as bytes, not strings. They are written as the Parquet ENUM logical type, sopyarrowtypes thembinaryand you readb'price_change', not'price_change'. Decode before comparing, or the filter silently matches nothing.
trades
token_id, condition_id (String), ts, local_ts (Int64), seq (UInt64), price,
size (Float64), side (String — buy or sell).
Note the type differences from book_events: trades carries price/size as Float64 and
side as a plain String — not a Decimal and an Enum8 — so the decoding note above does not
apply here. seq is 0 on every row of the current sample; order on ts, not on seq. Do not
assume one schema across streams.
l1_quotes
token_id, condition_id (String), ts, local_ts (Int64), bid, ask (Decimal(9,4)),
bid_size, ask_size (Decimal(18,6)).
Two properties worth relying on
Files are token-sorted. Rows for one token_id are contiguous and ascend by ts within that
run. That is what makes the family layout worth having, and it means a single-token extraction is
a contiguous scan once the file is local.
Recorder gaps are marked, not hidden: Freshness.
Free samples
/archive/samples/ needs no key and is a real slice of the full archive, not a synthetic or
truncated demo: one complete crypto up/down 5-minute market — the busiest of its kind that day by
book_events row count — with all three streams for both outcome tokens across the market's
entire lifecycle, open through resolution.
curl -s -O https://data.vike.io/archive/samples/README.txt
curl -s -O https://data.vike.io/archive/samples/book_events.parquet
curl -s -O https://data.vike.io/archive/samples/trades.parquet
curl -s -O https://data.vike.io/archive/samples/l1_quotes.parquetThe samples are regenerated daily from a recent real day, so the market slug, condition_id and
sizes change; README.txt names the market and date of the current one. Same column schema as the
full archive, at a few megabytes rather than a few gigabytes — the right place to write and debug
a parser before you create a key.
Authentication
Everything under /archive/ except manifest.json and /archive/samples/ needs a credential:
curl -H "X-API-Key: $VIKE_API_KEY" -o trades-2026-09-01.parquet \
"https://data.vike.io/archive/venue=polymarket/date=2026-09-01/trades.parquet"The manifest names the header as X-API-Key; the REST API's spec spells it X-API-KEY. HTTP
header names are case-insensitive, so these are the same header and the same key —
the same one that gates data.vike.io/v1 and MCP. Unlike those,
an archive download does not spend the hourly budget; it is recorded in
your usage view and nothing more. An unauthenticated request to a gated object is a 401, not a
404: the path resolved, the credential did not.
Gated responses carry Cache-Control: private, no-store, so the CDN never caches an
authenticated file — caching one would bypass both the gate and the usage record.
Follow the profitable cohort
See where the wallets with a record of realized profit put money in the last day, which of those markets they also bought below the mid, and whether the top market is still open.
Downloading
Archive downloads do not spend your hourly budget, but a Parquet reader pointed at a URL turns one file into thousands of slow, bandwidth-capped requests. Download whole files, read them locally.