How to get Hyperliquid positions and trade history as data
Hyperliquid serves any address's positions and fills to anyone, without a key. The catch is that its history endpoints stop at your 10,000 most recent fills.
POST to Hyperliquid's own info endpoint — clearinghouseState for open perp positions, spotClearinghouseState for balances, userFillsByTime for fills, userFunding for funding. No key is needed, only the account's address. The official Python SDK and CCXT wrap the same calls. The limit to plan around: only the 10,000 most recent fills are served, so a long history means archiving as you go or reading the venue's S3 dumps.
The short way
Every read goes to one URL, https://api.hyperliquid.xyz/info, as a JSON POST whose type field
picks the query. There is no key and no signature. What identifies the account is its address.
import requests
INFO = "https://api.hyperliquid.xyz/info"
ADDRESS = "0xYOUR_ACCOUNT_ADDRESS"
positions = requests.post(INFO, json={"type": "clearinghouseState", "user": ADDRESS}).json()
balances = requests.post(INFO, json={"type": "spotClearinghouseState", "user": ADDRESS}).json()
fills, start = [], 0
while True:
page = requests.post(INFO, json={"type": "userFillsByTime", "user": ADDRESS, "startTime": start}).json()
new = [f for f in page if f["tid"] not in {x["tid"] for x in fills}]
if not new:
break
fills += new
start = max(f["time"] for f in page)
clearinghouseState returns open perp positions — size as szi, entry price, leverage type,
liquidation price, unrealised PnL and cumulative funding — with an account margin summary.
userFillsByTime returns fills from startTime, and the docs' instruction for paging is to pass
the last timestamp you received as the next startTime. That bound is inclusive, which is why the
loop above deduplicates on the trade id tid: fills sharing a millisecond otherwise arrive twice.
Then fetch funding the same way, with userFunding and a startTime, and deposits, withdrawals and
transfers with userNonFundingLedgerUpdates. Those three streams together are the account's
history; any one of them alone is not.
What the options are
The info endpoint directly, as above. Nothing to install, and nothing between you and what the venue serves.
The official Python SDK. Its Info class is a thin wrapper
over the same calls — user_state posts clearinghouseState, user_fills_by_time posts
userFillsByTime, user_funding_history posts userFunding, and query_sub_accounts,
user_vault_equities, historical_orders and portfolio cover the rest. It adds no pagination,
no retries and no rate limiting, so the loop above is still yours to write.
CCXT, if Hyperliquid is one venue among several and you want its positions and
fills in the same structures as the others. Its Hyperliquid class implements fetch_positions
over clearinghouseState, fetch_my_trades over userFills or userFillsByTime depending on
whether you pass since, and fetch_funding_history and fetch_ledger over the two ledger
streams. Set walletAddress rather than an API key for reads; a subAccountAddress parameter
reads a sub-account, and a dex parameter reaches builder-deployed markets. The unified shape
costs you the venue's own fields, such as startPosition and dir, unless you read info on
each record.
Dune, for history past what the API will serve. Its curated Hyperliquid tables carry per-fill trades with fees and realised PnL from 27 July 2025, and per-account daily rows with funding and collateral flows; decoded node fills go back to January 2025. All of it is gated — an enterprise entitlement or a trial account — so this is the route when the question is a closed year rather than this morning.
0xArchive, a third-party market-data API for Hyperliquid and Lighter that also answers by wallet address — positions, order history, take-profit and stop-loss orders and TWAP events. Its free key covers the latest 30 days; the full archive, and webhook alerts on fills by a wallet you name, start at Build for $49 a month. Coverage starts on a different date for each data type, so check its coverage routes for the one you need before planning on a date.
Where this breaks
The history window is 10,000 fills. The docs on 4 October 2026: userFillsByTime returns at
most 2,000 fills per response "and only the 10000 most recent fills are available". userFills
without a time range returns the latest 2,000 and stops; historicalOrders and
userTwapSliceFills stop at 2,000 as well. An active account passes 10,000 fills in weeks, and
then its first trades are simply no longer served. Beyond that the venue points to S3 —
node_fills_by_block in the hl-mainnet-node-data bucket, with older data in a different
format, and transfers, staking and funding in misc_events_by_block. The requester pays the
transfer, and the dumps cover the whole venue rather than one address. If you need your own
history, archive it while it is still inside the window.
Funding is a separate record. It is paid every hour, and it arrives through userFunding as a
ledger entry with the coin, the position size, the rate and the USDC amount — not in the fills.
Fills carry closedPnl for the trade alone. A realised-PnL figure assembled from fills will be
wrong by every funding payment, and on a position held for days that is not a rounding error.
Builder fees are inside fee. When an order went through a front-end that charges a builder
code, the fill's fee is the total, inclusive of the builder's share, and a separate
builderFee field appears only when it is non-zero. Builder fees can run to 0.1% on perps and 1%
on spot. Velo and Insilico Terminal each charge one
basis point on Hyperliquid orders, WunderTrading 0.035%, and orders placed
through CCXT carry one basis point by default unless it is switched off. A
fee audit that treats fee as the venue's fee will overstate what Hyperliquid charged.
Sub-accounts and vaults are separate addresses. A sub-account has its own address, its own
positions and its own fills; the master's queries do not include it. List them with
subAccounts and query each one. A vault is also its own address — userRole returns vault —
while a depositor's stake in it shows up through userVaultEquities, not as positions. And the
common failure in the docs' own words: querying an API wallet's address returns an empty result,
because an agent key signs for an account and holds nothing itself.
Perp and spot are different calls and different names. Perp positions come from
clearinghouseState; spot balances from spotClearinghouseState. Spot fills name the pair as
@107 — an index into spotMeta — except for PURR/USDC, and the docs note that some pairs are
remapped on the interface: what the app shows as BTC/USDC is UBTC/USDC on HyperCore. Map coins
through spotMeta before you join anything to a price. Under unified-account or portfolio
margin, the docs say clearinghouseState is no longer the account balance and the spot endpoint
is the source of truth.
Builder-deployed markets need one call per venue. HIP-3 markets live on separate perp dexes,
and their fills come back with the dex as a prefix — xyz:XYZ100. Fills from every dex arrive in
one list, but clearinghouseState takes a dex parameter and defaults to the first one, so an
account's full position picture is one call per dex. perpDexs returned eleven entries on 4
October 2026: the first perp dex and ten deployer dexes. A position on any of the ten is invisible
to a script that never passes dex.
The rate limit is weight, not requests. Each IP gets 1,200 weight a minute. clearinghouseState
and spotClearinghouseState weigh 2, userRole 60, most other info requests 20 — and the fill,
order and funding history endpoints add further weight for every 20 items they return. The docs
do not print the increment; at one unit per 20, a full 2,000-fill page costs about 120, or ten
full pages a minute from one IP — so a backfill is paced by the response size, not the call count.
It is public in both directions. No key also means no privacy: anyone who knows an address
can read its positions, liquidation price and full recent history with the same calls. That is
why the address matters more than any key here, and why sub-accounts do not hide anything from a
reader who can see the master's subAccounts list.
If you outgrow this
Once the 10,000-fill window is the constraint, the choice is between running your own archive —
poll userFillsByTime, userFunding and the ledger stream on a schedule and keep everything —
and reading somebody else's. The venue's S3 dumps are complete but arrive in node formats, for
the whole venue, with transfer costs on you. A warehouse that has already ingested them, like
Dune, trades that work for an enterprise conversation. For the venue's market data
rather than one account's, Tardis.dev has recorded Hyperliquid since 29
October 2024.
The tools named above
In the order this page puts them in, which is an editorial judgement and not a ranking anyone paid for.
Hyperliquid Python SDK
Info.user_state, user_fills_by_time, user_funding_history and query_sub_accounts map one-to-one onto the info endpoint — no key needed for any of them.
The official Python client for signing and sending orders on one perp DEX.
FreeFree tierOpen source
CCXT
fetch_positions and fetch_my_trades in the shape every other venue uses, with walletAddress set and a dex parameter for builder-deployed markets.
One MIT client for 104 crypto exchanges, 76 of them over websocket.
FreeFree tierOpen source
Dune
SQL over HyperCore fills, funding and per-account daily tables back past the API's window — gated behind an enterprise entitlement or a trial.
SQL and an API over 100+ indexed chains, metered in credits. Free access is view-only.
$75/mo
0xArchive
Positions, order history and TWAP events by wallet address over REST, plus webhook alerts on a named wallet's fills — past 30 days needs Build, $49 a month.
Hyperliquid and Lighter data down to the single resting order, streamed live or replayed.
$49/moFree tier
FAQ
Do I need an API key to read my Hyperliquid positions?
No. Info requests are unauthenticated and take the account's address, so a script needs nothing secret to read positions, balances, fills or funding. The same is true for everyone else — any address's positions and history can be read by anyone who knows it.
How far back does Hyperliquid's API return fills?
To your 10,000 most recent fills and no further, per the info endpoint documentation on 4 October 2026, at up to 2,000 per response. userFills without a time range returns the latest 2,000 only. Anything older has to come from your own archive, the venue's requester-pays S3 dumps of node fills, or a warehouse that ingested them.
Why does my Hyperliquid query return nothing?
Usually because it was sent with an API wallet's address. The docs call this out as a common pitfall — an agent key signs for an account but holds nothing, so the query must use the master account's or sub-account's own address. userRole tells you which kind of address you have.
Is funding included in the fills?
No. Funding is paid hourly as its own ledger entry and comes back from userFunding, separate from fills. Deposits, withdrawals and transfers are a third stream, userNonFundingLedgerUpdates. A PnL built from fills alone leaves out every funding payment on every position held across the hour.
Sources
- Info endpoint — Hyperliquid, read
- Info endpoint — Perpetuals — Hyperliquid, read
- Rate limits and user limits — Hyperliquid, read
- Historical data — Hyperliquid, read
- Builder codes — Hyperliquid, read
- Hyperliquid Perpetuals — curated tables — Dune, read
The catalogue next door
This page names a handful of products. The rest of them are in Crypto Trading Terminals, each filled in against the same schema, with the fields to narrow it yourself.
Last updated . Corrected in place — an endpoint that moves is a bug on this page, not a new post.