Hyperliquid
Cohort positioning, wallets, leaderboards, liquidations, market structure and builder codes for Hyperliquid perps, one tool per family of routes.
8 tools over the on-chain half of Hyperliquid: where each cohort of wallets is positioned,
what one wallet has done, who ranks where, where liquidations would cluster and which builder codes earn the
fees. They are the /v1/hyperliquid REST routes shaped for an agent: each call
reads one view, which is one route, runs the same query function that route runs, and costs what that
route costs. Perps has the two older Hyperliquid tools (hl_perp_top_traders,
hl_position_match) and Candles has hl_ohlcv.
How these tools behave
A tool with several views takes a view parameter. The first view in each table is the default, so a
call with no view is valid. hl_cohort_metrics has one view and no view parameter.
A call is checked against its route before any query runs. An unknown view, an input the chosen view
needs and lacks, a parameter the chosen view does not take, a number above the route's cap, and a symbol
that disagrees with coin all come back as an {"error": ...} result that names the fix. A REST route
silently ignores a query parameter it does not know, which on a tool would hand an agent an unfiltered
answer that looks filtered; that is why these refuse instead.
symbol is the instrument parameter. Its old name, coin, is still accepted, as on
hl_position_match, and is not in the schema. Pass one of them, not two different ones.
Field names are the new ones only. Until 2026-11-19 a REST response from many of these routes repeats each
renamed field under its pre-2026-10-19 name (asset, coin, by_coin, n_coins, ...). These tools return the
new field names only, so a script ported from the REST responses must not read the old ones.
Lists page by keyset cursor. Pass nextCursor from a response back as cursor; it is null on the last
page. Every limit has the cap of its route, and the table row says which.
Units differ between fields with similar names. Read this table before comparing numbers.
| Field | Unit |
|---|---|
bias (every view that returns it) | Fraction from -1 (all short) to +1 (all long): (long - short) / (long + short) of notional |
pct_wallets_in_profit (hl_segments) | Fraction from 0 to 1, whatever its name says |
percent_risk (hl_liquidations) | Fraction from 0 to 1, whatever its name says |
win_rate, min_win_rate (hl_leaderboard, hl_wallet) | Fraction from 0 to 1 (0.6 is 60%). hl_perp_top_traders uses percent (win_rate_pct) |
max_drawdown_pct, pnl_pct | Percent (12.5 is 12.5%); pnl_pct is null under $1 of equity |
builder *_pct fields | Percentage points, 0 to 100 |
money (*_usd, size_usd, account_value, notional) | USD |
Cohort labels are not time-stable unless you ask. A wallet's pnl cohort is a percentile of its
cumulative realized PnL, and by default it is resolved at query time. A historical row therefore carries
today's grading: right for "who is smart now", look-ahead for a backtest. hl_cohort_metrics with
label_basis=point_in_time returns each hour's label as it was.
Not here. These routes have no tool: /wallets/{address}/trades (the closed round trips; use
hl_wallet view trades_summary for the statistics), /stats, /stats/volume, /hype/holders,
/symbols/hourly and /ohlcv/export. They stay on the REST API.
hl_cohort_metrics
1 credit. The hourly cohort matrix of one symbol: for every hour, how much long and short notional each bucket of one ladder holds. It answers "are whales or smart money long or short BTC", the history of that answer, and is the source for a positioning backtest.
| Parameter | Type | Default | Notes |
|---|---|---|---|
symbol | string | required | Instrument ticker, e.g. BTC; a HIP-3 market is dex:TICKER. An all-symbol matrix is refused |
axis | enum | size | size, pnl, pnl_abs, tier or pnl_unrealized; the ladders are described below |
start | string | — | ISO-8601 UTC lower bound |
end | string | — | ISO-8601 UTC upper bound |
opened_within | enum | all | 24h, 7d, 30d or all: positions opened in that window before each hour, rolling rather than relative to now. Anything but all is refused where the matrix is a stored aggregate (see below) |
hours | integer | 168 | Hourly buckets, newest first, max 8760. Pass 1 to 24 for a current read |
cursor | string | — | nextCursor from the previous page; it pages by whole hours |
label_basis | enum | — | current or point_in_time. Use point_in_time for any backtest |
grading | enum | — | realized (the default on pnl), realized-pit or unrealized; only on the pnl and pnl_unrealized axes |
Returns {axis, label_basis, grading, metrics, hours, nextCursor}, plus label_computed on every
graded ladder (pnl, pnl_unrealized, pnl_abs) and freshness on a window that reaches the present.
Each metrics row is one hour for one cohort: ts, cohort, total_position_value and
total_position_value_long in USD, bias, and, except under label_basis=point_in_time, position_count,
position_count_long, total_unrealized_pnl, total_position_size and total_position_size_long. The
response holds about hours times the number of buckets on the ladder, so ask for hours=1 to 24 for a
current read and page older data with cursor; paging is by whole hours, so an hour never arrives with
some of its cohorts missing.
Three ladders are per wallet (size, pnl, pnl_abs); tier buckets each position by its own notional;
pnl_unrealized ranks wallets by the unrealized PnL of their open positions as a share of account value,
from the last snapshot before that hour. pnl_abs is always today's dollar bands, so it is look-ahead and
never for a backtest. The hour in progress is never returned, so a series that is differenced or z-scored
has no partial bar at its right edge. For the current value use hl_market view symbol_metrics.
opened_within is applied or refused, never dropped. It filters live positions, so it works on size,
tier, pnl_abs and on pnl with the default label basis. The stored sources (axis=pnl_unrealized,
grading=unrealized or realized-pit, and label_basis=point_in_time) have position age summed away, and the
REST route answers the all-ages matrix for any value there, including a value that is not an age. The tool
refuses anything but all in those combinations, naming the combination, and refuses a value that is not an age
everywhere.
For a backtest, pass label_basis=point_in_time. Without it the pnl ladder is today's grading
applied to past hours, which lets a wallet that is "smart" now label a period when it was not.
label_computed states which source labelled the window and names the point where the label's resolution
changes (daily before 2026-08-06, hourly after), which a turnover count will read as a regime change that is
an artefact. grading=realized-pit is a frozen control for a study that crosses it: it stops growing, so a
recent window comes back short, and freshness.behind_hours says by how much.
For a live read, check freshness. On a window that reaches the present the envelope carries
{newest_ts, behind_hours, settled_lag_ceiling_hours, stale}; stale is the verdict, so a caller does not
re-derive the rollup's cadence. A window that ends in the past has no freshness.
Are whales net long or short BTC right now, and how has that moved over the last 24 hours?
Compare smart-money and rekt positioning on ETH over the last month, with the cohort labels as they were at the time.
hl_segments
1 credit. The cohort vocabulary and its positioning at the latest snapshot. Read cohorts or segments
first to learn the exact labels, then summary for one cohort or heatmap for the whole board.
| Parameter | Type | Default | Notes |
|---|---|---|---|
view | enum | segments | segments, cohorts, summary or heatmap |
cohort | string | — | One label on axis, e.g. whale or 3x smart, spelled as cohorts lists it. Required by summary |
axis | enum | size | size, pnl or pnl_abs (summary, heatmap) |
opened_within | enum | all | 24h, 7d, 30d or all (summary, heatmap); positions with no known open time are left out of every bounded window |
cohortsreturns{size, pnl, tier}, each a list of labels in ladder order (pnlalso holdsunknown, wallets under 10 closed trades). Everycohortparameter on every tool accepts exactly these strings.segmentsreturns one row per bucket withaxis,cohort,slug,rank,basis,applies_to,groupable, the bounds (min_usd,max_usd,percentile_min,percentile_max) andmin_closed_trades. It is how a client learns that thepnlladder is a percentile rank of cumulative realized PnL, not dollars, and thatpnl_absis dollars over the same PnL.groupableisfalsefortrade_size, which buckets a position rather than a wallet.summaryreturns everything one cohort holds across all symbols:total_long_usd,total_short_usd,total_value,bias,n_positions,n_wallets,n_wallets_in_profit,pct_wallets_in_profit,total_margin_used,total_equity,exposure_ratio(notional over margin used, the cohort's realized leverage),total_unrealized_pnland aby_sideblock.total_equityis the cohort's whole-account value, not the part backing the positions counted.heatmapreturns{axis, opened_within, snapshot_ts, source, totals, heatmap}: one row per symbol, ordered by total notional, each carrying its totals,biasand asegmentslist of the cohorts that hold anything in it. Empty cells are omitted rather than sent as zeros. It covers every symbol with open interest, so it is a large response.
axis is size, pnl or pnl_abs on summary and heatmap; the per-position tier ladder is refused
there and is available on hl_cohort_metrics.
Which Hyperliquid cohorts are the most net short right now?
Show how every cohort on the pnl ladder is positioned in SOL.
hl_wallet
1 credit. One Hyperliquid wallet. Take the address from hl_leaderboard, hl_perp_top_traders or
hl_position_match.
| Parameter | Type | Default | Notes |
|---|---|---|---|
view | enum | summary | summary or trades_summary |
address | string | — | Required. The wallet, 0x plus 40 hex characters, any case |
window | enum | all | 1d, 7d, 30d or all: the trade window of the metrics and statistics |
opened_within | enum | all | 24h, 7d, 30d or all (summary only); bounds the open-positions block only |
summaryreturns{wallet, window, metrics_updated_at, has_metrics, equity, open_positions, cohorts, lifetime, ledger_by_type, metrics}.equityis the live account (account_value,margin_used,cash_balance_usd,total_unrealized_pnl),open_positionsthe current set (count,notional_usd,unrealized_pnl, the oldest and newest open time),cohortsthe wallet's size and pnl labels and style tags,lifetimethe cumulative realized PnL since inception with deposits, withdrawals and vault flows, andmetricsthe window's risk set:win_rate(a fraction),max_drawdown_pct,pnl_pct, profit factor, Calmar, streaks and average leverage.metricsisnullandhas_metricsisfalsefor a wallet with no metrics row, which is not the same as a wallet with zero trades.trades_summaryreturns{wallet, window, overall, by_symbol}: trade counts split intoclosed_trade_countandopen_trade_count, wins and losses, average and median gain, loss, size and duration, profit factor, expectancy, fees and volume, overall and per symbol. Medians sit beside means because one outlier trade drags the mean far from anything the wallet typically does.
opened_within narrows only the open-positions block. The equity, lifetime and metrics blocks are
wallet-level totals with nothing per position to filter, and bounding them would return a number that
looks bounded and is not.
Is 0x... any good? Show its equity, open positions and risk metrics.
What does this wallet's trade record look like per coin, median versus mean?
hl_wallet_activity
1 credit. A wallet's event history. Fills, funding and ledger are newest first; builders is ranked by volume.
| Parameter | Type | Default | Notes |
|---|---|---|---|
view | enum | fills | fills, funding, ledger or builders |
address | string | — | Required. The wallet, 0x plus 40 hex characters, any case |
symbol | string | — | One symbol (fills, funding); omit for all |
start | string | — | ISO-8601 UTC lower bound (fills, funding, ledger) |
end | string | — | ISO-8601 UTC upper bound (fills, funding, ledger) |
side | enum | — | buy or sell (fills only) |
limit | integer | — | Rows per page. Default 100, max 5000; on builders default 50, max 500 |
cursor | string | — | nextCursor from the previous page, verbatim |
event_type | enum | — | One ledger event type (ledger only); an unknown value is refused |
window | enum | all | 1d, 7d, 30d or all (builders only) |
fills,fundingandledgerreturn{wallet, items, nextCursor}. A fill carriests,symbol,side,size_token,price,size_usd,feewith its ownfee_token,is_maker,closed_pnl,order_id,trade_idanddex_name. A funding row carriespayment_usd,position_sizeandfunding_rate;payment_usdis signed from the wallet's side, so negative means the wallet paid. A ledger row carriesevent_type,amount_usd,fee,token,counterpartyandhash.buildersreturns{wallet, window, builders, nextCursor}: one row per builder the wallet's flow went through, withshare_of_volume,volume_usd,fee_usd,fillsandactive_days. The share is of the wallet's builder-routed volume only. A fill placed without a builder code never reaches this table, so it is not a share of the wallet's total trading.
event_type is checked against the set the ledger holds; an unknown value is refused rather than answered
with an empty list that reads as "this wallet never did that". The ledger records send one-sided: an
outflow is seen where the matching inflow may not be, so a net-flow figure summed from send rows leans
negative.
How much has 0x... paid in funding on ETH this month?
List this wallet's deposits and withdrawals.
hl_leaderboard
1 credit. Rank wallets with the full risk set. This is the ranking to use for risk-adjusted questions, a
cohort filter, a deep page or a rank that excludes deposits; hl_perp_top_traders is the shorter list.
| Parameter | Type | Default | Notes |
|---|---|---|---|
view | enum | ranking | ranking or flow_adjusted |
window | enum | 30d | 1d, 7d, 30d or all |
sort | enum | pnl | One of 20 keys (ranking only); an unknown key is refused, never replaced by pnl |
order | enum | desc | asc or desc (ranking only) |
cohort | string | — | One label on axis (ranking only); omit for no filter |
axis | enum | size | size, pnl or pnl_abs: the ladder cohort filters on (ranking only) |
min_trades | integer | 0 | Minimum closed trades (ranking only). No floor at 0, unlike hl_perp_top_traders, which floors at 3 |
min_win_rate | number | 0 | A fraction from 0 to 1 (0.6 is 60%), compared with the win_rate the rows show (ranking only). 60 is refused: the route would return an empty board |
min_volume | number | 0 | Minimum total volume in USD (ranking only) |
limit | integer | 25 | Rows per page, max 500 |
cursor | string | — | nextCursor verbatim; on ranking, resend the window, sort, order, axis and cohort it was issued for |
rankingreturns{traders, totalCount, nextCursor}. A row carriesrank,wallet,account_value,total_realized_pnl,cum_realized_pnl,roi,pnl_pct, volume, fees, trade, win and loss counts,win_ratewith its long and short halves,profit_factor,expectancy,max_drawdown_pct,current_drawdown_pct,calmar_ratio,recovery_factor, streaks,avg_leverage,avg_duration_seconds, and thesize_cohort,pnl_cohortand style tag. Acursoris bound to thewindow,sort,order,axisandcohortit was issued for, and a different set is refused.flow_adjustedreturns the same rows plusranked_by, and per row anattributedblock, aflowblock of deposits and withdrawals, andflow_unbalanced. It ranks fills-derived realized trading PnL, which excludes deposits by construction: a wallet that deposits $1M and loses $50k reads as +$950k on any ranking built from account-value change, and not on this one.flow_unbalancedistruewhen the wallet had asendin the window, which is when theattributedblock beside the rank is unreliable. It is reported and never used to filter, so the board is not emptied of active traders.
Prefer pnl_pct to the raw roi: the stored roi is not guarded and reaches values like 4.8e10 on a
near-empty account, while pnl_pct is null under $1 of equity.
Rank the top Hyperliquid wallets of the last 30 days by Calmar ratio with at least 100 trades.
Who made the most from trading, not from deposits, this week?
hl_liquidations
1 credit. Realized liquidations, where open positions would liquidate, and which symbols have the most open interest close to it.
| Parameter | Type | Default | Notes |
|---|---|---|---|
view | enum | fills | fills, risk or heatmap |
symbol | string | — | One symbol. Optional on fills, required by heatmap |
wallet | string | — | One wallet, to ask whether it has been liquidated (fills only) |
start | string | — | ISO-8601 UTC lower bound (fills only) |
end | string | — | ISO-8601 UTC upper bound (fills only) |
direction | enum | — | long or short: the side of the position that was closed (fills only) |
limit | integer | 100 | Rows per page (fills, risk); max 5000 on fills, 500 on risk |
cursor | string | — | nextCursor verbatim (fills only) |
axis | enum | size | size, pnl or pnl_abs (risk only) |
cohort | string | — | One label on axis (risk only); omit for all wallets |
bins | integer | 50 | Equal-width price bins, 5 to 500 (heatmap only) |
fillsreturns{liquidations, label_computed, nextCursor}. A row carriesliq_ts,wallet,symbol,side,size_token,size_usd,mark_price,size_cohort,pnl_cohortandpnl_cohort_basis.directionfilters on the side of the position that was closed, sodirection=longis longs being liquidated, which is price falling. Thepnl_cohortis the label at the liquidation's own hour where the hourly label table covers it, and today's label where it does not;pnl_cohort_basissays which for each row, andlabel_computednames the windows with no point-in-time label.riskreturns a list of{symbol, total_value_usd, risk_value_usd, percent_risk, positions_count, at_risk_count, cohort_axis, cohort}. A position is at risk when it sits within half of its liquidation buffer, the same definition the Hyperliquid pages on the site use. Rows are ranked bypercent_risk, a fraction, so thin micro-cap markets can top the list: readtotal_value_usdbefore reading a ranking.heatmapreturns{symbol, mark_price, snapshot_ts, bins, total_value_usd, cohort_axis, window, coverage}. Each bin carriesprice_bin_start,price_bin_end,liquidation_value_usd,positions_count, the long and short split andmost_impacted_cohort. Bins are equal-width over +-50% of the mark price. Positions outside that window are dropped, not clamped into the edge bins, and positions with no liquidation price are excluded;coveragecounts both, within_window_fraction_of_open.
Which Hyperliquid symbols have the most open interest close to liquidation, among those with over $50M open?
Where would ETH liquidations cluster if price fell 10%?
hl_market
1 credit. Market-wide positioning: open interest per symbol, one symbol's hourly series, its split by position size, and individual position rows.
| Parameter | Type | Default | Notes |
|---|---|---|---|
view | enum | open_interest | open_interest, symbol_metrics, size_breakdown or positions |
symbol | string | — | One symbol. Required by symbol_metrics and size_breakdown; a filter on positions |
start | string | — | ISO-8601 UTC lower bound (symbol_metrics, positions); on positions it switches to history |
end | string | — | ISO-8601 UTC upper bound (symbol_metrics, positions); on positions it needs start and is refused alone |
axis | enum | — | size, pnl or pnl_abs. On symbol_metrics omit it to sum all cohorts; on positions it defaults to size |
cohort | string | — | One label on axis (symbol_metrics, positions) |
opened_within | enum | all | 24h, 7d, 30d or all (symbol_metrics only) |
limit | integer | — | Rows per page, max 5000. Default 500 on symbol_metrics, 100 on positions |
cursor | string | — | nextCursor verbatim (symbol_metrics, positions) |
label_basis | enum | — | current only (symbol_metrics); point_in_time is refused by name |
wallet | string | — | One wallet's positions (positions only) |
open_only | boolean | true | Latest open positions (positions only); start switches to history whatever this says, and false without start is refused |
open_interestreturns a list of{symbol, total_value, total_value_long, total_value_short, count, count_long, count_short, venue_open_interest_usd}: our aggregate of the positions we hold beside the venue's own open interest, so the two can be reconciled.symbol_metricsreturns{axis, label_basis, metrics, nextCursor}; each row is one hour withtotal_position_value,total_position_value_long,position_count,total_unrealized_pnl,biasandprovisional.provisionalistrueon at most one row, the newest, which covers the hour still in progress; itstsis a snapshot instant that changes on every poll, so drop it when storing a series. Omitaxisto sum all cohorts. Apnlcohort slice uses today's grading for every past hour, andlabel_basis=point_in_timeis refused here by name; usehl_cohort_metricsfor research.size_breakdownreturns a list of the ten position-size buckets, largest first, zero-filled:tier,min_size,max_size,position_count,total_position_value,total_position_value_long,value_close_to_liquidationandbias.positionsreturns{positions, nextCursor, label_computed}; a row carrieswallet,symbol,direction,size_usd,entry_price,mark_price,liquidation_price,unrealized_pnl,leverage,margin_used, the cohort labels withpnl_cohort_basis,opened_atandsnapshot_ts. By default it reads the latest open positions. Passingstartswitches to the historical snapshots, a very large table, so bound it withsymbolorwallet;endis an optional upper bound and is refused withoutstart, as isopen_only=false. A historicalpnlorpnl_abscohort filter needssymbolorwallet.
What is the open interest of every Hyperliquid symbol, next to the venue's own number?
Show the largest open BTC positions and how close each is to liquidation.
hl_builders
1 credit. Builder codes: front-ends and bots that attach their address to an order and are paid a slice of the taker fee for it.
| Parameter | Type | Default | Notes |
|---|---|---|---|
view | enum | leaderboard | leaderboard, stats, profile, users, fills or series |
window | enum | 30d | 1d, 7d, 30d or all (leaderboard, stats, profile, users) |
sort | string | — | leaderboard: fee (default), volume, users or fills. users: volume (default), fees or pnl |
order | enum | desc | asc or desc (leaderboard, users) |
limit | integer | — | Rows per page. Defaults and caps: leaderboard 50 and 500, users 100 and 1000, fills 100 and 5000, series 500 and 2000 |
cursor | string | — | nextCursor verbatim (leaderboard, users, fills, series) |
builder | string | — | The builder address. Required by profile, users, fills and series |
breakdown_limit | integer | 100 | Rows per breakdown, max 1000 (profile only) |
symbol | string | — | One symbol (fills only) |
side | enum | — | buy or sell (fills only) |
start | string | — | ISO-8601 UTC lower bound (fills, series) |
end | string | — | ISO-8601 UTC upper bound (fills, series) |
bucket | enum | 1d | 1h or 1d (series only) |
leaderboardreturns{window, sort, order, venue_volume_usd, market_share_window_start, builders, nextCursor}.sort=feeranks the builder's own cut, not the trader's total cost, and the two orderings differ materially.market_share_pctis over the overlap of the window with the period the venue-wide volume covers, which can start later thanwindow.start;market_share_window_startsays where.statsreturns{window, totals, lifetime, daily}: how many builders are active, how many distinct traders route through one, the share of all fill notional that carries a builder code, and how it moved day by day.lifetimeis unwindowed.profilereturns one builder'stotals,by_symbol,by_dex,user_volume_distribution(users split into volume deciles, so "19k users" and "one whale plus a tail" stop looking alike), andlifetime.profitable_usersis a per-wallet test, not a per-fill one.usersreturns the builder's wallets ranked byvolume,feesorpnl.pnlis the traders' signed realized PnL, not the builder's.fillsreturns the raw routed fills, newest first. Sumfee_usd, the builder's slice, which is dollars on every row;feeis in its ownfee_token, and some fills pay in a non-dollar token, so summingfeeis wrong.seriesreturns{builder, bucket, window, series, nextCursor}, newest first.usersis a distinct count per bucket and not additive: summing a daily column counts anyone who traded on two days twice. A period total comes fromprofileover that period.
Three fee columns are not interchangeable: fee_usd is the builder's own revenue, trader_fee_usd is the
total the trader paid, and builder_routed_deployer_fee_usd is the deployer's cut on builder-routed flow
only, a lower bound on deployer revenue. Builder history reaches back different distances for different
builders, so compare active_days and the resolved window before comparing two windows.
Which Hyperliquid builder codes earned the most fees in the last 30 days, and how concentrated are their users?
Show the daily fee revenue of this builder for the last 90 days.