vike

Archive file index

The index of the Hyperliquid Parquet file archive — every UTC day that has a fills or positions file, with rows, bytes and the snapshot count, the column schema of both datasets, and the entry for the 5-minute latest positions file.

GET https://data.vike.io/v1/hyperliquid/archive/datasets

The index of the Hyperliquid file archive: every UTC day that has a file, per dataset, with the file's row count and size, the column schema of both datasets, and the entry for the latest positions file. It is the only way to see what exists. There is no folder to browse and no daily or latest file has a URL of its own: a file is obtained from /archive/url, which answers with a temporary link. The one open folder is the free samples; samples_base in the response is its address (Free samples).

Two datasets, one Parquet file per UTC day each, from 2026-05-01:

DatasetWhat one file holdsFirst day
fillsEvery fill of every wallet that day, sorted by asset, fill_ts, trade_id, wallet.2026-05-01
positionsEvery 5-minute snapshot of every open position that day, sorted by asset, wallet, snapshot_ts.2026-05-08, a partial day

Positions start on 2026-05-08 because that is the first day the 5-minute snapshots exist. A third file, latest/positions.parquet, holds the current open positions of every asset and is replaced every 5 minutes. How to download and read all three is in Download the Hyperliquid file archive.

cURL
# what the archive holds: every day with its rows and bytes, the schema of both datasets, and the latest positions file
curl -s "https://data.vike.io/v1/hyperliquid/archive/datasets" \
  -H "X-API-KEY: $VIKE_API_KEY"

# the newest day that has a positions file, with its snapshot count
curl -s "https://data.vike.io/v1/hyperliquid/archive/datasets" \
  -H "X-API-KEY: $VIKE_API_KEY" \
  | jq '[.dates[] | select(.streams.positions)][0]'

# the days whose positions file holds fewer than 250 snapshots: the source has them, the file is not padded
curl -s "https://data.vike.io/v1/hyperliquid/archive/datasets" \
  -H "X-API-KEY: $VIKE_API_KEY" \
  | jq -r '.dates[] | select(.streams.positions) | select(.streams.positions.snapshots < 250) | "\(.date) \(.streams.positions.snapshots)"'

# how fresh the latest positions file is, and how big
curl -s "https://data.vike.io/v1/hyperliquid/archive/datasets" \
  -H "X-API-KEY: $VIKE_API_KEY" \
  | jq '.latest.positions | {snapshot_ts, generated_at, rows, bytes}'

The route takes no parameters. This is the capture of the index, cut for the page: dates held 173 entries, so the newest two are kept, and each dataset's schema list is cut to its first two columns (the full lists are under The columns). The envelope and the latest entry are complete. The capture is from 2026-10-21; samples_base was added to the index after it, with the value shown.

Example response
{
  "dates": [
    {
      "date": "2026-10-20",
      "streams": {
        "fills": {
          "rows": 13405172,
          "bytes": 401098335
        }
      }
    },
    {
      "date": "2026-10-19",
      "streams": {
        "fills": {
          "rows": 12022655,
          "bytes": 358509915
        },
        "positions": {
          "rows": 102738790,
          "bytes": 1639446104,
          "snapshots": 253
        }
      }
    }
  ],
  "schema": {
    "fills": [
      {
        "name": "fill_ts",
        "type": "DateTime64(3, 'UTC')",
        "meaning": "Execution time of the fill, UTC, millisecond precision."
      },
      {
        "name": "wallet",
        "type": "String",
        "meaning": "Trader address, lowercase 0x-hex."
      }
    ],
    "positions": [
      {
        "name": "snapshot_ts",
        "type": "DateTime('UTC')",
        "meaning": "Snapshot instant, UTC, whole minutes: the START of the ingestion cycle that wrote the row, not the instant of the node state it reads (typically 5-10 minutes earlier). Cycles are usually 5-7 minutes apart and irregular; a source outage leaves a longer gap. The day's `snapshots` count in the manifest says how many there are."
      },
      {
        "name": "wallet",
        "type": "String",
        "meaning": "Trader address, lowercase 0x-hex."
      }
    ]
  },
  "url_endpoint": "/v1/hyperliquid/archive/url",
  "samples_base": "https://data.vike.io/archive/hl-samples/",
  "latest": {
    "positions": {
      "snapshot_ts": "2026-10-21T10:13:00Z",
      "generated_at": "2026-10-21T10:17:15Z",
      "rows": 427884,
      "bytes": 29578173,
      "assets": 341,
      "note": "The current open positions of every asset, one row per (wallet, asset, direction), refreshed about every 5 minutes. The columns are those of the daily `positions` file (meanings under `schema.positions`). `snapshot_ts` is per row: the ingestion cycle that last wrote the position. A position that closed within the last 15 minutes can still be present with an older `snapshot_ts`, the same rule as /v1/hyperliquid/positions; keep the rows at the file's `snapshot_ts` for the newest cycle only. There is no `opened_at`: /v1/hyperliquid/positions fills it per page, and joining it here costs 3x the rest of the run. Sorted by (asset, wallet, direction) in row groups of about 65,000 rows with min/max statistics (one asset is 1-2 of them). The file is REPLACED every ~5 minutes: download it whole (about 30 MB); a ranged read can straddle a replacement and fail, so retry it.",
      "url_endpoint": "/v1/hyperliquid/archive/url?dataset=positions&date=latest"
    }
  }
}

