Skip to content
BuildBuild on HyperCore16 pages
Architecture: info, exchange, WebSocketFoundationHyperliquid's API has three surfaces: an unsigned read endpoint, a signed write endpoint, and a WebSocket that streams data and can carry both.Your first requestFoundationReading Hyperliquid needs no key, no account and no risk: three unsigned POST requests return the list of markets, an order book and any address's positions.Asset ids and namingPractitionerOrders name a market by an integer asset id and info requests by a coin string, and perps, spot, HIP-3 and outcomes each derive both by a different rule.Precision: tick size, lot size, significant figuresPractitionerA HyperCore price has at most 5 significant figures and a decimal cap set by szDecimals, a size at most szDecimals decimals, and outcomes use a 0.00001 grid.Signing from first principlesExpertL1 actions sign a keccak hash of their msgpack bytes, user-signed actions sign readable EIP-712 data, and one wrong byte in either recovers a stranger's address.Nonces, expiry and API walletsPractitionerHyperCore keeps each signer's 100 highest nonces and accepts a new one only if unused, above the smallest, and within 2 days before and 1 day after block time.Orders: place, modify, cancel, batch, dead man's switchPractitionerAn order is a signed action carrying a batch of compact orders; modify, cancel, TWAP and the dead man's switch are separate actions, each with its own response.Transfers and withdrawalsPractitionerEvery HyperCore transfer is a signed action: sends and withdrawals need the account's own key, and an API wallet's transfer actions keep value inside the account.Multi-sig actionsExpertA multi-sig action carries enough authorised users' signatures over one nonce and one named leader, in an envelope only the leader signs and submits.WebSocket: subscriptions, post requests, heartbeatsPractitionerOne WebSocket streams book, trade and account updates as blocks execute and carries signed actions, but drops without warning and forgets every subscription.Rate limits and user limitsPractitionerHyperliquid meters each IP by request weight, 1200 per minute, and each address by actions: 10,000 plus one per USDC traded.Priority fees and the activation feeExpertHyperliquid sells latency in two burned auctions, read priority per IP and write priority per order, and charges 1 quote token to activate a new account.Error responsesPractitionerHyperliquid reports errors at four layers, from HTTP status to a string inside a successful response, and several name a cause other than the real one.Debugging signatures and wire shapesExpertA signature over wrong bytes still verifies, so HyperCore recovers a stranger's address and says it does not exist; the fix is almost always the encoding.Optimising latencyExpertMost of an order's round trip is consensus; code wins by streaming reads, cutting serial round trips, using the fast order class and never stalling orders.Data: historical, node and explorerPractitionerThe API keeps only recent history; a full record needs pagination, archived node data or your own node, and each trade counted once by its trade id.
BuildBuild on the HyperEVM9 pages
What the HyperEVM is, and is notFoundationThe HyperEVM is Hyperliquid's smart contract environment: it reads HyperCore state and queues HyperCore actions, but never executes them atomically.Blocks, gas and RPCPractitionerThe HyperEVM produces small blocks every second and big blocks every minute, prices gas with EIP-1559 in HYPE, and serves a JSON-RPC with no historical state.Moving assets between HyperCore and the HyperEVMPractitionerLinked tokens cross between HyperCore and the HyperEVM via per-token system addresses, with a decimal offset and a 200k-gas fee when leaving HyperCore.Linking a tokenExpertLinking binds a HyperCore token to one ERC20 through the spot deployer's request and the contract deployer's finalisation; the protocol checks almost nothing else.Reading HyperCore: precompilesExpertRead precompiles are 21 fixed addresses, 0x800 to 0x814, that return HyperCore state as of the EVM block's construction, and only at the latest block.Writing to HyperCore: CoreWriterExpertCoreWriter turns a contract call into a HyperCore action for the caller's own account, run after the EVM block, with no result returned and no revert on refusal.Execution order and timingExpertEach L1 block runs the EVM block, then EVM to HyperCore transfers, then CoreWriter actions; on mainnet most actions land within half a second, vault transfers in about 4 s.PatternsExpertA contract that touches HyperCore stays solvent by recording an intent, sending the action, and settling only on HyperCore state that visibly moved.Footguns, measuredExpertTwelve ways a HyperEVM contract loses money or silently does nothing when it touches HyperCore, each with the on-chain evidence and the fix.

Rate limits and user limits

Practitioner

Hyperliquid meters each IP by request weight, 1200 per minute, and each address by actions: 10,000 plus one per USDC traded.

Hyperliquid meters you twice. Each IP spends request weight on the shared API servers, and each address spends actions it earns by trading. Info requests spend only the first budget; actions spend both.

Say your bot sends 79 orders in one action. The IP pays weight 2, 1 + floor(79 / 40), of its 1200 a minute. The address pays 79 actions from its starting 10,000, and earns 1 back per USDC that trades.

So a key that orders but never trades runs out, while a trading account earns room as it goes.

Every REST request from an IP draws on this one pool:

