DOCS/GETTING STARTED

Quickstart

From an empty environment to trades, a rebuilt order book, a live stream and a Parquet file. Every step shows the Python SDK and the same request over plain HTTP.

You need Python 3.9 or later and an API key. A test key is enough for this page: it returns a fixed 24-hour sample, Hyperliquid BTC-PERP, ETH-PERP and SOL-PERP on September 15, 2026 (UTC), across every dataset including L4 and extended fields.

1. Install the SDK

SHELL
pip install fdc
python -c "import fdc; print(fdc.__version__)"

The package depends on httpx, websockets and pyarrow. pandas and Polars are optional; install either to use .to_pandas() or .to_polars().

2. Get a test key

Test keys are issued from the account console, which opens with Phase 1. If you have not registered yet, pre-register and we will send console access with your early-access invitation. A key looks like fdc_test_7Hq2Lx9WbN4vR1cK8sTe3YpD6fMz0aJg.

Put the key in the environment rather than in code. The SDK reads FDC_API_KEY when no api_key argument is given.

SHELL
export FDC_API_KEY=fdc_test_7Hq2Lx9WbN4vR1cK8sTe3YpD6fMz0aJg
Requests outside the sample window or for other symbols return 403 test_key_out_of_range. Live keys (fdc_live_) work with the same code unchanged. Key types and scopes are on Authentication.

3. Pull trades

One hour of Hyperliquid BTC perpetual trades. start is inclusive and end is exclusive; both accept ISO 8601 strings or int64 nanoseconds.

from fdc import Client

fdc = Client()  # reads FDC_API_KEY

trades = fdc.trades(
    venue="hyperliquid",
    symbol="BTC-PERP",
    start="2026-09-15T14:00:00Z",
    end="2026-09-15T15:00:00Z",
).to_pandas()

print(trades[["ts_exchange", "price", "size", "side"]].head())
print(len(trades), "trades")

The REST response wraps rows in a data array. When more rows exist, next_cursor is set; pass it back as cursor to get the next page. The SDK follows cursors for you.

RESPONSE · TRUNCATED
{
  "data": [
    {"venue":"hyperliquid","symbol":"BTC-PERP","venue_symbol":"BTC",
     "ts_exchange":1789480800043000000,"ts_capture":1789480800047915320,
     "seq":1094089331232771,"source":"fdc_capture","region":"apne1",
     "price":"104291","size":"0.0158","side":"sell","trade_id":"812773664018202", ...}
  ],
  "next_cursor": "eyJzIjoxMDk0MDg5MzMxMjMyNzcxLCJpIjoxMDAwfQ"
}

Prices and sizes are strings in JSON and decimal.Decimal in the SDK, so nothing is rounded on the way. Column meanings are on Schema reference.

4. Rebuild an L2 order book

Ask for depth="L2" with a start time and the first rows you receive are a snapshot of the book as of start, followed by deltas. Each delta carries the absolute size at a price level; size 0 removes the level. Apply every row of a message (up to is_last) before reading the book.

from decimal import Decimal
from fdc import Client

fdc = Client()
book = {"bid": {}, "ask": {}}
new_message = True

for r in fdc.book(venue="hyperliquid", symbol="BTC-PERP", depth="L2",
                  start="2026-09-15T14:30:00Z", end="2026-09-15T14:31:00Z"):
    if new_message and r["is_snapshot"]:
        book = {"bid": {}, "ask": {}}      # a snapshot replaces the book
    levels = book[r["side"]]
    price, size = Decimal(r["price"]), Decimal(r["size"])
    if size == 0:
        levels.pop(price, None)
    else:
        levels[price] = size
    new_message = r["is_last"]

bids = sorted(book["bid"].items(), reverse=True)[:5]
asks = sorted(book["ask"].items())[:5]
print("spread", asks[0][0] - bids[0][0])

The full set of rules, including truncated venues, checksums and L3 replay, is on Order book depth.

5. Subscribe to a live stream

Add live=True and the SDK backfills from start, then switches to the WebSocket stream without a gap or a duplicate. The handover is done on seq, so you can restart a consumer from its last processed timestamp. With a test key the stream replays the sample day at recorded speed.

from fdc import Client

fdc = Client()

for t in fdc.trades(venue="hyperliquid", symbol="BTC-PERP",
                    start="2026-09-15T14:55:00Z", live=True):
    lag_ms = (t["ts_capture"] - t["ts_exchange"]) / 1e6
    print(t["side"], t["price"], t["size"], f"{lag_ms:.1f} ms")

Live access needs the live scope, which comes with Desk and Enterprise keys and with every test key. Channel names match dataset names (trades, book_l2, book_l3, funding and so on); message envelopes and heartbeats are on WebSocket.

6. Export to Parquet

For anything larger than a few hours, download files instead of paging through JSON. fdc.download writes one Parquet file per symbol per UTC day, in the same layout as the S3 archive, and returns the paths.

import polars as pl
from fdc import Client

fdc = Client()
paths = fdc.download(
    dataset="trades",
    venue="hyperliquid",
    symbol="BTC-PERP",
    start="2026-09-15",
    end="2026-09-16",
    format="parquet",
    path="./fdc-data",
)
# ['./fdc-data/trades/venue=hyperliquid/date=2026-09-15/BTC-PERP.parquet']

df = pl.read_parquet(paths[0])
print(df.schema["price"])  # Decimal(precision=38, scale=18)

format accepts json (default), csv and parquet. Parquet responses are not paginated: one request returns one file per UTC day, up to 31 days per request. For long ranges, use Bulk S3.

Next steps