Schema reference
Every dataset below has its fields, real rows drawn from responses the API actually returned, and loading code. The conventions they share come first. The full per-route reference lives at vike.io/docs.
Conventions
Timestamps
Every time is UTC. The spelling depends on the route — this is exactly what each one emits.
| Where | Field | Format |
|---|---|---|
| Hyperliquid rows — positions, fills, cohort metrics, liquidations | ts, snapshot_ts, liq_ts | ISO-8601 string with offset, 2026-10-12T09:00:00+00:00 |
Hyperliquid /ohlcv | ts (bar open) | epoch milliseconds |
Hyperliquid /symbols/hourly | ts | epoch seconds |
| Polymarket — API and archive | ts, local_ts, as_of_ts, first_ms, last_ms | epoch milliseconds. ts is the venue's event time, local_ts the recorder's receive time. |
| Query bounds | start / end (Hyperliquid), from_ms / to_ms (Polymarket /events) | ISO-8601 or epoch ms on Hyperliquid /ohlcv; epoch ms on /events. Start inclusive, end exclusive. |
Identifiers
| Field | Means |
|---|---|
token_id | Polymarket — one outcome token (a decimal string; keep it a string) |
condition_id | Polymarket — the market, 0x-prefixed. Joins the API to the archive. |
wallet · address | Hyperliquid and EVM wallets, 0x-prefixed hex |
symbol | Hyperliquid instrument, e.g. BTC, a HIP-3 market xyz:TSLA, a spot pair @107. Until 2026-11-19 responses also carry its old name (asset or coin) |
cohort | Hyperliquid cohort rung on the chosen axis; paths take its slug |
Paging envelopes
| Namespace | How it pages | Pass back |
|---|---|---|
/v1/hyperliquid list routes | Keyset. The response carries nextCursor (a string, or null). The cursor is opaque and bound to the query that issued it. | cursor=<nextCursor>, verbatim |
/v1/hyperliquid/ohlcv | No cursor — one page per request; move start / end. | — |
/v1/polymarket/events | An enveloped Polymarket route: {"events": [...], "has_more": …, "next_cursor": …}, at most 24 hours per call. | cursor=<next_cursor> while has_more is true |
/v1/polymarket/settlements/bulk | The other enveloped route: {"settlements": [...], "has_more": …, "next_cursor": …}, oldest resolution first. | cursor=<next_cursor> while has_more is true |
Other /v1/polymarket routes | A bare JSON array — except /archive/datasets, which returns {"datasets": [...], "limit": …, "offset": …, "total": …}, and /wallets/{address} and /archive/url, which each return one object. | offset on /archive/datasets |
/v1/onchain | Does not page: limit only, one page. | — |
A non-null nextCursor is emitted whenever a page comes back full, so the last page of an evenly divisible result hands you a cursor whose next fetch is empty: stop on null or an empty page. More on the API page.
Data-quality notes
- Two identical requests return identical bytes. Aggregates are snapped to a fixed precision before they leave the server, so a re-run of a query cannot drift in its last digits.
- A failure is never an empty 200. A query that could not run returns an error status, so an empty list means "no rows", not "the store was down".
- Cohort labels declare their basis. Graded Hyperliquid responses say whether a label is
point_in_time(as it was that hour) orcurrent(today's) —label_basison the envelope orpnl_cohort_basison the row. - Polymarket archive gaps are detectable. Archive events carry
seq, so a missing event shows up as a jump rather than silence;manifest.jsonlists every partition with its byte size and, where recorded, its row count. - Feed delay is measurable per event. Polymarket rows carry both the venue's
tsand the recorder'slocal_ts. - An unknown Polymarket id is an empty array, not a 404.
/markets/{condition_id}and/settlements/{condition_id}return[]for an id they do not know.
Position snapshots
Open Hyperliquid positions captured every 5 minutes — the latest snapshot, or history by window.
- Route
GET /v1/hyperliquid/positions- Cadence
- Snapshot every 5 minutes; archive one file per UTC day
- Coverage
- API: latest open positions (freshness-gated to the newest 15 minutes of snapshots) or historical 5-minute snapshots: pass start (and optionally end), and bound the read by symbol or wallet. Every row says which pnl-cohort label it carries (pnl_cohort_basis). Keyset pagination: nextCursor back as cursor, limit up to 5,000. Archive: positions.parquet per UTC day from 2026-05-08 (CC BY 4.0), every open position at every snapshot of the day, plus one file of the current open positions replaced every 5 minutes (date=latest). Each file comes as a temporary link from /archive/url; /archive/datasets lists the days and the file's own columns.
- Rows
Fields
| Field | Type | Description |
|---|---|---|
wallet | string | Account address, 0x-prefixed |
symbol | string | Symbol; HIP-3 DEX symbols carry their DEX prefix, e.g. xyz:SILVER |
direction | string | long | short |
size_token | number | Position size in coin units |
size_usd | number | Notional at mark, USD |
entry_price | number | Average entry price |
mark_price | number | Mark price at the snapshot |
liquidation_price | number | Liquidation price as reported; 0.0 when none is reported |
unrealized_pnl | number | Mark-to-market PnL, USD |
leverage | number | Position leverage as reported |
leverage_type | string | cross | isolated |
margin_used | number | Margin backing the position, USD |
size_cohort | string | Account-value rung, e.g. whale (size ladder) |
pnl_cohort | string | Realized-PnL percentile rung, or unknown under 10 closed trades |
pnl_abs_cohort | string | Realized-PnL dollar band, e.g. -$10k-$0 |
liq_progress | number | How far price has moved toward liquidation (0 = none); > 0.5 counts as at risk |
dex_name | string | HL, or the HIP-3 DEX the position is on |
snapshot_ts | string | Snapshot instant, ISO-8601 UTC |
pnl_cohort_basis | string | point_in_time | current — which label the row carries |
pnl_cohort_as_of | string|null | Hour the point-in-time label was computed at; null for current |
opened_at | string|null | When the position was first seen open, ISO-8601 UTC; null when unknown |
Real rows
Position snapshots · rows
Load it
curl -H "X-API-KEY: $VIKE_API_KEY" \
"https://data.vike.io/v1/hyperliquid/positions?symbol=BTC&limit=10"
# Full history is the Parquet archive, one file per UTC day (any day /archive/datasets
# lists; date=latest is the current open positions, replaced every 5 minutes).
# Ask for a link to the file, then download it: the link needs no key and is
# valid 7 days (expires_in in the answer).
DAY=YYYY-MM-DD
URL=$(curl -s -H "X-API-KEY: $VIKE_API_KEY" \
"https://data.vike.io/v1/hyperliquid/archive/url?dataset=positions&date=$DAY" | jq -r '.url')
curl -f -o positions.parquet "$URL"
# Or the free sample, no key:
curl -O https://data.vike.io/archive/hl-samples/positions-sample.parquet
import os, requests
import pyarrow.parquet as pq
H = {"X-API-KEY": os.environ["VIKE_API_KEY"]}
day = "YYYY-MM-DD" # any day https://data.vike.io/v1/hyperliquid/archive/datasets lists
ask = requests.get("https://data.vike.io/v1/hyperliquid/archive/url",
params={"dataset": "positions", "date": day}, headers=H, timeout=30)
ask.raise_for_status()
# The link needs no key and is valid 7 days (expires_in): download the file,
# then read it locally.
with requests.get(ask.json()["url"], stream=True, timeout=600) as r:
r.raise_for_status()
with open("positions.parquet", "wb") as f:
for chunk in r.iter_content(1 << 20):
f.write(chunk)
table = pq.read_table("positions.parquet")
print(table.num_rows, table.schema.names)
import polars as pl
df = pl.read_parquet("positions.parquet") # downloaded first — see the cURL tab
last = df.filter(pl.col("snapshot_ts") == pl.col("snapshot_ts").max())
bias = (
last.group_by("asset")
.agg((pl.col("size_usd") * pl.col("direction")).sum().alias("net_usd"),
pl.col("size_usd").sum().alias("open_usd"))
.sort("open_usd", descending=True)
)
print(bias.head())
-- positions.parquet downloaded first (see the cURL tab), then read locally
SELECT asset,
sum(size_usd * direction) AS net_usd,
sum(size_usd) AS open_usd
FROM 'positions.parquet'
WHERE snapshot_ts = (SELECT max(snapshot_ts) FROM 'positions.parquet')
GROUP BY asset
ORDER BY open_usd DESC
LIMIT 10;
A link is valid 7 days for any number of requests, so a Parquet reader may read byte ranges straight from it; for repeated queries, download the file once and read it locally. Asking for a link is one ordinary request; the download needs no key, is not counted, and has no quota. Archive guide →
Full reference for /v1/hyperliquid/positions → · back to the catalog
Fills
Per-wallet fills with side, price, notional, fee, maker flag and closed PnL.
- Route
GET /v1/hyperliquid/wallets/{address}/fills- Cadence
- Continuous; archive one file per UTC day
- Coverage
- API: a wallet's fills, newest first, keyset-paged on (fill_ts, trade_id) — pass nextCursor back as cursor, limit up to 5,000; filter by symbol, side, start and end. Builder-routed fills have their own route, `/builders/{builder}/fills`. Archive: fills.parquet per UTC day from 2026-05-01 (CC BY 4.0), every fill of every wallet. Each file comes as a temporary link from /archive/url; /archive/datasets lists the days and the file's own columns.
- Rows
Fields
| Field | Type | Description |
|---|---|---|
ts | string | Fill time, ISO-8601 UTC with milliseconds |
symbol | string | Symbol; HIP-3 DEX symbols carry their DEX prefix, spot pairs are HL's @index |
side | string | buy | sell |
direction | string | buy | sell (mirrors side) |
size_token | number | Size in coin units |
price | number | Fill price |
size_usd | number | Notional, USD |
fee | number | Fee paid in fee_token; negative is a rebate |
fee_token | string | Currency the fee is paid in |
is_maker | boolean | Maker (true) or taker (false) |
crossed | boolean | The order crossed the book |
closed_pnl | number | Realized PnL closed by this fill, USD |
order_id | integer | Venue order id |
trade_id | integer | Venue trade id |
dex_name | string | HL, or the HIP-3 DEX |
Real rows
Fills · rows
Load it
# replace each {placeholder} with your own value
curl -H "X-API-KEY: $VIKE_API_KEY" \
"https://data.vike.io/v1/hyperliquid/wallets/{address}/fills?limit=50"
# Full history is the Parquet archive, one file per UTC day (any day /archive/datasets
# lists).
# Ask for a link to the file, then download it: the link needs no key and is
# valid 7 days (expires_in in the answer).
DAY=YYYY-MM-DD
URL=$(curl -s -H "X-API-KEY: $VIKE_API_KEY" \
"https://data.vike.io/v1/hyperliquid/archive/url?dataset=fills&date=$DAY" | jq -r '.url')
curl -f -o fills.parquet "$URL"
# Or the free sample, no key:
curl -O https://data.vike.io/archive/hl-samples/fills-sample.parquet
import os, requests
import pyarrow.parquet as pq
H = {"X-API-KEY": os.environ["VIKE_API_KEY"]}
day = "YYYY-MM-DD" # any day https://data.vike.io/v1/hyperliquid/archive/datasets lists
ask = requests.get("https://data.vike.io/v1/hyperliquid/archive/url",
params={"dataset": "fills", "date": day}, headers=H, timeout=30)
ask.raise_for_status()
# The link needs no key and is valid 7 days (expires_in): download the file,
# then read it locally.
with requests.get(ask.json()["url"], stream=True, timeout=600) as r:
r.raise_for_status()
with open("fills.parquet", "wb") as f:
for chunk in r.iter_content(1 << 20):
f.write(chunk)
table = pq.read_table("fills.parquet")
print(table.num_rows, table.schema.names)
import polars as pl
df = pl.read_parquet("fills.parquet") # downloaded first — see the cURL tab
# both wallets of a trade are rows: the taker side (crossed) counts a trade once
by_asset = (
df.group_by("asset")
.agg(pl.col("size_usd").filter(pl.col("crossed")).sum().alias("taker_volume_usd"),
pl.len().alias("fills"))
.sort("taker_volume_usd", descending=True)
)
print(by_asset.head())
-- fills.parquet downloaded first (see the cURL tab), then read locally
-- both wallets of a trade are rows: the taker side (crossed) counts a trade once
SELECT asset,
count(*) AS fills,
sum(size_usd) FILTER (WHERE crossed) AS taker_volume_usd
FROM 'fills.parquet'
GROUP BY asset
ORDER BY taker_volume_usd DESC
LIMIT 10;
A link is valid 7 days for any number of requests, so a Parquet reader may read byte ranges straight from it; for repeated queries, download the file once and read it locally. Asking for a link is one ordinary request; the download needs no key, is not counted, and has no quota. Archive guide →
Full reference for /v1/hyperliquid/wallets/{address}/fills → · back to the catalog
Candles
Hyperliquid OHLCV built from executed fills — time bars and N-trade tick bars.
- Route
GET /v1/hyperliquid/ohlcv- Cadence
- Bars close every interval; closed bars also stream
- Coverage
- Time bars 1m 5m 15m 30m 1h 4h 1d from fill rollups (no TTL); tick bars (Nt, e.g. 15t = 15 trades) walked from raw fills, kept without expiry back to 2025-12-31. market=perp | spot; HL core plus the HIP-3 DEXs (dex=XYZ, CASH, HYNA, KM, FLX, VNTL, PARA, MKTS). Closed bars stream on wss://data.vike.io/v1/stream; /ohlcv/export streams a window as NDJSON.
- Rows
Fields
| Field | Type | Description |
|---|---|---|
ts | integer | Bar open time, epoch milliseconds UTC |
open | number | First fill price in the bar |
high | number | Highest fill price |
low | number | Lowest fill price |
close | number | Last fill price in the bar |
volume | number | Volume in coin units |
volume_usd | number | Volume, USD |
n_trades | integer | Fills in the bar |
source | string | Envelope: the data that answered — 1m fills rollup, 1h fills rollup, or fills for tick bars |
Real rows
Candles · rows
Load it
curl -H "X-API-KEY: $VIKE_API_KEY" \
"https://data.vike.io/v1/hyperliquid/ohlcv?symbol=BTC&interval=1m&market=perp&limit=48"
import os, requests
r = requests.get(
"https://data.vike.io/v1/hyperliquid/ohlcv?symbol=BTC&interval=1m&market=perp&limit=48",
headers={"X-API-KEY": os.environ["VIKE_API_KEY"]},
timeout=30,
)
r.raise_for_status()
body = r.json()
import os, requests
import polars as pl
body = requests.get(
"https://data.vike.io/v1/hyperliquid/ohlcv?symbol=BTC&interval=1m&market=perp&limit=48",
headers={"X-API-KEY": os.environ["VIKE_API_KEY"]},
timeout=30,
).json()
rows = body if isinstance(body, list) else next(v for v in body.values() if isinstance(v, list))
df = pl.DataFrame(rows)
print(df.head())
Full reference for /v1/hyperliquid/ohlcv → · back to the catalog
Cohort metrics
One coin's positioning across every cohort of a ladder, hourly — up to a year in one request.
- Route
GET /v1/hl-cohorts/symbols/{symbol}/cohort-metrics- Cadence
- Hourly buckets
- Coverage
- One coin, every cohort on an axis (size, tier, pnl, pnl_abs, pnl_unrealized), up to 8,760 hourly buckets per request, paged by hour. axis=size, axis=tier and axis=pnl_unrealized are always point_in_time; axis=pnl takes labelBasis=point_in_time (each hour's label as it was then — point-in-time labels from 2026-04-07) or current; every response declares which in label_basis and label_computed. /segments publishes every ladder's bounds.
- Rows
Fields
| Field | Type | Description |
|---|---|---|
ts | string | Hour bucket, ISO-8601 UTC |
cohort | string | Rung on the chosen axis, e.g. whale, 3x smart, $1m-$2.5m |
position_count | integer | Open positions in the cohort |
position_count_long | integer | …of which long |
total_position_value | number | Notional, USD |
total_position_value_long | number | Long notional, USD |
total_unrealized_pnl | number | Unrealized PnL, USD |
total_position_size | number | Size in coin units |
total_position_size_long | number | Long size in coin units |
bias | number | (long − short) / (long + short), by notional |
label_basis | string | point_in_time | current — the basis actually used |
grading | string | On axis=pnl rows: which PnL grading labelled the wallet (realized by default) |
Real rows
Cohort metrics · rows
Load it
curl -H "X-API-KEY: $VIKE_API_KEY" \
"https://data.vike.io/v1/hl-cohorts/symbols/BTC/cohort-metrics?axis=size&hours=1"
import os, requests
r = requests.get(
"https://data.vike.io/v1/hl-cohorts/symbols/BTC/cohort-metrics?axis=size&hours=1",
headers={"X-API-KEY": os.environ["VIKE_API_KEY"]},
timeout=30,
)
r.raise_for_status()
body = r.json()
# list routes page by keyset: pass body["nextCursor"] back as cursor=… until it is null
import os, requests
import polars as pl
body = requests.get(
"https://data.vike.io/v1/hl-cohorts/symbols/BTC/cohort-metrics?axis=size&hours=1",
headers={"X-API-KEY": os.environ["VIKE_API_KEY"]},
timeout=30,
).json()
rows = body if isinstance(body, list) else next(v for v in body.values() if isinstance(v, list))
df = pl.DataFrame(rows)
print(df.head())
Full reference for /v1/hl-cohorts/symbols/{symbol}/cohort-metrics → · back to the catalog
Liquidations
Liquidation events, and each coin's open interest sitting close to liquidation.
- Route
GET /v1/hyperliquid/liquidations- Cadence
- Seconds after the liquidation fill; inferred between snapshots before 2026-08-03
- Coverage
- Liquidation events are exact, from the node's fills since 2026-08-03: the liquidated wallet's own fill, at its millisecond, with the venue's mark price. Older rows are inferred from position snapshots: a position that vanished between two snapshots while its last mark was within 5% of its liquidation price. Newest first (keyset-paged, limit up to 5,000; filter by symbol, wallet, direction, start, end), each with its point-in-time pnl cohort where the hourly label table covers that hour (pnl_cohort_basis says which). /liquidations/risk ranks coins by the share of notional within half its liquidation buffer; `/symbols/{symbol}/liquidation-heatmap` bins where open positions would liquidate.
- Rows
Fields
| Field | Type | Description |
|---|---|---|
liq_ts | string | Liquidation time, ISO-8601 UTC |
wallet | string | Liquidated account |
symbol | string | Symbol; HIP-3 DEX symbols carry their DEX prefix, spot pairs are HL's @index |
side | string | long | short — the side that was closed |
size_token | number | Size liquidated, coin units |
size_usd | number | Notional liquidated, USD |
mark_price | number | Mark price at liquidation |
size_cohort | string | Account-value rung |
pnl_cohort | string | Realized-PnL rung at the liquidation's hour, or unknown |
pnl_cohort_basis | string | point_in_time | current — which label the row carries |
Real rows
Liquidations · rows
Load it
curl -H "X-API-KEY: $VIKE_API_KEY" \
"https://data.vike.io/v1/hyperliquid/liquidations?limit=20"
import os, requests
r = requests.get(
"https://data.vike.io/v1/hyperliquid/liquidations?limit=20",
headers={"X-API-KEY": os.environ["VIKE_API_KEY"]},
timeout=30,
)
r.raise_for_status()
body = r.json()
# list routes page by keyset: pass body["nextCursor"] back as cursor=… until it is null
import os, requests
import polars as pl
body = requests.get(
"https://data.vike.io/v1/hyperliquid/liquidations?limit=20",
headers={"X-API-KEY": os.environ["VIKE_API_KEY"]},
timeout=30,
).json()
rows = body if isinstance(body, list) else next(v for v in body.values() if isinstance(v, list))
df = pl.DataFrame(rows)
print(df.head())
Full reference for /v1/hyperliquid/liquidations → · back to the catalog
Order-book events
The full L2 tape — book snapshots, price_change deltas, trades, tick-size and status markers.
- Route
GET /v1/polymarket/events- Cadence
- Continuous; archive one partition per UTC day
- Coverage
- API: /events returns up to 24 hours per call and up to 50,000 events per page (next_cursor + has_more), from 2026-07-25; /orderbook returns the nearest book snapshot at or before any instant from then on, depth up to 25. Archive: book_events.parquet per UTC day from 2026-07-25 (CC BY 4.0), which also carries condition_id and tick_size.
- Rows
Fields
| Field | Type | Description |
|---|---|---|
token_id | string | The outcome token (ERC-1155 id) |
ts | integer | Event time, epoch milliseconds |
local_ts | integer | Receive time, epoch milliseconds |
seq | integer | Feed sequence number |
event_type | string | book | price_change | trade | tick_size_change | status |
is_snapshot | integer | 1 on a full-book row, 0 otherwise |
side | string | none | buy | sell |
price | number | The changed level (price_change) or the print (trade) |
size | number | Shares at that level, or traded |
best_bid | number | Top of book as recorded on the event; 0.0 when not set |
best_ask | number | Top of book as recorded on the event; 0.0 when not set |
bids | string | JSON [[price, size], …] carried as a string; empty when absent |
asks | string | JSON [[price, size], …] carried as a string; empty when absent |
status | string | Marker on status rows, e.g. gap_start; empty otherwise |
Real rows
Order-book events · rows
Load it
# replace each {placeholder} with your own value
curl -H "X-API-KEY: $VIKE_API_KEY" \
"https://data.vike.io/v1/polymarket/events?token_id={token_id}&from_ms={from_ms}&to_ms={to_ms}&event_type=price_change&limit=50"
# Full history is the Parquet archive. Pick a date from manifest.json (no key needed),
# download the whole file, then read it locally:
DAY=YYYY-MM-DD
curl -H "X-API-KEY: $VIKE_API_KEY" -O \
"https://data.vike.io/archive/venue=polymarket/date=$DAY/book_events.parquet"
# Or the free sample — one complete market, no key:
curl -O https://data.vike.io/archive/samples/book_events.parquet
import os, requests
import pyarrow.parquet as pq
H = {"X-API-KEY": os.environ["VIKE_API_KEY"]}
day = "YYYY-MM-DD" # any date listed in https://data.vike.io/archive/manifest.json
url = f"https://data.vike.io/archive/venue=polymarket/date={day}/book_events.parquet"
# Download the whole file once, then read it locally — a remote Parquet
# reader re-fetches the footer and column chunks on every query.
with requests.get(url, headers=H, stream=True, timeout=600) as r:
r.raise_for_status()
with open("book_events.parquet", "wb") as f:
for chunk in r.iter_content(1 << 20):
f.write(chunk)
table = pq.read_table("book_events.parquet")
print(table.num_rows, table.schema.names)
import polars as pl
df = pl.read_parquet("book_events.parquet") # downloaded first — see the cURL tab
df = df.with_columns(pl.from_epoch("ts", time_unit="ms").alias("time"))
print(df.group_by("event_type").len().sort("len", descending=True))
-- book_events.parquet downloaded first (see the cURL tab), then read locally
SELECT event_type,
count(*) AS events,
count(DISTINCT token_id) AS tokens
FROM 'book_events.parquet'
GROUP BY event_type
ORDER BY events DESC;
Download whole files, then read them locally — do not point a Parquet reader at the remote URL. Archive downloads need a key but do not count against the hourly limit.
Full reference for /v1/polymarket/events → · back to the catalog
Trades
The taker tape alone — trade prints without the book.
- Route
GET /v1/polymarket/events- Cadence
- Continuous; archive one partition per UTC day
- Coverage
- API: /events?event_type=trade, up to 24 hours per call, from 2026-07-25. Archive: trades.parquet per UTC day from 2026-07-25 (CC BY 4.0) — the same prints, with condition_id, and price/size as Float64.
- Rows
Fields
| Field | Type | Description |
|---|---|---|
token_id | string | The outcome token |
ts | integer | Trade time, epoch milliseconds |
local_ts | integer | Receive time, epoch milliseconds |
seq | integer | Feed sequence number; order trades on ts |
event_type | string | trade |
side | string | buy | sell |
price | number | Outcome price, 0–1 |
size | number | Shares traded |
Real rows
Trades · rows
Load it
# replace each {placeholder} with your own value
curl -H "X-API-KEY: $VIKE_API_KEY" \
"https://data.vike.io/v1/polymarket/events?token_id={token_id}&from_ms={from_ms}&to_ms={to_ms}&event_type=trade&limit=50"
# Full history is the Parquet archive. Pick a date from manifest.json (no key needed),
# download the whole file, then read it locally:
DAY=YYYY-MM-DD
curl -H "X-API-KEY: $VIKE_API_KEY" -O \
"https://data.vike.io/archive/venue=polymarket/date=$DAY/trades.parquet"
# Or the free sample — one complete market, no key:
curl -O https://data.vike.io/archive/samples/trades.parquet
import os, requests
import pyarrow.parquet as pq
H = {"X-API-KEY": os.environ["VIKE_API_KEY"]}
day = "YYYY-MM-DD" # any date listed in https://data.vike.io/archive/manifest.json
url = f"https://data.vike.io/archive/venue=polymarket/date={day}/trades.parquet"
# Download the whole file once, then read it locally — a remote Parquet
# reader re-fetches the footer and column chunks on every query.
with requests.get(url, headers=H, stream=True, timeout=600) as r:
r.raise_for_status()
with open("trades.parquet", "wb") as f:
for chunk in r.iter_content(1 << 20):
f.write(chunk)
table = pq.read_table("trades.parquet")
print(table.num_rows, table.schema.names)
import polars as pl
df = pl.read_parquet("trades.parquet") # downloaded first — see the cURL tab
df = df.with_columns(pl.from_epoch("ts", time_unit="ms").alias("time"))
vwap = (
df.group_by("token_id")
.agg(((pl.col("price") * pl.col("size")).sum() / pl.col("size").sum()).alias("vwap"),
pl.len().alias("prints"))
.sort("prints", descending=True)
)
print(vwap.head())
-- trades.parquet downloaded first (see the cURL tab), then read locally
SELECT token_id,
count(*) AS prints,
sum(size) AS shares,
sum(price * size) / sum(size) AS vwap,
epoch_ms(min(ts)) AS first_print
FROM 'trades.parquet'
GROUP BY token_id
ORDER BY prints DESC
LIMIT 10;
Download whole files, then read them locally — do not point a Parquet reader at the remote URL. Archive downloads need a key but do not count against the hourly limit.
Full reference for /v1/polymarket/events → · back to the catalog
Market trades
One market's on-chain trades with the wallet behind each, newest first.
- Route
GET /v1/polymarket/markets/{condition_id}/trades- Cadence
- Continuous; the newest trades are a few minutes behind the chain
- Coverage
- API: one market's trades, newest first, keyset-paged — pass next_cursor back as cursor, limit up to 10,000 (the MCP tool: 200); from_ms and to_ms bound the window. From 2026-01-01 to a few minutes ago. A row is one order's fill, so a match is several rows (role maker | taker): never add both roles' volume. A few percent of fills are merged away. The recorded order book, which has no wallets, is the Order-book events dataset.
- Rows
Fields
| Field | Type | Description |
|---|---|---|
ts | integer | The block's time, epoch milliseconds (whole seconds) |
tx_hash | string | Transaction hash, 0x-prefixed |
log_index | integer | Position of the fill in the block; orders the fills of one transaction |
block_number | integer | Polygon block |
proxy_wallet | string | The Polymarket proxy wallet that placed the order, 0x-prefixed |
name | string | Polymarket display name; empty when unset |
pseudonym | string | Polymarket-generated pseudonym; empty when unset |
side | string | BUY | SELL of the outcome token |
role | string | maker (a resting order that was hit) | taker (the order that hit it) |
outcome | string | The outcome this token is, e.g. Up |
outcome_index | integer | Position of the outcome in the market's outcome list |
token_id | string | The outcome token traded |
size | number | Shares traded |
price | number | Outcome price, 0–1 |
Real rows
Market trades · rows
Load it
# replace each {placeholder} with your own value
curl -H "X-API-KEY: $VIKE_API_KEY" \
"https://data.vike.io/v1/polymarket/markets/{condition_id}/trades?limit=50"
import os, requests
r = requests.get(
"https://data.vike.io/v1/polymarket/markets/{condition_id}/trades?limit=50",
headers={"X-API-KEY": os.environ["VIKE_API_KEY"]},
timeout=30,
)
r.raise_for_status()
body = r.json()
import os, requests
import polars as pl
body = requests.get(
"https://data.vike.io/v1/polymarket/markets/{condition_id}/trades?limit=50",
headers={"X-API-KEY": os.environ["VIKE_API_KEY"]},
timeout=30,
).json()
rows = body if isinstance(body, list) else next(v for v in body.values() if isinstance(v, list))
df = pl.DataFrame(rows)
print(df.head())
Full reference for /v1/polymarket/markets/{condition_id}/trades → · back to the catalog
Top-of-book quotes
Derived top of book — bid, ask and both sizes, one row per change.
- Route
GET /v1/polymarket/archive/url- Cadence
- One partition per UTC day
- Coverage
- Archive only: l1_quotes.parquet per UTC day from 2026-07-25 (CC BY 4.0), derived from book_events and published separately because it is far smaller. Resolve a file with /archive/url (redirect=1 for a 302); /archive/datasets lists every partition with its size and, where recorded, its row count.
- Rows
Fields
| Field | Type | Description |
|---|---|---|
token_id | String | The outcome token |
condition_id | String | The market, 0x-prefixed |
ts | Int64 | Quote time, epoch milliseconds |
local_ts | Int64 | Receive time, epoch milliseconds |
bid | Decimal(9, 4) | Best bid |
ask | Decimal(9, 4) | Best ask |
bid_size | Decimal(18, 6) | Shares at the best bid |
ask_size | Decimal(18, 6) | Shares at the best ask |
Real rows
Top-of-book quotes · rows
Load it
curl -H "X-API-KEY: $VIKE_API_KEY" \
"https://data.vike.io/v1/polymarket/archive/datasets?limit=1000"
# Full history is the Parquet archive. Pick a date from manifest.json (no key needed),
# download the whole file, then read it locally:
DAY=YYYY-MM-DD
curl -H "X-API-KEY: $VIKE_API_KEY" -O \
"https://data.vike.io/archive/venue=polymarket/date=$DAY/l1_quotes.parquet"
# Or the free sample — one complete market, no key:
curl -O https://data.vike.io/archive/samples/l1_quotes.parquet
import os, requests
import pyarrow.parquet as pq
H = {"X-API-KEY": os.environ["VIKE_API_KEY"]}
day = "YYYY-MM-DD" # any date listed in https://data.vike.io/archive/manifest.json
url = f"https://data.vike.io/archive/venue=polymarket/date={day}/l1_quotes.parquet"
# Download the whole file once, then read it locally — a remote Parquet
# reader re-fetches the footer and column chunks on every query.
with requests.get(url, headers=H, stream=True, timeout=600) as r:
r.raise_for_status()
with open("l1_quotes.parquet", "wb") as f:
for chunk in r.iter_content(1 << 20):
f.write(chunk)
table = pq.read_table("l1_quotes.parquet")
print(table.num_rows, table.schema.names)
import polars as pl
df = pl.read_parquet("l1_quotes.parquet") # downloaded first — see the cURL tab
df = df.with_columns(pl.from_epoch("ts", time_unit="ms").alias("time"))
spread = (
df.with_columns((pl.col("ask") - pl.col("bid")).alias("spread"))
.group_by("token_id")
.agg(pl.len().alias("changes"), pl.col("spread").mean().alias("avg_spread"))
.sort("changes", descending=True)
)
print(spread.head())
-- l1_quotes.parquet downloaded first (see the cURL tab), then read locally
SELECT token_id,
count(*) AS changes,
avg(ask - bid) AS avg_spread,
epoch_ms(min(ts)) AS first_quote,
epoch_ms(max(ts)) AS last_quote
FROM 'l1_quotes.parquet'
GROUP BY token_id
ORDER BY changes DESC
LIMIT 10;
Download whole files, then read them locally — do not point a Parquet reader at the remote URL. Archive downloads need a key but do not count against the hourly limit.
Full reference for /v1/polymarket/archive/url → · back to the catalog
Markets & settlements
Market search, one-market detail, and the on-chain resolution per condition.
- Route
GET /v1/polymarket/markets- Cadence
- Kept by the market recorder; state derived per request
- Coverage
- Recorded markets with derived state: closed when the chain resolved the condition OR Polymarket's own feed closed it. /markets filters by slug substring and active | closed (never both), newest end_date first, limit up to 100. `/markets/{condition_id}` answers a market outside the recorded set from Polymarket's full market catalog; its `source` says which list answered. `/settlements/{condition_id}` is the on-chain resolution — [] means the chain resolved nothing, not that the market is open; `/settlements/bulk` pages every settlement oldest resolution first (filter by resolution time and slug prefix, up to 5,000 a page, next_cursor + has_more) from pages rebuilt every 5 minutes, so a newly resolved market can appear there up to about 5 minutes after `/settlements/{condition_id}` shows it. Candles per token: /ohlcv.
- Rows
Fields
| Field | Type | Description |
|---|---|---|
condition_id | string | The market, 0x-prefixed |
slug | string | URL slug |
active | integer | 1 when not closed, else 0 |
closed | integer | 1 when resolved on chain or closed by the venue |
end_date | integer | Scheduled end, epoch milliseconds |
neg_risk | integer|null | 1 for a neg-risk market; null on a catalog row |
tick_size | number|null | Price increment; null on a catalog row |
token_ids | array | One outcome token id per outcome |
Real rows
Markets & settlements · rows
Load it
curl -H "X-API-KEY: $VIKE_API_KEY" \
"https://data.vike.io/v1/polymarket/markets?active=true&limit=25"
import os, requests
r = requests.get(
"https://data.vike.io/v1/polymarket/markets?active=true&limit=25",
headers={"X-API-KEY": os.environ["VIKE_API_KEY"]},
timeout=30,
)
r.raise_for_status()
body = r.json()
import os, requests
import polars as pl
body = requests.get(
"https://data.vike.io/v1/polymarket/markets?active=true&limit=25",
headers={"X-API-KEY": os.environ["VIKE_API_KEY"]},
timeout=30,
).json()
rows = body if isinstance(body, list) else next(v for v in body.values() if isinstance(v, list))
df = pl.DataFrame(rows)
print(df.head())
Full reference for /v1/polymarket/markets → · back to the catalog
Token transfers
Who bought and who sold a token over a period, aggregated from raw transfers.
- Route
GET /v1/onchain/tokens/{token_address}/transfers- Cadence
- Synced block by block
- Coverage
- Per-token buyer and seller totals over a period (period, tab=bought|sold, chain) on Ethereum, BNB Chain, Base and HyperEVM — chain=ethereum|bsc|base|hyperevm is a filter, not a namespace. Per-wallet transfer lists at `/wallets/{address}/transactions` cover the same four chains. Amount and USD fields on this route are whole tokens and plain USD (they were integers scaled by 10^6 until 2026-10-13).
- Rows
Fields
| Field | Type | Description |
|---|---|---|
address | string | Wallet, 0x-prefixed |
bought | number | Tokens received, whole tokens |
sold | number | Tokens sent, whole tokens |
net_tokens | number | bought − sold, whole tokens |
usd_spent | number | USD value of tokens received, in USD |
usd_received | number | USD value of tokens sent, in USD |
net_usd | number | usd_received − usd_spent, in USD |
net_usd_raw | number | net_usd rounded to cents |
chains | array | Chain tags the wallet traded on, e.g. eth |
title | string|null | Known wallet title, when one exists |
addr_short | string | Display form, 0xabcd...1234 |
bought_fmt | string | Display string of bought |
sold_fmt | string | Display string of sold |
net_tokens_fmt | string | Display string of |net_tokens| |
usd_spent_fmt | string | Display string of usd_spent |
usd_received_fmt | string | Display string of usd_received |
net_usd_fmt | string | Display string of net_usd |
net_bar_pct | integer | |net_usd| as a percent of the page's largest |
Real rows
Token transfers · rows
Load it
curl -H "X-API-KEY: $VIKE_API_KEY" \
"https://data.vike.io/v1/onchain/tokens/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48/transfers?period=24h&chain=ethereum"
import os, requests
r = requests.get(
"https://data.vike.io/v1/onchain/tokens/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48/transfers?period=24h&chain=ethereum",
headers={"X-API-KEY": os.environ["VIKE_API_KEY"]},
timeout=30,
)
r.raise_for_status()
body = r.json()
import os, requests
import polars as pl
body = requests.get(
"https://data.vike.io/v1/onchain/tokens/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48/transfers?period=24h&chain=ethereum",
headers={"X-API-KEY": os.environ["VIKE_API_KEY"]},
timeout=30,
).json()
rows = body if isinstance(body, list) else next(v for v in body.values() if isinstance(v, list))
df = pl.DataFrame(rows)
print(df.head())
Full reference for /v1/onchain/tokens/{token_address}/transfers → · back to the catalog
Wallet labels
Entity, wallet type and risk flag per address — plus which source claimed what.
- Route
GET /v1/onchain/labels/{address}/provenance- Cadence
- Rebuilt by the label pipeline
- Coverage
- Ethereum addresses for `/labels/{address}/provenance`, which returns the consensus label and every source's own claim, among them `provider_e` (Polymarket wallets, the addresses that control or funded them, and mass payers), which answers for an address on any EVM chain and never feeds the consensus; POST /labels/batch labels a comma-separated list of addresses in one call, from the label set of the `chain` you pass (ethereum, bsc, base or hyperevm); `/wallets/{address}` summarises flows over 24h, 7d, 30d, 90d and all time.
- Rows
Fields
| Field | Type | Description |
|---|---|---|
address | string | The address asked about |
found | boolean | At least one source knows this address (consensus is all null when only provider_e does) |
consensus.entity | string|null | Resolved entity name |
consensus.wallet_type | string|null | e.g. cex, market_maker, treasury, dex, mev |
consensus.cex_product | string|null | For exchange wallets, e.g. retail |
consensus.user_subtype | string|null | e.g. active |
consensus.risk_flag | string|null | Null when none |
consensus.confidence | string|null | Classification confidence, e.g. medium |
sources | array | One object per source: source, label, and entity / category when the source has them. A `provider_e` entry also carries basis, evidence, n_wallets, volume_usd, realized_pnl_usd, top_wallet and top_wallet_name |
source_count | integer | How many sources made a claim |
Real rows
Wallet labels · rows
Load it
# replace each {placeholder} with your own value
curl -H "X-API-KEY: $VIKE_API_KEY" \
"https://data.vike.io/v1/onchain/labels/{address}/provenance"
import os, requests
r = requests.get(
"https://data.vike.io/v1/onchain/labels/{address}/provenance",
headers={"X-API-KEY": os.environ["VIKE_API_KEY"]},
timeout=30,
)
r.raise_for_status()
body = r.json()
import os, requests
import polars as pl
body = requests.get(
"https://data.vike.io/v1/onchain/labels/{address}/provenance",
headers={"X-API-KEY": os.environ["VIKE_API_KEY"]},
timeout=30,
).json()
rows = body if isinstance(body, list) else next(v for v in body.values() if isinstance(v, list))
df = pl.DataFrame(rows)
print(df.head())
Full reference for /v1/onchain/labels/{address}/provenance → · back to the catalog
Token holders
Holder leaderboard per token, ranked by cumulative net inflow in USD.
- Route
GET /v1/onchain/tokens/{token_address}/holders- Cadence
- Daily aggregates
- Coverage
- Top wallets by all-time net inflow of one token (USD at transfer time), with gross volume, transfer count, first and last active day and the wallet's label when known. limit up to 50. Find a token first with /tokens/search.
- Rows
Fields
| Field | Type | Description |
|---|---|---|
wallet | string | Holder, 0x-prefixed |
net_flow_usd | number | Inflow − outflow, USD — the ranking |
volume_usd | number | Inflow + outflow, USD |
tx_count | integer | Transfers counted |
first_seen | string | First active day, YYYY-MM-DD |
last_active | string | Last active day, YYYY-MM-DD |
entity | string | Resolved entity; empty when unlabelled |
wallet_type | string | Label type; empty when unlabelled |
Real rows
Token holders · rows
Load it
curl -H "X-API-KEY: $VIKE_API_KEY" \
"https://data.vike.io/v1/onchain/tokens/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48/holders?chain=ethereum&limit=10"
import os, requests
r = requests.get(
"https://data.vike.io/v1/onchain/tokens/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48/holders?chain=ethereum&limit=10",
headers={"X-API-KEY": os.environ["VIKE_API_KEY"]},
timeout=30,
)
r.raise_for_status()
body = r.json()
import os, requests
import polars as pl
body = requests.get(
"https://data.vike.io/v1/onchain/tokens/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48/holders?chain=ethereum&limit=10",
headers={"X-API-KEY": os.environ["VIKE_API_KEY"]},
timeout=30,
).json()
rows = body if isinstance(body, list) else next(v for v in body.values() if isinstance(v, list))
df = pl.DataFrame(rows)
print(df.head())
Full reference for /v1/onchain/tokens/{token_address}/holders → · back to the catalog