IRONFLOW
PRODUCTMCPPRICINGNODESDOCSOPEN_TERMINAL
OPEN_TERMINAL
GUIDES · UPDATED 27 OCTOBER 2026 · 8 MIN READ

Hyperliquid trades and fills data: API, archive and history by wallet

Hyperliquid records every match as two fills, one per side. This page covers what each fill holds, what the public API returns and where it stops, where older fills live, and how to pull tick data by market or by wallet over REST.

CONTENTS
  1. The short version
  2. 1. Fills and trades
  3. 2. Fill fields
  4. 3. The Hyperliquid API
  5. 4. The S3 fills archive
  6. 5. What a node writes
  7. 6. Ironflow /v1/trades and /v1/fills
  8. 7. Python: one window of tick data
  9. Questions
  10. Related guides
  11. References

The short version#

  • Each trade is two fills. They share a tid. Count trades and volume once per tid.
  • By wallet, recent: userFills and userFillsByTime. Capped at the 10000 most recent fills, and they leave out TWAP slices.
  • By market: recentTrades returns only the latest trades. There is no public trades history request.
  • Everything older: node_fills_by_block in s3://hl-mainnet-node-data, or your own node.
  • Over REST with history: Ironflow /v1/trades by market and /v1/fills by address.

1. Fills and trades#

A trade is one match between a buyer and a seller. Hyperliquid writes it as two fills: one for the buy side, one for the sell side. Both carry the same tid, price, size and time. Each fill has its own address, fee, closed PnL and direction. The side that crossed the book pays the taker fee. The resting side pays the maker fee or gets a rebate.

In a 5 minute BTC window on 27 October 2026 we pulled 984 fills. They held 492 distinct tidvalues, and 492 was the trade count in Hyperliquid's own 1m candles for those minutes. Summing size over all 984 rows gives twice the traded volume.

2. Fill fields#

These are the fields of a fill object in the API and in node files. The last column is the name in Ironflow's REST responses.

FieldMeaningIronflow
coinMarket. BTC for a native perp, xyz:NVDA for a HIP-3 market.market (BTC-PERP, xyz:NVDA-PERP)
px, szPrice and size as decimal strings. Size is in the base asset.price, size
sideB for the buy side, A for the sell side.side (buy, sell)
timeUnix milliseconds.timestamp
tidTrade id. Both sides of one trade carry the same tid.tid
oid, cloidOrder id, and the client order id when the trader set one.oid
crossedtrue when this side took liquidity.not returned
fee, feeTokenFee paid. A negative fee is a maker rebate.fee, fee_token
closedPnlPnL realized by this fill.realized_pnl
dir, startPositionOpen Long, Close Short, Short > Long and so on, and the position before the fill.not returned
hashTransaction hash. In our samples TWAP slice fills had an all-zero hash.hash (empty in current responses)
twapIdSet on TWAP slice fills.not returned
builderFee, deployerFeeNode files and the S3 archive: builderFee when the order used a builder code, deployerFee on HIP-3 markets. userFills left deployerFee out in our checks.not returned
liquidationPresent on liquidation fills: liquidatedUser, markPx, method.see /v1/liquidations

Ironflow rows add block_number and sequence, the fill's order inside its block, plus notional, display_symbol, market_class and quote_asset.

3. The Hyperliquid API#

All of these go to POST https://api.hyperliquid.xyz/info. Limits are from the info endpoint docs, checked on 27 October 2026.

RequestWhat it returns
userFillsOne address. At most 2000 most recent fills. No time range.
userFillsByTimeOne address and a time range. At most 2000 fills per response. Only the 10000 most recent fills are available.
userTwapSliceFillsOne address. TWAP slice fills, which the two requests above leave out. At most 2000 most recent.
recentTradesOne coin. The latest trades only. No time range. Our call for BTC returned 10.
bash
# fills for one address since a time, oldest first, 2000 per response
curl -s https://api.hyperliquid.xyz/info \
  -H 'Content-Type: application/json' \
  -d '{"type":"userFillsByTime","user":"0xYOUR_ADDRESS","startTime":1792972800000}'

# latest trades for one coin: coin, side, px, sz, time, hash, tid, users
curl -s https://api.hyperliquid.xyz/info \
  -H 'Content-Type: application/json' \
  -d '{"type":"recentTrades","coin":"BTC"}'

The 10000 fill cap#

userFillsByTime returns fills oldest first. Page forward by setting startTimeto the last fill's time. For a busy market maker address on 27 October 2026, a request from startTime: 0 started 33 hours back. Everything before that was gone from the API.

TWAP slices#