Reading the index

  • dates is newest first. Each entry has the day and a streams object with one key per dataset that has a file that day. rows is the number of rows in the file and bytes its size, so you can budget a download before asking for the link.
  • snapshots appears on positions only: the number of distinct snapshot_ts values in that day's file, counted from the file itself. Read it before you aggregate. A day holds roughly 250 to 290 snapshots, and some hold far fewer because the source has fewer (see the callout on this page).
  • reconstructed_hours appears on a positions entry only when that day has hours the source rebuilt by replaying fills instead of reading the venue's state: a list of hour starts such as 2026-10-23T11:00:00Z. Rows in those hours are reconstructed, not measured: margin_used and liquidation_price are 0 and state_ts is 1970-01-01. Their sizes matched the next real snapshot at 97% or better, except the hours also listed in reconstructed_lower_match_hours, which matched at 83 to 97%. Both lists are refreshed whenever the index is written, while a published day file is not rewritten, so an hour rebuilt after its day was published can be listed while that day's file still holds the gap: check the snapshot_ts values in the file. On 2026-10-24, 52 of 169 positions days listed rebuilt hours.
  • A day appears once it is complete. Yesterday's files are published in the early UTC morning; the newest day can therefore carry a fills file before its positions file, as the capture shows for 2026-10-20.
  • latest.positions is present while that file exists. snapshot_ts is the newest ingestion cycle in it and generated_at is when the file was written; rows, bytes and assets describe the file as of that moment. The file is replaced about every 5 minutes, so these values move: ask the index again rather than caching them.
  • url_endpoint is where a file is asked for. The index carries no URL for any daily or latest file, because none exists.
  • samples_base is the public folder of the free samples. It is the one address in the index that you can request as it stands; see Free samples.

Fewer than 250 snapshots means a gap in the source, not a missing piece of the file

A positions file is never padded. Ten days hold fewer than 250 snapshots: 2026-05-08 (71, a partial first day), 2026-06-08 (226), 2026-06-26 to 2026-06-29 (154, 138, 125 and 124, a snapshot about every 12 minutes), 2026-06-30 (248), 2026-08-12, 2026-08-13 and 2026-08-17 (236 to 245). The source wrote fewer on those days. The snapshots field is how a client tells a thin day from a normal one without opening the file; the download guide lists the other facts worth knowing about the data.

Free samples

samples_base is https://data.vike.io/archive/hl-samples/. It needs no API key and holds three files:

FileWhat it holds
fills-sample.parquetEvery BTC fill in the first UTC hour (00:00 to 01:00) of the sample day.
positions-sample.parquetOne instant: every open BTC position of every wallet at the sample day's first snapshot, the first snapshot_ts in 00:00 to 01:00 UTC.
manifest.jsonThe sample day and asset, and per dataset its description, sort order, published range (from, to, days), the full column schema, and which part of the day its sample file covers.

The sample day is the newest day for which both datasets are published, and the samples are rebuilt about once a week, so the day moves; read it from manifest.json. Each sample is cut with the same query as the daily file, so its columns, their order and types and its sort order are those of the daily files, and positions-sample.parquet has all 19 columns. A sample file is at most 2 MB (fills) or 4 MB (positions). The folder cannot be listed, only GET and HEAD are answered, and it carries none of the daily files: those come only from /archive/url.

The columns

Both lists are the schema key of the response, in file order.

fills

