Hyperliquid SQL: query every fill, liquidation and transfer
Ironflow indexes every Hyperliquid fill, liquidation, funding rate and transfer from its own nodes and Hyperliquid's archive, and lets you query all of it with read-only SQL: over HTTP at /v1/query, or from an AI agent with the MCP run_query tool. This page covers the tables, the limits per plan, how to keep a query fast, and eight queries we ran against the live API.
The short version#
- Endpoint:
POST https://api.ironflow.sh/v1/querywith{"sql": "..."}, orGET /v1/query?sql=for clients that only fetch URLs. - Dialect: ClickHouse SQL, read-only, one statement, tables in the
hldatabase. - Data: every fill and liquidation since 25 May 2025, plus funding, mark and oracle prices, open interest, transfers and per-wallet daily totals.
- No key needed: the last 24 hours, 5 seconds per query. A free key reaches 30 days, paid plans up to 1 year.
- Schema:
GET /v1/query/schemareturns every table, column, tip, example and your own limits.
1. Your first query#
With curl, URL-encoded in a GET. This totals the liquidated USD per market over the last 24 hours:
curl -G "https://api.ironflow.sh/v1/query" --data-urlencode \
"sql=SELECT market, round(sum(notional)) AS usd FROM hl.liquidations WHERE ts > now() - INTERVAL 1 DAY GROUP BY market ORDER BY usd DESC LIMIT 5"The same query as a POST, which is easier for long SQL:
curl -X POST "https://api.ironflow.sh/v1/query" \
-H "Content-Type: application/json" \
-d '{"sql": "SELECT market, round(sum(notional)) AS usd FROM hl.liquidations WHERE ts > now() - INTERVAL 1 DAY GROUP BY market ORDER BY usd DESC LIMIT 5"}'The answer lists the columns with their ClickHouse types, the rows as arrays, what the query read and the query seconds you have left this hour. This one is from 7 November 2026:
{
"columns": [{"name": "market", "type": "LowCardinality(String)"}, {"name": "usd", "type": "Float64"}],
"rows": [["BTC-PERP", 13799106], ["ZEC-PERP", 2678685], ["XRP-PERP", 1982851], ["xyz:SOXL-PERP", 1494720], ["xyz:SPCX-PERP", 1380007]],
"row_count": 5,
"truncated": false,
"stats": {"elapsed_ms": 6.957377, "rows_read": 72298, "bytes_read": 1840641},
"limits": {"history_days": 1, "max_execution_seconds": 5, "max_rows": 1000, "budget_seconds_per_hour": 60},
"budget_remaining_seconds": 59.991042295
}In Python, with requests:
import requests
sql = """
SELECT hour, round(volume_usd) AS volume_usd, round(liquidation_usd) AS liquidated
FROM hl.market_hourly
WHERE market = 'BTC-PERP' AND hour >= now() - INTERVAL 6 HOUR
ORDER BY hour
"""
r = requests.post("https://api.ironflow.sh/v1/query", json={"sql": sql})
r.raise_for_status()
body = r.json()
cols = [c["name"] for c in body["columns"]]
for row in body["rows"]:
print(dict(zip(cols, row)))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.
With a key, send it as Authorization: Bearer $IRONFLOW_API_KEY. The query and the response stay the same; only the history window and the limits change.
2. Tables#
Fifteen views in the hl database. Every market column uses Ironflow's symbols: BTC-PERP, HYPE/USDC for spot, xyz:NVDA-PERP for a HIP-3 perp. HIP-3 tables carry the builder in dex, empty for native markets.
| Table | From | Holds |
|---|---|---|
| hl.fills | 25 May 2025 | Every fill: perps, spot, HIP-3 and HIP-4 outcomes. One row per side, so a trade is two rows with the same tid. About 15 million rows a day. |
| hl.trades | 25 May 2025 | One row per trade: the taker's side of each fill pair. Exact for perps and spot. |
| hl.wallet_fills | Last 92 days | The same fills sorted by wallet. Use it with WHERE address = '0x...'. |
| hl.liquidations | 25 May 2025 | One row per liquidated fill of the liquidated wallet, with the liquidator, side, price, size and notional. |
| hl.market_hourly | 25 May 2025 | One row per market and hour: OHLC, volume, taker buy and sell USD, liquidation USD, trades. |
| hl.wallet_daily | 29 Jul 2025 | Per wallet, day and market: fills, volume, realized PnL, fees, maker and taker volume. |
| hl.wallet_behavior | Latest daily build | One row per wallet active in the last 30 days, with trading-style features and labels such as market_maker or whale. |
| hl.funding | 27 Sep 2025 | Funding rate per perp: the live rate, recorded when it changes, plus a row at each hour boundary. |
| hl.funding_payments | Late Sep 2025 | Funding paid or received per wallet, market and hour, as in Hyperliquid's userFunding. |
| hl.mark_prices | Last 90 days | Mark price per perp every 10 seconds (native) or 30 seconds (HIP-3). |
| hl.oracle_prices | Last 90 days | Oracle price per perp at the same times as hl.mark_prices. |
| hl.open_interest | 29 Apr 2026 | Open interest per perp every 10 to 30 seconds. |
| hl.transfers | 21 May 2026 | Ledger events: USDC and spot sends, perp and spot class transfers, sub-accounts, staking. Both sides of a transfer, signed. |
| hl.builder_fills | 1 Jul 2026 | Fills placed through a builder code, with the builder address and fee. |
| hl.vault_operations | 27 Sep 2025 | Vault deposits, withdrawals, profit distributions and leader commissions. |
Column lists and descriptions come from GET /v1/query/schema, or in SQL with DESCRIBE hl.liquidations. Two notes on fills: is_liquidation is not set on fills from 1 November 2025 to 21 August 2026, so use hl.liquidations for liquidations across that range; and direction and hash are recorded from 1 August 2026.
3. Limits per plan#
Your plan sets every limit and ClickHouse enforces it. Rows older than the history window are filtered out of every table, so a query over a longer range returns less, not an error.
| Plan | History | Per query | Rows | Query time per hour |
|---|---|---|---|---|
| No key (per IP) | 1 day | 5 s | 1,000 | 60 s |
| Free key | 30 days | 10 s | 1,000 | 300 s |
| Explorer | 90 days | 20 s | 5,000 | 1,800 s |
| Builder | 1 year | 30 s | 10,000 | 7,200 s |
| Enterprise | Everything stored | 30 s | 10,000 | 36,000 s |
- Only
SELECT,WITH,DESCRIBE,SHOWandEXPLAIN, one statement per request, at most 20,000 characters, without aFORMATclause. - A query past its time limit answers 504
QUERY_TIMEOUT. An hour's budget used up answers 429QUERY_BUDGET_EXCEEDED. A query that would read too much answers 400QUERY_TOO_EXPENSIVE. - Plans and prices are on the pricing table. When a query hits a limit, the error names the plan that lifts it.
4. Fast queries#
- Filter on time first.
tsis what limits the scan onhl.fillsandhl.trades: the last few hours are cheap, older windows cost more. A market filter cuts the work, but every row in the time window is still read. - Totals over days or months: read
hl.market_hourly(per market) orhl.wallet_daily(per wallet). A year of every market inhl.market_hourlyis a few million rows;hl.fillsholds about 15 million a day. - One wallet:
hl.wallet_fillsis sorted by address and reads a few hundred thousand rows instead of every fill in the window. Addresses are lowercase. - Counting trades: each trade is two rows in
hl.fills. Usesum(notional) / 2there, orsum(notional)onhl.trades. - Aggregate in SQL. Results stop at the row limit (1,000 without a key), and
truncatedsays when they did. - Useful functions:
toStartOfInterval(ts, INTERVAL 5 MINUTE),argMax(price, ts)for the last value,sumIf,countIf,uniqExact(address),quantile(0.9)(x).
5. Eight example queries#
Each one filters to one market (or one token) and a short window, so it runs without a key. On 7 November 2026 each returned rows in 9 to 201 ms, reading at most 1.7 million rows.
1. 5-minute BTC candles built from trades, last hour#
SELECT toStartOfInterval(ts, INTERVAL 5 MINUTE) AS t,
argMin(price, ts) AS open, max(price) AS high, min(price) AS low, argMax(price, ts) AS close,
round(sum(notional)) AS volume_usd, count() AS trades
FROM hl.trades
WHERE market = 'BTC-PERP' AND ts > now() - INTERVAL 1 HOUR
GROUP BY t
ORDER BY t2. Taker buying against selling on ETH per hour, with liquidated volume#
SELECT hour, round(taker_buy_usd) AS taker_buy, round(taker_sell_usd) AS taker_sell,
round(taker_buy_usd - taker_sell_usd) AS net, round(liquidation_usd) AS liquidated
FROM hl.market_hourly
WHERE market = 'ETH-PERP' AND hour >= now() - INTERVAL 12 HOUR
ORDER BY hour3. The 10 largest BTC liquidation fills in the last 24 hours#
SELECT ts, address, if(side = 'sell', 'long', 'short') AS position, price, round(notional) AS usd
FROM hl.liquidations
WHERE market = 'BTC-PERP' AND ts > now() - INTERVAL 1 DAY
ORDER BY notional DESC
LIMIT 104. The wallets that took the most HYPE liquidity in the last hour#
SELECT address, round(sumIf(notional, side = 'buy')) AS bought,
round(sumIf(notional, side = 'sell')) AS sold, count() AS fills
FROM hl.fills
WHERE market = 'HYPE-PERP' AND ts > now() - INTERVAL 1 HOUR AND is_taker
GROUP BY address
ORDER BY bought + sold DESC
LIMIT 105. BTC funding settled each hour, with the annual rate#
SELECT toStartOfHour(ts) + INTERVAL 1 HOUR AS settled_at, argMax(rate, ts) AS hourly_rate,
round(argMax(rate, ts) * 24 * 365 * 100, 2) AS apr_pct
FROM hl.funding
WHERE market = 'BTC-PERP' AND ts > now() - INTERVAL 12 HOUR
GROUP BY settled_at
ORDER BY settled_at6. Mark against oracle on a HIP-3 stock perp, in basis points per 5 minutes#
SELECT toStartOfFiveMinutes(m.ts) AS t,
round(avg((m.price - o.price) / o.price) * 10000, 2) AS mark_vs_oracle_bps
FROM hl.mark_prices AS m
INNER JOIN hl.oracle_prices AS o ON o.market = m.market AND o.ts = m.ts
WHERE m.market = 'xyz:NVDA-PERP' AND o.market = 'xyz:NVDA-PERP'
AND m.ts > now() - INTERVAL 1 HOUR AND o.ts > now() - INTERVAL 1 HOUR
GROUP BY t
ORDER BY t7. The largest USDC sends between wallets in the last hour#
SELECT ts, address AS sender, counterparty AS receiver, amount
FROM hl.transfers
WHERE type = 'send' AND token = 'USDC' AND signed_amount < 0 AND ts > now() - INTERVAL 1 HOUR
ORDER BY amount DESC
LIMIT 108. The BTC wallets with the most realized PnL since yesterday (UTC)#
SELECT address, round(sum(realized_pnl_usd)) AS pnl, round(sum(volume_usd)) AS volume, sum(fills) AS fills
FROM hl.wallet_daily
WHERE market = 'BTC-PERP' AND day >= today() - 1
GROUP BY address
ORDER BY pnl DESC
LIMIT 10In the funding query each hour's last rate is the one settled at the next hour boundary; the newest row is the live rate of the hour in progress. In the liquidation query a sell closes a long and a buy closes a short, and one liquidation can take several fills.
6. From an AI agent#
Ironflow's MCP server at https://mcp.ironflow.sh/mcp has two tools for this: describe_data returns the tables, columns, tips and your limits, and run_query runs one query with the same limits as the API. No key is needed to start. In Claude Code:
claude mcp add --transport http ironflow https://mcp.ironflow.sh/mcpThen ask in plain words, for example “Which wallets took the most HYPE liquidity in the last hour?”. The agent reads the schema, writes the SQL and shows the rows. Setup for other clients is in the AI agents guide.
Questions#
Can I query Hyperliquid data with SQL?#
Yes. Ironflow's /v1/query runs one read-only ClickHouse SELECT over every Hyperliquid fill, trade, liquidation, funding rate, transfer and per-wallet daily total, from its own nodes and Hyperliquid's archive. Send it as POST JSON {"sql": ...} or GET ?sql= to https://api.ironflow.sh/v1/query.
Do I need an API key to run SQL on Hyperliquid data?#
No. Without a key a query reaches the last 24 hours, runs up to 5 seconds and returns up to 1,000 rows, with 60 query seconds per hour per IP. A free key from ironflow.sh/key reaches 30 days with 10 seconds per query and 300 query seconds an hour.
How far back can I query Hyperliquid fills with SQL?#
Fills, trades and liquidations are stored from 25 May 2025. Your plan sets the window: 1 day without a key, 30 days with a free key, 90 days on Explorer and 1 year on Builder. Rows older than the window are filtered out, not an error.
Which SQL dialect does it use?#
ClickHouse SQL, read-only. Only SELECT, WITH, DESCRIBE, SHOW and EXPLAIN are accepted, one statement per request, without a FORMAT clause, over the hl.* tables. GET /v1/query/schema lists every table, column and example.
How do I get one wallet's fills with SQL?#
Query hl.wallet_fills, which is sorted by wallet and holds the last 92 days: SELECT ts, market, side, price, size FROM hl.wallet_fills WHERE address = '0x...' ORDER BY ts DESC. Addresses are lowercase. For totals over many days use hl.wallet_daily.
Can an AI agent run SQL on Hyperliquid data?#
Yes. Ironflow's MCP server at https://mcp.ironflow.sh/mcp has a run_query tool that runs the same SQL and a describe_data tool that returns the tables and columns. It works without a key.
Related guides#
- Hyperliquid historical data: API, S3 archive, SQL and free downloads
- Hyperliquid trades and fills data
- Hyperliquid liquidation data: history, levels and API
- Hyperliquid funding rates: formula, history and API
References#
- Ironflow docs: Run SQL (POST). Request body, limits table and error codes.
- Ironflow docs: Run SQL (GET). The same query from a URL.
- Ironflow docs: Query schema. Every table and column, with your own limits.
- ClickHouse docs: SELECT. The query syntax.
- ClickHouse docs: Functions. Time, aggregate and string functions.
For data you would rather download than query, every liquidation, trade, funding rate, hourly candle and open interest value is a free Parquet file on ironflow.sh/datasets. A free key takes SQL to 30 days of history.