DOCS/ACCESS

Bulk S3 archive

Every dataset is published to S3 as daily Parquet files, one file per symbol per UTC day. For anything longer than a few days of book data, reading the archive directly is faster than paging through the REST API, and it does not count against your rate limit.

The archive is included in every plan, for the datasets and venues the plan covers. Files are identical in content and types to format=parquet responses from the REST API.

Bucket layout

s3://fdc-archive/{dataset}/venue={venue}/date={YYYY-MM-DD}/{symbol}.parquet

The bucket is in ap-northeast-1 (Tokyo). Partition directories use Hive-style key=value names, so DuckDB, Polars, Spark, Athena and pyarrow can read the partition values as columns without extra configuration.

EXAMPLE PATHS
s3://fdc-archive/trades/venue=hyperliquid/date=2026-09-15/BTC-PERP.parquet
s3://fdc-archive/book_l2/venue=hyperliquid/date=2026-09-15/ETH-PERP.parquet
s3://fdc-archive/book_l4/venue=hyperliquid/date=2026-09-15/SOL-PERP.parquet
s3://fdc-archive/candles_1m/venue=hyperliquid/date=2026-09-15/BTC-PERP.parquet
s3://fdc-archive/extended/venue=hyperliquid/date=2026-09-15/BTC-PERP.parquet
s3://fdc-archive/_manifests/date=2026-09-15/manifest.json

Dataset prefixes

PrefixContentsFile per
tradesTradessymbol, day
quotesL1 best bid and offersymbol, day
candles_1s, candles_1m, candles_5m, candles_1h, candles_1dCandles, one prefix per intervalsymbol, day
book_l2Snapshots and deltas, one row per level changesymbol, day
book_l3, book_l4Order events, with participant id in L4symbol, day
funding, open_interest, liquidations, mark_indexDerivatives datasymbol, day
options_chainOption quotes with IV and greeks where published (planned for Deribit, OKX, Bybit, Binance)symbol, day (for example BTC-26MAR27-90000-C.parquet)
extendedExtended fields, default profile. Custom profiles use extended_{profile}.symbol, day
instrumentsReference data in force during the dayvenue, day (_all.parquet)
gapsRecorded gapsvenue, day (_all.parquet)
chain_flowsOn-chain flows, partitioned by chain= in place of venue=chain, day (flows.parquet)
chain_entitiesEvery entity version valid during the day (versions are hourly), by chain=chain, day (entities.parquet)

The raw tier (venue messages exactly as received, before normalization) is available on request under raw/venue={venue}/date={YYYY-MM-DD}/ as zstd-compressed JSON lines, one file per channel per capture region per hour. CSV and newline-delimited JSON copies of any dataset can also be delivered in the same layout, gzip-compressed (.csv.gz, .jsonl.gz).

File naming and partitions

  • File names are the normalized symbol from Symbology, for example BTC-USD-INV-26MAR27.parquet. Symbols only use [A-Z0-9-], so no escaping is needed. The native symbol is in the venue_symbol column.
  • The date partition is the UTC day of ts_capture: a row belongs to the day we received it. A message stamped 23:59:59.999 by the venue and received after midnight is in the next day's file. Rows with source = "venue_archive" have no capture time (ts_capture is null), so they are partitioned by the UTC day of ts_exchange.
  • A file exists only for days with at least one row. Delisted instruments stop producing files; the instruments file for the day records the delisting.
  • Rows inside a file are sorted by ts_capture, then seq, then position within the venue message.
  • Book files (book_l2, book_l3, book_l4) open with a full snapshot at 00:00:00 UTC (snapshot_reason = "file_start") and contain a full-depth snapshot at every UTC hour ("checkpoint"), so each file replays on its own. See Order book depth.

Parquet settings

Files use zstd compression at level 3 and row groups of up to 1,000,000 rows, with min/max statistics on ts_capture so readers that push down time filters skip most row groups on intraday queries. Enum and string columns are dictionary-encoded. Decimals are decimal128(38,18) and timestamps are int64 nanoseconds.

Each file carries key-value metadata in its footer:

KeyExampleMeaning
fdc.schema_version0.9Schema version the file was written with.
fdc.datasettradesDataset name.
fdc.revision1Data revision, starting at 1.
fdc.row_count1904417Rows in the file.
fdc.gap_count0Rows in gaps that overlap this file.
fdc.seq_range1094058240111203-1094119875520481First and last seq.
fdc.sha2564be07c…d91aDigest of the row data, stable across re-encodings of the same revision.
DatasetRows per dayFile size
trades1.9M41 MB
book_l2212M1.8 GB
book_l396M2.4 GB
book_l498M3.0 GB
extended864K63 MB

Median day, Hyperliquid BTC-PERP, September 2026, from our internal capture.

Getting access

Access is granted per customer to IAM principals in your own AWS account. There are no shared credentials and no access keys to rotate on our side.