userFills and userFillsByTime leave out TWAP slice fills. We checked an address that was running a BTC TWAP: userFillsByTime returned nothing for a window where it had slice fills every 30 seconds. userTwapSliceFills returned them. Node files and the S3 archive include TWAP slices with a twapId.

userFills, userFillsByTime and recentTrades add rate limit weight for every 20 items returned. The rate limits guide has the numbers. The WebSocket userFills subscription streams new fills, with a cap of 10 unique users per IP.

4. The S3 fills archive#

Hyperliquid publishes every fill in s3://hl-mainnet-node-data/node_fills_by_block, one hourly LZ4 file per hour. The bucket is requester-pays: you need an AWS account and you pay for transfer. When we listed it, this prefix started in late July 2025. Older periods are in node_fills, which matches the API format, and node_trades, which does not. The historical data guide lists every bucket.

bash
aws s3 cp s3://hl-mainnet-node-data/node_fills_by_block/hourly/20260918/9.lz4 . \
  --request-payer requester
unlz4 --rm 9.lz4

# keep one wallet's fills from that hour (addresses are lowercase in these files)
jq -c --arg a 0xyour_address '.events[] | select(.[0] == $a) | .[1]' 9 > wallet.jsonl

To rebuild a wallet's full history from the archive you download every hour in the range and filter each one. The files hold every address, so most of what you download is other people's fills.

5. What a node writes#

A non-validator node started with --write-fills --batch-by-block writes the same files as blocks arrive, under ~/hl/data/node_fills_by_block/hourly/. Each line is one block. events is a list of [address, fill] pairs in execution order. This block from the archive holds one BTC trade:

json
{"local_time": "2026-03-22T01:00:00.425752239",
 "block_time": "2026-03-22T01:00:00.114473824",
 "block_number": 931271363,
 "events": [
  ["0xb3a38662575bdf1541013ce987934dba919851ea",
   {"coin": "BTC", "px": "68895.0", "sz": "0.00184", "side": "B",
    "time": 1774141200114, "dir": "Close Short", "closedPnl": "-0.011224",
    "crossed": true, "fee": "0.018254", "tid": 99990795293934, "twapId": null}],
  ["0xb668858ff53322d5511651a2cd92391eb5d60ee3",
   {"coin": "BTC", "px": "68895.0", "sz": "0.00184", "side": "A",
    "time": 1774141200114, "dir": "Close Long", "closedPnl": "0.137632",
    "crossed": false, "fee": "-0.003803", "tid": 99990795293934, "twapId": null}]]}

Some fields are trimmed above. Node fills also carry the builder address of the order. The public API shows builderFee but not that address. A node does not backfill: it writes from the block where it synced. The node guide covers flags, disk use and cleanup.

6. Ironflow /v1/trades and /v1/fills#

Ironflow indexes these fills from its own nodes and serves them at https://api.ironflow.sh. Both endpoints answer without a key. /v1/trades returns every fill in a market, both sides. /v1/fills returns one address's fills and requires address.

bash
# newest BTC fills, both sides of each trade
curl "https://api.ironflow.sh/v1/trades?market=BTC-PERP&order=desc&limit=3"

# one wallet's fills, any market
curl "https://api.ironflow.sh/v1/fills?address=0xYOUR_ADDRESS&limit=100"

Free keyWithout a key the API reaches 24 hours back. A free key reaches 30 days at 60 requests a minute. Get one by email.

One row from the first call, with the repeated venue fields trimmed:

json
{
  "market": "BTC-PERP",
  "market_type": "perp",
  "timestamp": 1793089259026,
  "block_number": 1198922811,
  "sequence": 7,
  "price": "79855",
  "size": "0.00334",
  "side": "buy",
  "address": "0xd3bbe70c4cc5d24637aabbb768a626e38a8c9435",
  "fee": "0.120022",
  "realized_pnl": "0",
  "notional": "266.7157",
  "oid": 587071244104,
  "tid": 822197913620469,
  "fee_token": "USDC"
}
  • Parameters: market, from and to in Unix milliseconds, limit up to 1000, cursor. /v1/trades also takes order=asc|desc.
  • Paging: pass pagination.next_cursor back as cursor until has_more is false.
  • TWAP slices are in /v1/fills with the other fills. The address we checked above showed its slice fills there.
  • Window per request: 1 hour without a key, 6 hours with a free key. A wider range returns 400 QUERY_WINDOW_EXCEEDED. Split it into smaller windows.
  • History: 24 hours without a key, 30 days with a free key, up to 12 months on paid plans.
  • Rate: 10 requests per minute per IP without a key, 60 with a free key. Over that you get 429 RATE_LIMIT_EXCEEDED.

