Hyperliquid (HyperCore) Data
Bitquery provides Hyperliquid Core Parquet exports from the Bitquery Data Store, designed for backtesting, market microstructure research, and data lake integrations. These datasets can be hosted directly in your own cloud storage (for example, AWS S3) and queried using engines like Snowflake, BigQuery, Athena, Spark, etc.
This dataset covers HyperCore, the L1 order-book exchange, not HyperEVM state. It is an event stream, not a state chain: there are no blocks, transactions or transfers tables. Every row is an exchange event (a fill, an order status change, a book delta, an oracle price, a funding payment), and every row carries Block_Number and Block_Time, so tables join on block without a separate blocks table.
Coverage & Freshness
- History: the Data Store lists the last 30 days on all seven tables. Older ranges are available on request from sales@bitquery.io.
- More order flow than the API: the Hyperliquid GraphQL API keeps about 30 days of trades, liquidations, funding, TWAPs and prices, but only about 7 days of raw order updates and book updates. The Data Store listing has order and book updates as far back as trades.
- Freshness: a delivery covers data up to the day before the files are prepared.
- Markets: plain perpetuals, HIP-3 builder-deployed perps, spot pairs, and outcome/prediction tokens. See Markets for how they share one column.
How Much History Is Included?
The Data Store lists the last 30 days, on every table, so you can replay roughly a month of live HyperCore, including any HIP-3 market that launched inside that window. Older ranges are available on request from sales@bitquery.io. The data has known gaps of about an hour each in September and October 2026, listed on request.
The Hyperliquid API keeps about 30 days of trades, liquidations, funding, TWAPs, prices and candles, and about 7 days of raw orders, book updates and signed actions. A query for anything earlier returns an empty result. For older history, ask sales@bitquery.io for an older range as a Data Store export.
How Is This Different From Hyperliquid's Free S3 Archive?
Hyperliquid's own s3://hyperliquid-archive bucket (and free mirrors of it) publishes L2 snapshots and asset contexts. This dataset is a different shape:
hyperliquid-archive and mirrors | Bitquery Data Store export | |
|---|---|---|
| Order book | L2 snapshots (price levels, fixed cadence) | L4 per-order updates with owner address and order id, every change |
| Orders | Not included | Full lifecycle: opens, cancels, every reject reason, TP/SL children |
| Fills | Not included | Both sides of every match, realized PnL, fees, builder and deployer fees |
| Liquidations | Not included | Both sides, with the liquidated account, mark price and method |
| Funding | Not included | Every account's payment at each hourly settlement |
| Oracle | Not included | Mark, oracle and external perp prices per HIP-3 market, plus the inputs behind them |
| History | Varies by bucket, no completeness guarantee | Last 30 days on the Data Store; older ranges on request |
| Freshness | Roughly monthly uploads | Up to the day before delivery |
Queue position, order lifetime, maker behaviour, spoofing patterns, and why a liquidation fired are only recoverable from this shape of data. Hyperliquid's separate s3://hl-mainnet-node-data bucket does carry raw node fills and misc events (funding among them) as node JSON, but no order statuses or book diffs.
Hyperliquid Data for Tax Reporting
Tax reports need every trading event a wallet produced. The dataset carries fills, liquidations, TWAP fills and funding, so you, your accountant or your tax software can compute gains, losses and income for any wallet. The Data Store lists the last 30 days. A full tax year or other past range can be requested from sales@bitquery.io.
| Tax input | Bitquery table |
|---|---|
| Realized PnL on every closed position | trades and perp_liquidations: *_Position_RealizedPnl |
| Trading fees, including builder and HIP-3 deployer fees | trades and perp_liquidations: *_Fees_Fee, _FeeToken, _BuilderFee, _DeployerFee |
| Funding paid and received | perp_fundings: Funding_Amount |
| Liquidations | perp_liquidations |
| TWAP executions, fill by fill | trades where Trade_TwapId > 0 (the TWAP owner's side; Trade_IsTwap = 1 also marks the counterparty), with twaps for the parent order |
| Spot buys and sells for cost basis | trades where Trade_Market_Kind = 'spot' |
Deposits, withdrawals and transfers between accounts are not in the export today.
Why teams use it for tax work:
- Nothing dropped from trading. Funding, TWAP child fills and liquidations are included alongside regular trades, so PnL reconciles.
- Exact amounts. Prices, sizes, PnL and fees are stored as decimal strings, so no precision is lost to floating point.
- Works with your stack. Parquet files that load into S3, GCS, Snowflake or BigQuery. Query one wallet or thousands with SQL, then export to CSV for tax software.
Built for tax platforms, accountants and funds reconciling Hyperliquid activity, as well as individual traders with high trade counts. To get the full dataset, buy it from the Bitquery Data Store, email sales@bitquery.io or fill the API form.
Get Full Access
A Data Store purchase is downloaded from a Requester Pays S3 bucket or a Cloudflare R2 bucket, chosen at checkout. Custom exports can be delivered into your own S3, GCS or R2 bucket, or as a Snowflake or BigQuery share. To buy or trial it, go to the Bitquery Data Store, contact sales@bitquery.io or fill the API form.
For the raw HyperCore feed from 17 July 2026, which you decode and parse your own way, see the Blockchain Data Lake.
Datasets listed on the Data Store (last 30 days). Other ranges and table combinations are available on request:
- Hyperliquid: all seven tables
- Hyperliquid Trades:
trades,perp_liquidationsandtwaps - Hyperliquid Order Book:
order_updates,book_updatesandprice_updates - Hyperliquid Funding:
perp_fundings
Available Hyperliquid Tables
| Table | Grain | What it holds |
|---|---|---|
trades | one row per side of each match, liquidations excepted | Price, size, direction, taker flag, fees, signed position before and after the fill, execution order, realized PnL, TWAP link |
perp_liquidations | one row per side of each liquidation match | The same fields as trades, plus the liquidated account, mark price and method |
twaps | one row per TWAP status change | Total size, executed size and notional, duration, status and error text |
order_updates | one row per order status change | Full lifecycle: open, canceled, filled, triggered and every *Rejected state, with TIF, triggers and TP/SL children |
book_updates | one row per L4 change of a single order | new, update or remove, each with the owning address, order id, price and size |
price_updates | one row per coin and price kind in each oracle push | Resulting mark, oracle and external perp prices, and the inputs behind them, for HIP-3 markets only |
perp_fundings | one row per account and market at each hourly settlement | Funding rate, signed position size, USDC paid or received |
Pick trades for executed volume and realized PnL, order_updates for intent and rejection analysis, and book_updates when you need to rebuild the book at an arbitrary block.
Why L4 Matters
An L2 snapshot tells you the book was 12 deep at a price; an L4 stream tells you which order moved and whose it was. Likewise, order_updates includes orders that never traded. Rejects and cancels are invisible in any trades-only dataset, and in the sample below they are most of the order events.
Sample Hyperliquid Cloud Dataset
You can explore schemas and validate your tooling using the public Hyperliquid sample datasets:
GitHub reference (schemas & examples) https://github.com/bitquery/blockchain-cloud-data-dump-sample/tree/main/hyperliquid
Sample Parquet downloads (public S3)
Every sample has the same columns as a full delivery.
| Table | Rows | Source | Download |
|---|---|---|---|
trades | 100 | first rows of pack 1165214000–1165214999 | 1165214001_1165214024.parquet |
twaps | 7 | every TWAP status in the same pack | 1165214012_1165214910.parquet |
perp_fundings | 100 | the settlement in block 1165214467 | 1165214467_1165214467.parquet |
order_updates | 100 | first rows from block 1078000000 | 1078000000_1078000000.parquet |
book_updates | 100 | first rows from block 1078000000 | 1078000000_1078000001.parquet |
price_updates | 100 | first rows from block 1078000000 | 1078000012_1078000012.parquet |
perp_liquidations has no public sample; liquidations are rare, and its columns match trades plus Liquidation_MarkPx, Liquidation_Method and Liquidation_LiquidatedUser.
The samples were exported on 30 September 2026, before Trade_Execution_Index, Liquidation_Execution_Index and Order_Index existed and before Trade_Position_Size was filled in. New exports carry all four.
The figures on this page come from the full export of two 1,000-block packs, not only the small samples: pack 1165214000–1165214999 (29 September 2026) held 11,082 trade rows, 7 TWAP statuses and one funding settlement of 411,880 rows across 327 markets, and pack 1078000000–1078000999 (19 July 2026) held 321,282 order updates, 193,903 book updates and 22,696 price updates.
Hyperliquid Dataset Directory Structure
bitquery-blockchain-dataset/
└── hyperliquid/
├── trades/
├── perp_liquidations/
├── twaps/
├── order_updates/
├── book_updates/
├── price_updates/
└── perp_fundings/
├── <start_block>_<end_block>.parquet
└── ...
Each file name gives the first and last block it holds. HyperCore produces roughly 14 blocks per second. File sizes track activity rather than block count, so size a download from the delivery manifest rather than from the block range.
A range with no events produces no file. Sparse tables such as twaps, perp_liquidations and perp_fundings (one settlement an hour) skip ranges, so discover files under a prefix rather than generating names from a stride.
Common Columns
Every table starts with the same block columns and ends with the same bookkeeping columns:
| Column | Type | Description |
|---|---|---|
ChainId | string | Always hyperliquid-core |
Block_Number | uint64 | HyperCore block height; the join key across tables |
Block_Time | int64 | Block timestamp, epoch nanoseconds |
Indexing_Time | int64 | When our pipeline indexed the block, epoch nanoseconds |
Indexing_OnTree, Indexing_OnTrunk | uint8 | 1 when the block was on our block tree and finalized chain |
Recording_Time | uint32 | When the row was written, epoch seconds |
Recording_Hash | uint64 | Row hash used to drop duplicates |
Block_Date | date | UTC date of the block, handy for partitioning |
Each table then prefixes its own fields with its entity name: Trade_*, Liquidation_*, Twap_*, Order_*, BookUpdate_*, PriceUpdate_*, Funding_*. Nested structures are flattened with _, for example Trade_Execution_Price and Order_Market_CoinRaw. The trading tables share these groups:
| Group | Columns |
|---|---|
_Trader_* | Address, Signer, Vault, Broadcaster, SignedAt |
_Market_* | Symbol, Kind, Protocol, CoinRaw, IsPerp, MaxLeverage |
_Position_* | Side, Size, SizeBefore, EntryPrice, RealizedPnl, Funding, Leverage, IsCross, Closed |
_Execution_* (trades, perp_liquidations) | Side, Direction, IsAggressor, Price, Size, Oid, Cloid, Tid, Hash, EventTime, Index |
Some of these are never filled in a given table (for example Trade_Position_EntryPrice, Trade_Position_Funding or Trade_MarkPx), and signer, vault and leverage fields are filled only when the source data carries them. The listing on the Data Store says, per column, when it can be empty.
Correctness Notes
Six things about this dataset will silently produce wrong numbers if you treat it like an EVM chain export.
1. Money Is Strings
Prices, sizes, PnL, fees and funding amounts are stored as decimal strings, not numerics. This is deliberate: exchange decimals must not pass through a float. Block numbers, ids and timestamps are integers. Use Decimal rather than float for money:
from decimal import Decimal
df["px"] = df.Trade_Execution_Price.map(Decimal)
Summing Trade_Execution_Price as float across a large export will drift; Decimal will not.
2. Every Match Appears Twice
trades carries one row per side, so a single match produces two rows, one taker and one maker. In the 29 September pack, 11,082 trade rows are 5,548 with Trade_Execution_IsAggressor = 1 and 5,534 with 0, resolving to 5,555 unique (Block_Number, Trade_Market_CoinRaw, Trade_Execution_Tid) keys.
Summing Trade_Execution_Size across the raw table double-counts volume. Filter to one side:
SELECT SUM(CAST(Trade_Execution_Size AS DECIMAL(38,8)))
FROM hyperliquid_trades
WHERE Trade_Execution_IsAggressor = 1 -- taker side only
Liquidation fills are not in trades; they are only in perp_liquidations. Include both tables for complete volume and PnL.
3. Trade_Execution_Hash Is Not a Join Key
One L1 transaction spans many matches, and TWAP fills carry 32 zero bytes. In the same pack, 11,082 rows share only 2,938 distinct hashes, and 1,296 rows (the TWAP fills, Trade_IsTwap = 1) have an all-zero hash.
Key matches on (Block_Number, Trade_Market_CoinRaw, Trade_Execution_Tid) instead. The maker and taker rows of a match share the Tid. It identifies the match but is not a sequence number: see note 6 for ordering.
4. Rejected Orders Dominate order_updates
order_updates is a record of intent, and most intent never trades. The 19 July pack's 321,282 order events break down as:
| Status | Records |
|---|---|
badAloPxRejected | 108,952 |
open | 95,735 |
canceled | 91,148 |
perpMarginRejected | 15,373 |
reduceOnlyCanceled | 3,875 |
iocCancelRejected | 3,505 |
insufficientSpotBalanceRejected | 1,299 |
filled | 1,118 |
minTradeNtlRejected | 121 |
reduceOnlyRejected | 101 |
selfTradeCanceled | 48 |
tooManyOpenOrdersRejected | 6 |
triggered | 1 |
Only 1,118 of 321,282 events, about 0.35%, are filled. badAloPxRejected alone is a third of the file: post-only (Alo) orders priced through the book, rejected rather than crossed. Alo is 313,186 of the 321,282 Order_Tif values, so this table is dominated by market makers quoting and requoting. Order_IsReject = 1 flags every *Rejected state.
Filter on Order_Status for whatever you are measuring; never treat row count as order flow.
5. Time Units Differ by Field
Two different epochs are in play, and mixing them is a factor-of-a-million error:
| Field | Unit |
|---|---|
Block_Time, Order_EventTime, Twap_Interval_EventTime | nanoseconds |
Trade_Execution_EventTime, Liquidation_Execution_EventTime | milliseconds |
Order_PlacedTime, Twap_Interval_StartTime | milliseconds |
Recording_Time | seconds |
In the sample, the same fill carries Block_Time = 1790672366376122193 and Trade_Execution_EventTime = 1790672366376.
6. Order Fills Inside a Block by Index, Not Tid
All rows of one block share Block_Time, and Trade_Execution_Tid is a hash-like identifier, so sorting by either scrambles the fills of a busy block. The index columns give the exchange's own order:
Trade_Execution_IndexandLiquidation_Execution_Indexgive the row's place in the block's fills, counting from 1. They form one sequence across both tables, so trades and liquidations of a block interleave correctly. Each side of a match has its own index.Order_Indexdoes the same for the order status changes of a block, which also shareOrder_EventTime.BookUpdate_Indexis the replay order of book changes.
To replay an account's position, merge both fill tables and sort by block, then index:
SELECT * FROM (
SELECT Block_Number, Trade_Execution_Index AS idx, 'trade' AS kind, Trade_Trader_Address AS account,
Trade_Execution_Side AS side, Trade_Execution_Size AS size, Trade_Position_Size AS position_after
FROM hyperliquid_trades WHERE Trade_Market_CoinRaw = 'BTC'
UNION ALL
SELECT Block_Number, Liquidation_Execution_Index, 'liquidation', Liquidation_Trader_Address,
Liquidation_Execution_Side, Liquidation_Execution_Size, Liquidation_Position_Size
FROM hyperliquid_perp_liquidations WHERE Liquidation_Market_CoinRaw = 'BTC'
) fills
ORDER BY Block_Number, idx
Markets
*_Market_CoinRaw covers four market types in one column, and *_Market_Kind names the type so you do not have to parse the prefix:
CoinRaw | Kind | Meaning | Trade rows (29 Sep pack) | Order events (19 Jul pack) |
|---|---|---|---|---|
HYPE, BTC | perp | Plain perpetual | 6,406 | 258,980 |
xyz:DRAM | hip3 | HIP-3 builder-deployed perp | 4,044 | 50,963 |
@107 | spot | Spot pair, by index | 524 | 10,603 |
#1890 | outcome | Outcome / prediction token | 108 | 736 |
*_Market_Protocol is hypercore for plain perps, spot and outcome tokens, and the deploying dex (such as xyz) for HIP-3 markets. HIP-3 perps were 36% of trade rows in the 29 September pack, so leaving them in a "Hyperliquid perps" aggregate will not give you the numbers you expect.
One exception: PURR/USDC is a spot pair, but its Kind reads perp and its IsPerp is 1. Exclude it by name when you need perpetuals only.
Table Notes
The full column list for every table, with a description of each column, is on the Data Store listings. The points below are the ones that matter when you query.
trades
Trade_Execution_SideisBuyorSellfor this side;Trade_Execution_Directionis the node's label:Open Long,Close Short,Long > Short,Buy,Selland so on.Trade_Position_SizeBeforeis the signed position before the fill (negative is short), not money.Trade_Position_RealizedPnlis the realized PnL of the fill in USDC. It was non-zero on 5,277 of the 11,082 rows in the 29 September pack.Trade_Position_Sizeis the signed position after the fill. For perps it isSizeBeforeplusSizeon aBuy, or minus it on aSell. For spot it is the base-token balance after the fill, net of a fee charged in that token, and can be empty.Trade_Execution_Indexis the fill's place in its block; see note 6.Trade_Fees_Feeis negative when the maker received a rebate.Trade_Fees_FeeTokenwasUSDCon 10,743 of those rows.Trade_TwapIdjoins toTwap_TwapId.
perp_liquidations
- Each liquidation match has two rows, one for the liquidated account and one for the counterparty.
Liquidation_LiquidatedUsernames the liquidated account on both; keep the rows whereLiquidation_Trader_Address = Liquidation_LiquidatedUserfor the liquidated side only. - Sign a liquidation fill like a trade, by
Liquidation_Execution_Side: aBuyadds to that row's account position and aSellsubtracts.Liquidation_Position_SizeBeforeandLiquidation_Position_Sizeare the position before and after the fill. Liquidation_MarkPxandLiquidation_Method(for examplemarket) come from the node.Liquidation_Liquidatoris always empty.
twaps
Twap_State_Statusisactivated,waitingForTrigger,finished,terminated,stoppedorerror, with the error text inTwap_State_StatusError.Twap_Order_Sizeis the total to execute;Twap_State_ExecutedSizeandTwap_State_ExecutedNotionalare the running totals.
order_updates
Order_Oidjoins toTrade_Execution_OidandBookUpdate_Oid.Order_ParentOidandOrder_Leglink take-profit and stop orders to the order they are attached to.Order_Indexorders the status changes inside a block, where they all shareOrder_EventTime.
book_updates
BookUpdate_Kindisnew(95,060 rows in the 19 July pack),remove(95,456) orupdate(3,387).BookUpdate_Sizeis the order's size onnewand the size after the change onupdate;BookUpdate_SizeBeforeis the size before anupdate.- Replay in
Block_NumberthenBookUpdate_Indexorder to reconstruct the book at any block.
price_updates
- The table covers HIP-3 markets only: the node publishes oracle updates for builder-deployed dexes, not for plain perps such as
BTCorHYPE. PriceUpdate_Kindsays which price the row is:mark,oracleorextPerpfor the resulting prices,markInput,spotInputorextPerpInputfor the inputs.- Oracle prices drive funding and liquidation, so for a HIP-3 market join these to
perp_liquidationsonBlock_Numberwhen reconstructing why a liquidation fired.
perp_fundings
Funding_Amountis the USDC the account received, negative when it paid.Funding_Sizeis the signed position, negative for a short. With a positiveFunding_Rate, longs pay shorts.
Joining Tables
Every table carries Block_Number, and the order id threads the trading tables together:
-- taker fills of orders that reached 'filled'
SELECT
t.Trade_Market_CoinRaw,
t.Trade_Execution_Price,
t.Trade_Execution_Size,
t.Trade_Position_RealizedPnl,
o.Order_Tif,
o.Order_ReduceOnly
FROM hyperliquid_trades t
JOIN hyperliquid_order_updates o
ON t.Trade_Execution_Oid = o.Order_Oid
AND o.Order_Status = 'filled'
WHERE t.Trade_Execution_IsAggressor = 1
Because one order produces many status events and many fills, join on the order id plus a status filter, or aggregate one side first; otherwise you get a cross product.
Reading Files with DuckDB
No key, no client library. Point DuckDB at the public sample directly:
SELECT Trade_Market_CoinRaw,
SUM(CAST(Trade_Execution_Size AS DECIMAL(38,8)) * CAST(Trade_Execution_Price AS DECIMAL(38,8))) AS notional
FROM read_parquet('https://bitquery-blockchain-dataset.s3.us-east-1.amazonaws.com/hyperliquid/trades/1165214001_1165214024.parquet')
WHERE Trade_Execution_IsAggressor = 1 -- taker side only, see Correctness Notes
GROUP BY Trade_Market_CoinRaw
ORDER BY notional DESC
LIMIT 10;
Reading Files in Python
import pandas as pd
from decimal import Decimal
BASE = "https://bitquery-blockchain-dataset.s3.us-east-1.amazonaws.com/hyperliquid/"
trades = pd.read_parquet(f"{BASE}trades/1165214001_1165214024.parquet")
orders = pd.read_parquet(f"{BASE}order_updates/1078000000_1078000000.parquet")
# taker side only: every match is in the file twice
taker = trades[trades.Trade_Execution_IsAggressor == 1]
taker = taker.assign(
px=taker.Trade_Execution_Price.map(Decimal),
sz=taker.Trade_Execution_Size.map(Decimal),
)
print("matches:", len(taker), "of", len(trades), "trade rows")
print("notional:", (taker.px * taker.sz).sum())
# realized PnL net of fees, by wallet
net = trades.assign(
pnl=trades.Trade_Position_RealizedPnl.map(Decimal) - trades.Trade_Fees_Fee.map(Decimal)
).groupby("Trade_Trader_Address").pnl.sum()
print(net.sort_values().tail())
# order flow is mostly rejects, not trades
print(orders.Order_Status.value_counts().head())
Real-Time vs Batch Data Access
Parquet exports are optimized for batch analytics and historical workloads.
If you require low-latency or streaming Hyperliquid data, Bitquery also provides:
The Parquet columns are the live stream's fields flattened with _ (Trade.Execution.Price becomes Trade_Execution_Price), so logic written against one maps field for field onto the other.
Buy the Full Dataset
The public samples above are a few hundred rows. A Data Store purchase is downloaded from a Requester Pays S3 bucket or a Cloudflare R2 bucket, chosen at checkout. Custom exports can be delivered into your own S3, GCS or R2 bucket, or as a Snowflake or BigQuery share. A Data Store purchase covers the last 30 days. Older history and custom ranges are available through sales@bitquery.io.
Ready to run this in production?
Get an API key and run these queries in minutes, or talk to us about plans and enterprise delivery.