ColumnTypeMeaning
fill_tsDateTime64(3, 'UTC')Execution time of the fill, UTC, millisecond precision.
walletStringTrader address, lowercase 0x-hex.
assetStringInstrument as Hyperliquid names it: BTC on the main dex, dex:NAME (e.g. xyz:SP500) for a builder-deployed perp.
sideStringbuy or sell: the direction of THIS wallet's order.
size_tokenFloat64Fill size in base-asset units, unsigned (the direction is side).
priceFloat64Execution price, USD.
size_usdFloat64Notional of the fill, size_token * price, USD.
feeFloat64Fee charged in fee_token; negative is a maker rebate.
fee_tokenStringToken the fee is denominated in, e.g. USDC.
is_makerBoolTrue when this wallet's order was resting on the book.
order_idUInt64Hyperliquid order id (oid) of this wallet's order.
trade_idUInt64Hyperliquid trade id (tid). Both counterparties of one trade carry the same value; a fill is identified by (wallet, fill_ts, trade_id).
crossedBoolTrue when this fill crossed the book (the taker side).
closed_pnlFloat64Realized PnL in USD the node booked on this fill; 0 for a fill that opens or adds to a position.
dex_nameStringPerp dex the asset trades on: HL for the main dex, else the builder dex (e.g. XYZ).
start_positionFloat64 (nullable)Signed position in the asset immediately BEFORE this fill (negative is short). NULL for fills before 2026-09-29, when the column was added; 0.0 is a real value (flat before the fill).

positions

ColumnTypeMeaning
snapshot_tsDateTime('UTC')Snapshot instant, UTC, whole minutes: the START of the ingestion cycle that wrote the row, not the instant of the node state it reads (typically 5-10 minutes earlier). Cycles are usually 5-7 minutes apart and irregular; a source outage leaves a longer gap. The day's snapshots count in the manifest says how many there are.
walletStringTrader address, lowercase 0x-hex.
assetStringInstrument as Hyperliquid names it: BTC on the main dex, dex:NAME (e.g. xyz:SP500) for a builder-deployed perp. Called symbol in /v1/hyperliquid/positions. The prefix is authoritative: judge the dex by it, not by dex_name.
directionInt81 long, -1 short. The only place the side is stored: size_token and size_usd are never negative.
size_tokenFloat64Position size in base-asset units, unsigned (the side is direction).
size_usdFloat64Notional of the position at mark_price, size_token * mark_price, USD, unsigned.
entry_priceFloat64Average entry price of the position, USD.
mark_priceFloat64Mark price when the row was written, USD.
liquidation_priceFloat64Liquidation price, USD; 0 when the position has none (e.g. a well-collateralised cross-margin position).
unrealized_pnlFloat64Unrealized PnL in USD, direction * (mark_price - entry_price) * size_token.
leverageFloat32Leverage of the position as a multiple (10 = 10x). 0 when none was recorded, which happens on cross-margin rows.
margin_usedFloat64Margin the position ties up, USD: size_usd / leverage, or size_usd itself where leverage is 0.
size_cohortStringAccount-value size tier of the wallet WHEN THE ROW WAS WRITTEN (shrimp, 2x shrimp, fish, ... up to 4x whale: 12 absolute-dollar thresholds assigned at ingestion), as stored: a point-in-time label, not today's grading. The same field and vocabulary as size_cohort in /v1/hyperliquid/positions.
leverage_typeStringcross or isolated.
dex_nameStringPerp dex the asset trades on: HL for the main dex, else the builder dex (e.g. XYZ). Rows written while a clearinghouse was not yet registered hold the literal clsN (e.g. cls10) and keep it; the asset prefix is authoritative.
state_tsDateTime('UTC')The instant the state-derived columns (size, entry, mark, unrealized_pnl, leverage, margin_used, direction) describe, read from the node's state file; snapshot_ts is the WRITE time of the ingestion cycle, typically 5-10 minutes later (liquidation_price is re-read from the node at write time and belongs to snapshot_ts). 1970-01-01 00:00:00 means the row came from no state file (the per-wallet API fallback) or was written before 2026-08-22, when the column was added. Do not anchor a projection of the row on it: use snapshot_ts.
liq_progressFloat64How far the mark has moved from entry toward liquidation, clamped to 0..1: 0 at or in profit versus entry (or no liquidation price), 1 at or through the liquidation price. When a cross-margin liquidation price has drifted past entry it falls back to liquidation_price / mark_price (long) or mark_price / liquidation_price (short). The same SQL as liq_progress in /v1/hyperliquid/positions.
account_valueFloat64 (nullable)Native Hyperliquid perp account value of the wallet, USD (the quantity /v1/hyperliquid/wallets/{address} serves as equity.account_value), AS OF THE START OF THE ROW'S CLOCK HOUR: the wallet's last snapshot of the previous hour (a row at 10:37 carries the 09:5x value). Builder-DEX account equity is NOT included, so a wallet trading only dex:NAME assets usually reads 0. In latest, as of the start of the hour of the file's newest snapshot (an older hour while the hourly rollup lags), so on the rows of positions that closed in the last 15 minutes it can be up to 15 minutes later than the row. NULL when unknown: the wallet had no snapshot in that previous hour (e.g. its first position opened in the row's hour); the stored value is a reconstruction, not a measurement (hours rebuilt from fills, and the values the 2026-08-10 repair rewrote); or the row is a builder-DEX position (dex_name other than HL) whose value comes from an hour up to 2026-08-10 00:48 UTC (the repair seam): until then a builder-DEX-only wallet's empty native account was stored as a non-equity figure the repair did not catch every time, and a genuine balance cannot be told apart from a fabricated one, so genuine values are withheld on those rows too. The same wallet's HL rows in the same snapshot keep the value; only its builder-DEX rows read NULL.
pnl_cohortStringRealized-PnL percentile cohort of the wallet AS OF THE START OF THE ROW'S CLOCK HOUR: graded on fills strictly before the row's hour (the hourly label of the previous hour). This is ONE HOUR EARLIER than the label /v1/hyperliquid/positions returns for the same row in historical mode (start given), where a row graded from the hourly label table carries pnl_cohort_basis = point_in_time: that label is the row's own hour and so counts the fills of up to 59 minutes after the row. In latest, as of the start of the hour of the file's newest snapshot (an older hour while the label rollup lags), so on the rows of positions that closed in the last 15 minutes it can count up to 15 minutes of fills after the row. unknown for a wallet with fewer than 10 closed trades then, or with no realized record that hour; never empty.

