> ## Documentation Index
> Fetch the complete documentation index at: https://docs.0xarchive.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Pull historical market data

> Pull one bounded historical 0xArchive window with explicit Unix-millisecond inputs, cursor handling, quality state, and request metadata.

Pull one bounded window of historical rows, page by page, and save it with the cursor chain, request IDs, and quality decision that make it reproducible. The example pulls an hour of Hyperliquid core BTC trades; the same steps apply to any venue family and data type.

For an overview of Hyperliquid core history and its route families, see the [Hyperliquid core coverage and route-family overview](/hyperliquid-historical-data-api).

## Prerequisites

* Know the namespace and symbol. [Venues and market types](/core-concepts/venue-taxonomy) maps a market to its namespace.
* Load the API key from the runtime environment.
* Set a page budget and output path before the first request.
* For a window older than the most recent 30 days, check the [Free tier](/core-concepts/free-tier) history boundary: Free serves the most recent rolling 30 days, and Build and above keep the full retained archive.

A venue-wide first served date, or the end of a known backfill period, does not show that every row in your window was captured. Check the window itself, as in the first step below.

## Inputs

| Field | Bounded value |
| - | - |
| Venue family | Hyperliquid core |
| Symbol | `BTC` |
| Data family | Trades |
| Route | `/v1/hyperliquid/trades/BTC` |
| Window | The hour that ended one hour ago, as `START` and `END` in Unix milliseconds |
| Page size | `limit=1000` |
| Stop rule | Stop after the requested window is exhausted or the client page budget is reached |

Fix the window once and reuse it for every call. It sits inside every plan's history, including Free's most recent 30 days:

```bash theme={"theme":"github-dark"}
END=$(( ($(date +%s) - 3600) * 1000 ))   # one hour ago, in Unix milliseconds
START=$(( END - 3600 * 1000 ))           # one hour earlier
```

## Steps

<Steps>
  <Step title="Check the window">
    Check symbol-specific historical coverage and known gaps for the same bounds:

    ```bash theme={"theme":"github-dark"}
    curl -sS "https://api.0xarchive.io/v1/data-quality/coverage/hyperliquid/BTC?from=$START&to=$END" \
      -H "X-API-Key: $OXARCHIVE_API_KEY"
    ```

    Read the trades entry (reported as `fills`) for completeness, gaps, and cadence. Freshness is not historical completeness: recency and capture of the historical window are different checks. [Data quality](/data-quality) explains how to read each signal.
  </Step>

  <Step title="Pull the first page">
    ```bash theme={"theme":"github-dark"}
    curl -sS "https://api.0xarchive.io/v1/hyperliquid/trades/BTC?start=$START&end=$END&limit=1000" \
      -H "X-API-Key: $OXARCHIVE_API_KEY"
    ```
  </Step>

  <Step title="Follow the cursor within bounds">
    If the response has `meta.next_cursor`, send that exact opaque string as `cursor` on the next request. Keep the original `start`, `end`, `limit`, and every other filter unchanged. A page-budget stop with a remaining continuation is partial output, not a completed pull. [Pagination](/rest-api/pagination) shows the loop in shell and Python.
  </Step>

  <Step title="Save each page">
    Store the full query, response status, returned rows, cursor value, and request ID for every page. Keep the raw response separate from any transformed table.
  </Step>
</Steps>

## Expected state

The first response has `success`, a `data` array of trade rows, and route metadata. `meta.next_cursor` is absent when the window is exhausted. A completed pull has no remaining cursor inside the requested window and a request ID for each page.

## Verification

Pass each returned cursor unchanged; do not parse, calculate, or increment it. Check that the final row stays inside the requested window, the cursor chain is recorded, and the historical coverage and gap decision is attached to the output. A successful response or a recent record alone does not describe the window's historical gaps.

## Failure and recovery

* `400`: correct the named parameter, symbol, or time window before retrying.
* `401`: repair the key source or header.
* `403` with `historical_depth_exceeded` (`history_window_exceeded` without the version header): the window is older than your plan's history; move it forward or use a plan that keeps the full archive.
* `404`: return to [Venue coverage](/venue-coverage) and check the family namespace.
* `429`: honor `Retry-After` when present, otherwise use capped exponential backoff with jitter and lower concurrency.
* A remaining cursor at the page budget: mark the output partial and resume with the saved cursor under a new bounded run.
* A quality or incident failure: narrow, delay, or mark the output. Do not add synthetic rows.

## Saved run metadata

```json theme={"theme":"github-dark"}
{
  "run": "btc-trades-hour-001",
  "venue_family": "hyperliquid_core",
  "symbol": "BTC",
  "data_family": "trades",
  "route": "/v1/hyperliquid/trades/BTC",
  "query": {
    "start": "START in Unix ms",
    "end": "END in Unix ms",
    "limit": 1000
  },
  "cursor_chain": [],
  "request_ids": ["00000000-0000-0000-0000-000000000000"],
  "quality_state": "record the returned coverage, freshness, and incident decision",
  "output": "./artifacts/btc-trades-hour-001.json"
}
```

The zero UUID is a shape placeholder only. Replace it with the UUID returned by each response or client wrapper, and write the numeric `START` and `END` you used. Append each real cursor to `cursor_chain`; do not invent an opaque cursor.

## Next step

Turn the saved window into a research run with [Prepare data for a reproducible backtest](/guides/point-in-time-backtesting).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.