Two halves, one chain
Hyperliquid is one blockchain with two execution environments that share a single state.
- HyperCore is the exchange: order books, margin, matching, funding and liquidations, all on-chain. Every order goes through consensus, yet a client near the API servers sees a median of 0.2 s from sending an order to a committed result, and 0.9 s at the 99th percentile. Mainnet handles about 200,000 orders a second.
- HyperEVM is an ordinary EVM (the Cancun hard fork, without blobs) whose blocks are built inside the same execution, with HYPE as gas. Contracts read HyperCore state through precompiles and send it actions through a system contract called CoreWriter. That’s T9.
Most of this roadmap is about HyperCore, because that’s where the trading primitives live, and you talk to it with plain HTTP and WebSocket rather than smart contracts.
The addresses you’ll use
| Mainnet | Testnet | |
|---|---|---|
| HTTP API | https://api.hyperliquid.xyz |
https://api.hyperliquid-testnet.xyz |
| WebSocket | wss://api.hyperliquid.xyz/ws |
wss://api.hyperliquid-testnet.xyz/ws |
| HyperEVM JSON-RPC | https://rpc.hyperliquid.xyz/evm |
https://rpc.hyperliquid-testnet.xyz/evm |
| HyperEVM chain ID | 999 | 998 |
| Web app | app.hyperliquid.xyz |
app.hyperliquid-testnet.xyz |
The HTTP API has two endpoints. /info reads data and needs no signature; /exchange takes signed actions. Both take a POST with a JSON body. Your first call can be this:
curl -s https://api.hyperliquid-testnet.xyz/info \
-H 'Content-Type: application/json' -d '{"type":"meta"}'
Getting testnet funds
The testnet faucet only serves addresses that have deposited on mainnet. Once yours has, the faucet at app.hyperliquid-testnet.xyz/drip gives 1,000 mock USDC.
- If you sign in to the app with email, Privy creates a different address on each network. Export the mainnet wallet and import it into a wallet extension to use the same address on testnet.
- Testnet HYPE is harder to get. The faucet gives USDC only, and the third-party HYPE faucets we tried in October 2026 required a balance on mainnet.
Your account and its API wallets
Your account is the address that holds the funds. Don’t put that key in a bot. Instead, approve an API wallet (the docs also call it an agent wallet): a separate key that signs actions for your account or its sub-accounts.
- It can trade, not withdraw. Withdrawals are signed with the account’s own key. The one transfer an API wallet may sign,
agentSendAsset, has to send to the same address it comes from. - It only signs. To read an account’s balances, orders or fills, ask about the account’s address. Asking about the API wallet’s address returns nothing, a classic first-day bug.
- Don’t reuse one. When an API wallet is replaced (approving a new unnamed one replaces the old unnamed one), expires, or its account runs out of funds, its nonce history can be pruned. Its old signed actions could then be replayed. Generate a fresh key each time.
Nonces work differently
Ethereum makes every transaction use exactly the next nonce. That would break a market maker sending thousands of orders a second, so Hyperliquid does this instead:
- It keeps the 100 highest nonces each signer has used. A new action needs a nonce higher than the smallest of those, and never used before.
- The nonce must fall within two days before and one day after the block’s timestamp, in milliseconds. In practice: use the current time in milliseconds.
- Nonces are tracked per signer. Two sub-accounts traded by one API wallet share its nonces, so give each trading process its own API wallet.
The docs suggest batching orders and cancels every 0.1 s, with one atomic counter per signer. Batches that contain only post-only (ALO) orders get priority from validators, so keep them separate from IOC and GTC orders.
Your first order
With testnet USDC and an API wallet, place and cancel one order with an SDK before writing your own signing code. Using the official Python SDK:
from eth_account import Account
from hyperliquid.exchange import Exchange
from hyperliquid.utils import constants
agent = Account.from_key("0x...") # an API wallet key, testnet only
ex = Exchange(agent, constants.TESTNET_API_URL, account_address="0xYourAccount")
res = ex.order("ETH", True, 0.02, 1000, {"limit": {"tif": "Gtc"}}) # $20, far below market
oid = res["response"]["data"]["statuses"][0]["resting"]["oid"]
ex.cancel("ETH", oid)
When that works, T2 covers reading data properly and T3 covers what the SDK just did for you.