opened_at, which /v1/hyperliquid/positions fills in per page, is not in these files. The latest file has the same 19 columns.

Example response
{  "dates": [    {      "date": "2026-10-20",      "streams": {        "fills": {          "rows": 13405172,          "bytes": 401098335        }      }    },    {      "date": "2026-10-19",      "streams": {        "fills": {          "rows": 12022655,          "bytes": 358509915        },        "positions": {          "rows": 102738790,          "bytes": 1639446104,          "snapshots": 253        }      }    }  ],  "schema": {    "fills": [      {        "name": "fill_ts",        "type": "DateTime64(3, 'UTC')",        "meaning": "Execution time of the fill, UTC, millisecond precision."      },      {        "name": "wallet",        "type": "String",        "meaning": "Trader address, lowercase 0x-hex."      }    ],    "positions": [      {        "name": "snapshot_ts",        "type": "DateTime('UTC')",        "meaning": "Snapshot instant, UTC, whole minutes: the START of the ingestion cycle that wrote the row, not the instant of the node state it reads (typically 5-10 minutes earlier). Cycles are usually 5-7 minutes apart and irregular; a source outage leaves a longer gap. The day's `snapshots` count in the manifest says how many there are."      },      {        "name": "wallet",        "type": "String",        "meaning": "Trader address, lowercase 0x-hex."      }    ]  },  "url_endpoint": "/v1/hyperliquid/archive/url",  "samples_base": "https://data.vike.io/archive/hl-samples/",  "latest": {    "positions": {      "snapshot_ts": "2026-10-21T10:13:00Z",      "generated_at": "2026-10-21T10:17:15Z",      "rows": 427884,      "bytes": 29578173,      "assets": 341,      "note": "The current open positions of every asset, one row per (wallet, asset, direction), refreshed about every 5 minutes. The columns are those of the daily `positions` file (meanings under `schema.positions`). `snapshot_ts` is per row: the ingestion cycle that last wrote the position. A position that closed within the last 15 minutes can still be present with an older `snapshot_ts`, the same rule as /v1/hyperliquid/positions; keep the rows at the file's `snapshot_ts` for the newest cycle only. There is no `opened_at`: /v1/hyperliquid/positions fills it per page, and joining it here costs 3x the rest of the run. Sorted by (asset, wallet, direction) in row groups of about 65,000 rows with min/max statistics (one asset is 1-2 of them). The file is REPLACED every ~5 minutes: download it whole (about 30 MB); a ranged read can straddle a replacement and fail, so retry it.",      "url_endpoint": "/v1/hyperliquid/archive/url?dataset=positions&date=latest"    }  }}