01Send your principalsSend your 12-digit AWS account ID and the ARNs of the IAM roles or users that will read the archive to daniel@financedatacloud.com, or add them in the account console once it opens with Phase 1.
02We update the bucket policyWithin one business day we grant those principals read access, scoped to the dataset and venue prefixes in your plan. You receive a confirmation listing the prefixes.
03Allow access in your accountAttach an IAM policy like the one below to the same principals. Cross-account access needs a grant on both sides.
04TestList a prefix from your account. A listing of dates means the grant is complete.
IAM POLICY (YOUR ACCOUNT)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "FdcArchiveList",
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::fdc-archive"
    },
    {
      "Sid": "FdcArchiveRead",
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::fdc-archive/*"
    }
  ]
}

Objects are encrypted with S3-managed keys (SSE-S3), so no KMS grant is needed. Data transfer is included in your plan and the bucket is not requester-pays. Reads from ap-northeast-1 are fastest; reads from other regions work the same way. Plans include 5 TB of archive transfer per month on Researcher and 25 TB on Desk; Enterprise limits are set per contract.

Publication schedule

  • Files for UTC day D are finalized and published before 02:00 UTC on day D+1. Over the last 90 days of internal capture, the median completion time was 01:12 UTC.
  • Each file is written once it has passed validation: sequence continuity checks, cross-region merge, and gap recording. See Data quality.
  • The day's manifest is written last. Treat the manifest as the signal that a day is complete; files that appear before it may still be joined by others.
  • Rows from a venue's licensed historical archive, where the agreement with that venue includes one, are published in bulk when that venue launches, with source = "venue_archive" in every row.

If publication runs late, the delay is posted on the status page.

Manifests and checksums

s3://fdc-archive/_manifests/date={YYYY-MM-DD}/manifest.json

One manifest per day lists every file published for that day across all datasets and venues. Your bucket policy grants read access to all manifests, so you can see what exists even for prefixes outside your plan.

JSON
{
  "date": "2026-09-15",
  "schema_version": "0.9",
  "generated_at": "2026-09-16T01:09:42.118Z",
  "files": [
    {
      "path": "trades/venue=hyperliquid/date=2026-09-15/BTC-PERP.parquet",
      "dataset": "trades",
      "venue": "hyperliquid",
      "symbol": "BTC-PERP",
      "rows": 1904417,
      "bytes": 42977310,
      "file_sha256": "9f2c4e81b7a05d63e1f8c2a94b7d0e35f6a1c8b29d4e70f3a5b6c1d8e2f9a047",
      "row_sha256": "4be07c19a2f3d6e58b0c7a41e9d2f5b38c6a0e17d4b9f2c85a3e61d07f4cd91a",
      "seq_range": "1094058240111203-1094119875520481",
      "ts_capture_min": 1789430400004211870,
      "ts_capture_max": 1789516799999102335,
      "gap_count": 0,
      "revision": 1
    }
  ]
}

file_sha256 is the digest of the file bytes; verify downloads against it. row_sha256 equals fdc.sha256 in the footer and stays the same if you re-encode the file. S3 ETags are not content hashes for multipart uploads and should not be used for verification.

Revisions

Published data is never edited in place. When a file is corrected (a normalization fix, messages recovered after the daily merge, or a venue busting trades), the canonical key is replaced by the new revision, with fdc.revision incremented in the footer and in the manifest. Earlier revisions stay readable as S3 object versions of the same key for 24 months; list them with aws s3api list-object-versions and read one with --version-id, or pass revision= to the API or SDK. Every revision gets a DATA REVISION entry in the changelog and an email to account contacts on the day it is published.

Reading the archive

AWS CLI

SHELL
# list available days
aws s3 ls s3://fdc-archive/trades/venue=hyperliquid/

# one file
aws s3 cp s3://fdc-archive/trades/venue=hyperliquid/date=2026-09-15/BTC-PERP.parquet .

# a month of one symbol, keeping the partition layout
aws s3 sync s3://fdc-archive/book_l2/venue=hyperliquid/ ./fdc/book_l2/venue=hyperliquid/ \
  --exclude "*" --include "date=2026-09-*/BTC-PERP.parquet"

DuckDB

SQL
INSTALL httpfs; LOAD httpfs;
CREATE SECRET fdc (TYPE s3, PROVIDER credential_chain, REGION 'ap-northeast-1');

-- hourly buy and sell volume for September, read straight from S3
SELECT date_trunc('hour', make_timestamp(ts_exchange // 1000)) AS hour,
       side,
       sum(size) AS volume
FROM read_parquet(
  's3://fdc-archive/trades/venue=hyperliquid/date=2026-09-*/BTC-PERP.parquet',
  hive_partitioning = true
)
GROUP BY ALL
ORDER BY hour, side;

Polars

PYTHON
import polars as pl

lf = pl.scan_parquet(
    "s3://fdc-archive/book_l3/venue=hyperliquid/date=2026-09-1*/ETH-PERP.parquet",
    hive_partitioning=True,
    storage_options={"aws_region": "ap-northeast-1"},
)

cancels_per_day = (
    lf.filter(pl.col("event") == "cancel")
      .group_by("date")
      .agg(pl.len().alias("cancels"))
      .sort("date")
      .collect()
)

Verify against the manifest

PYTHON
import hashlib, json, pathlib
import boto3

s3 = boto3.client("s3", region_name="ap-northeast-1")
day = "2026-09-15"
manifest = json.loads(
    s3.get_object(Bucket="fdc-archive", Key=f"_manifests/date={day}/manifest.json")["Body"].read()
)

for f in manifest["files"]:
    local = pathlib.Path("fdc") / f["path"]
    if not local.exists():
        continue
    h = hashlib.sha256()
    with local.open("rb") as fh:
        for chunk in iter(lambda: fh.read(1 << 20), b""):
            h.update(chunk)
    assert h.hexdigest() == f["file_sha256"], local

Without an AWS account, fdc.download() in the SDK fetches the same files through the API with your key, in the same layout, and verifies each one. See Python SDK.