Request Weight
Any documented exchange action 1 + floor(batch_length / 40); 79 orders in one action weigh 2
l2Book, allMids, clearinghouseState, orderStatus, spotClearinghouseState, exchangeStatus 2
userRole 60
Every other documented info request 20
recentTrades, historicalOrders, userFills, userFillsByTime, fundingHistory, userFunding, nonUserFundingUpdates, twapHistory, userTwapSliceFills, userTwapSliceFillsByTime, delegatorHistory, delegatorRewards, validatorStats base weight, plus more for every 20 items returned
candleSnapshot base weight, plus more for every 60 items returned
Any explorer request 40; blockList adds 1 per block, and older blocks not queried recently can weigh more

That is 10 weight-2 requests a second, or one weight-20 request. WebSocket caps are in the numbers and on WebSocket; the public HyperEVM RPC at rpc.hyperliquid.xyz/evm allows 100 requests per minute.

Each address earns 1 action per USDC traded

Section titled “Each address earns 1 action per USDC traded”

The count runs from the address’s creation, on top of an initial 10,000; sub-accounts count as separate users. Once spent, it may send one action every 10 seconds, but a larger cancel allowance still lets it pull its orders. A batch of n orders or cancels is 1 request for the IP limit and n for the address limit.

The docs’ example userRateLimit response:

{"cumVlm": "2854574.593578", "nRequestsUsed": 2890, "nRequestsCap": 2864574, "nRequestsSurplus": 0}

nRequestsCap is 10,000 plus the 2,854,574 USDC traded. nRequestsUsed is actions used minus any reserved, and nRequestsSurplus is reserved minus used, both floored at zero.

Open-order, congestion and daily caps are in the numbers. During congestion, the docs advise against resending cancels whose results have already returned: a duplicate still uses block space, which the congestion share measures.

Even a rejection costs: an action refused for a passed expiresAfter still costs 5 times the usual address budget.

Buying actions instead of trading for them

Section titled “Buying actions instead of trading for them”

An address can buy capacity with reserveRequestWeight:

{"type": "reserveRequestWeight", "weight": 100000}
  • Paid from the perps balance: 100,000 extra actions cost 50 USDC.
  • Optional destination pays for another address, which must already exist. Omit the key when unused: it is skipped in hashing if unset.
  • The reserve shows up as nRequestsSurplus.
Limit Value
REST weight per IP 1200 per minute
Exchange action weight 1 + floor(batch_length / 40)
Info weight 2, 20 or 60, plus per-item weight on history requests
Explorer weight 40
WebSocket connections per IP 10; 30 new per minute
WebSocket subscriptions per IP 1000; 10 unique users
WebSocket messages sent per IP 2000 per minute
WebSocket posts in flight per IP 100
HyperEVM public RPC 100 requests per minute
Address budget 10,000 + 1 per USDC traded
When address-limited 1 action per 10 s
Cancel budget min(limit + 100000, limit * 2)
Open orders per user 1000 + 1 per 5M USDC volume, at most 5000; from 1000 open, new reduce-only and trigger orders are rejected
Block space under high congestion at most 2 x the previous day’s maker share, computed per UTC date and weighted like fee-tier volume (HIP-3 assets in growth mode count for less)
Unified and portfolio margin 50k user actions per day, regardless of volume; standard accounts have no such cap (account modes)
reserveRequestWeight 0.0005 USDC per request
Stale expiresAfter rejection costs 5 x the usual address budget

A 100 USDC quote costs one action and earns one per USDC filled, so break-even is a 1 per cent fill rate. Heavy cancel-and-replace spends the 10,000 starting actions, then must trade or buy more.

The docs recommend standard mode for market makers and high-volume bots, because of the 50k daily cap on unified and portfolio margin.

The costly failure: reads spend the IP budget, and your order can’t be sent. One production bot’s order preparation called /info first, polling hit the IP limit and order placement stopped. The fix:

  1. Market data on WebSocket: REST traffic fell to about 0.15 requests per second.
  2. One limiter per IP that budgets weight, not request count, with a slice reserved for /exchange.
  3. On a 429, pause every caller at once, with exponential backoff and jitter. No Retry-After header is sent.
  4. Order placement never waits on an info request: cache what it needs (optimising latency).
// Weight-based limiter for one IP. The reserve and backoff values are design choices, not protocol values.
const WEIGHT_PER_MINUTE = 1200;
const EXCHANGE_RESERVE = 200;
let tokens = WEIGHT_PER_MINUTE;
let cooldownUntil = 0;
let backoffMs = 1_000;
setInterval(() => {
tokens = Math.min(WEIGHT_PER_MINUTE, tokens + WEIGHT_PER_MINUTE / 60);
}, 1_000);
const sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));
export async function limited(weight: number, isExchange: boolean, send: () => Promise<Response>): Promise<Response> {
const floor = isExchange ? 0 : EXCHANGE_RESERVE;
while (Date.now() < cooldownUntil || tokens - weight < floor) await sleep(50);
tokens -= weight;
const res = await send();
if (res.status === 429) {
cooldownUntil = Date.now() + backoffMs + Math.random() * backoffMs;
backoffMs = Math.min(backoffMs * 2, 60_000);
} else {
backoffMs = 1_000;
}
return res;
}

For the address budget, poll userRateLimit and top up with reserveRequestWeight before nRequestsUsed reaches nRequestsCap. Batching saves IP weight (up to 39 orders weigh the same as one, by 1 + floor(n / 40)) but costs the address budget the same as single orders.

For reading without spending REST weight, see WebSocket. For what a limit looks like when you hit it, see Error responses.