7. Python: one window of tick data#

This pulls every BTC fill in a 5 minute window, follows the cursor, then counts trades and volume once per trade. It runs without a key. Set IRONFLOW_API_KEY for 30 days of history.

python
import os
import time
import requests

headers = {}
if os.getenv("IRONFLOW_API_KEY"):
    headers["Authorization"] = f"Bearer {os.environ['IRONFLOW_API_KEY']}"

to = int(time.time() * 1000) // 60_000 * 60_000
params = {"market": "BTC-PERP", "from": to - 5 * 60_000, "to": to, "limit": 1000}
rows = []
while True:
    page = requests.get("https://api.ironflow.sh/v1/trades", params=params, headers=headers).json()
    rows += page["data"]
    if not page["pagination"]["has_more"]:
        break
    params["cursor"] = page["pagination"]["next_cursor"]

trades = {r["tid"] for r in rows}  # two rows per trade, one per side
volume = sum(float(r["size"]) for r in rows if r["side"] == "buy")
print(len(rows), "fills,", len(trades), "trades,", round(volume, 5), "BTC")

The same pattern against the Hyperliquid API, for one wallet. It stops at the 10000 fill cap without saying so, and it misses TWAP slices.

python
import requests

def user_fills(address, start_ms=0):
    fills, seen = [], set()
    while True:
        batch = requests.post(
            "https://api.hyperliquid.xyz/info",
            json={"type": "userFillsByTime", "user": address, "startTime": start_ms},
        ).json()
        new = [f for f in batch if (f["tid"], f["oid"]) not in seen]
        if not new:
            return fills
        fills += new
        seen.update((f["tid"], f["oid"]) for f in new)
        if len(batch) < 2000:
            return fills
        start_ms = batch[-1]["time"]  # fills that share this time are deduped above

Questions#

How do I get all historical fills for a Hyperliquid wallet?#

The API keeps only the 10000 most recent fills per address. Older fills are in node_fills_by_block in the public S3 archive: download the hours you need and keep the rows for that address. Ironflow's /v1/fills returns them by address over REST, as far back as your access level allows.

What is the difference between a fill and a trade on Hyperliquid?#

A trade is one match between a buyer and a seller. It produces two fills, one per side, with the same tid. A fill carries one account's fee, closed PnL and direction.

Why are TWAP fills missing from userFills?#

userFills and userFillsByTime leave out TWAP slice fills. Request them with userTwapSliceFills. Node output and the S3 archive include them with a twapId.

Where can I get Hyperliquid tick data?#

Every fill is in node_fills_by_block, from the S3 archive or from your own node. Ironflow serves the same fills by market at /v1/trades, with block number and in-block sequence on each row.

Does the Hyperliquid trades API return history?#

No. recentTrades returns only the latest trades for one coin and takes no time range. For a past window, use the S3 archive, a node, or an indexed API such as /v1/trades.

Why is my Hyperliquid volume twice the real number?#

You summed both fills of every trade. Count each tid once, or sum only the buy side.

Related guides#

  • Hyperliquid historical data: API, S3 archive and Python
  • Hyperliquid candles and OHLCV data
  • Hyperliquid API rate limits explained
  • How to run a Hyperliquid non-validator node

References#

  • Hyperliquid docs: Info endpoint. userFills, userFillsByTime, userTwapSliceFills, recentTrades and their limits.
  • Hyperliquid docs: Historical data. The S3 buckets and the requester-pays rule.
  • Hyperliquid docs: L1 data schemas. Field layout of node_fills_by_block.
  • hyperliquid-dex/node. The --write-fills and --batch-by-block flags.

Every fill above is queryable by market or wallet over REST. The API page has the endpoints and an MCP server for AI clients. A free key gives 30 days of history. For reads at node scale, see the nodes page.

ON THIS PAGE
  1. The short version
  2. 1. Fills and trades
  3. 2. Fill fields
  4. 3. The Hyperliquid API
  5. 4. The S3 fills archive
  6. 5. What a node writes
  7. 6. Ironflow /v1/trades and /v1/fills
  8. 7. Python: one window of tick data
  9. Questions
  10. Related guides
  11. References

Automated derivatives trading is high-risk: you can lose all of your funds. Ironflow is trading software, not financial advice, and never holds your funds. Not available to U.S. persons or in sanctioned jurisdictions. See Risk Disclosure.

Not affiliated with Hyperliquid or Pear Protocol.

IRONFLOW

© 2026 IRONFLOW

APIPricingDocsNodesGuidesFree API keyGitHubTermsPrivacyRiskAcknowledgements