Short answer: To use the Hyperliquid API in Python, install the official hyperliquid-python-sdk, read public data with its Info client and sign actions with an approved API wallet through its Exchange client. Hyperliquid has no API key and secret: every action is signed with a wallet private key, so key handling is the main risk. Start on testnet, and remember that leveraged perpetual trading can lose the whole margin.
Most exchange APIs issue a key and secret that a server can restrict or revoke. Hyperliquid works differently: an order is a signed message, and the address recovered from the signature decides which account it acts on. This guide uses the official Python SDK (version 0.24.0, the latest on PyPI) and Hyperliquid's own documentation, both read on 4 November 2026. Public calls ran against testnet that day. The signed call used a throwaway, unfunded key, so no order was placed.
This is a technical guide, not a recommendation of Hyperliquid, perpetual contracts or any strategy. Rules depend on your jurisdiction and the platform's terms. Hyperliquid's Terms of Use (last updated 15 June 2026) say the app interface is not available to persons residing, located or incorporated in the United States, Ontario or sanctioned jurisdictions, or to citizens of restricted territories anywhere, and that neither it nor Hyperliquid is licensed by any regulator. They are written for the interface, so do not read API access as a way around them. In Turkey, Article 99/A of Capital Markets Law No. 6362 treats activity aimed at Turkish residents by foreign platforms as unlicensed, and this guide is not aimed at them. Automated trading can lose money.
Hyperliquid API basics: hosts, endpoints and testnet
The trading API is two POST endpoints and a WebSocket, with separate mainnet and testnet hosts (documentation, 4 November 2026).
| Purpose | Mainnet | Testnet |
|---|---|---|
| Read data | POST api.hyperliquid.xyz/info | POST api.hyperliquid-testnet.xyz/info |
| Signed actions | POST api.hyperliquid.xyz/exchange | POST api.hyperliquid-testnet.xyz/exchange |
| Streams | wss://api.hyperliquid.xyz/ws | wss://api.hyperliquid-testnet.xyz/ws |
Info requests carry no signature, only a request type and, for account data, a user address. Exchange requests carry an action, a nonce and a signature. One catch: the faucet page says an address must already have deposited on mainnet to receive 1,000 mock USDC. The API page also lists community SDKs and a CCXT integration, covered in the CCXT guide.
Hyperliquid API Python SDK: a public testnet example
Install the SDK with pip install hyperliquid-python-sdk; its Info client reads public data with no key at all.
from hyperliquid.info import Info
from hyperliquid.utils import constants
info = Info(constants.TESTNET_API_URL, skip_ws=True) # no key, no WebSocket
meta = info.meta()
btc = next(a for a in meta["universe"] if a["name"] == "BTC")
book = info.l2_snapshot("BTC")
bid, ask = book["levels"][0][0], book["levels"][1][0]
print("szDecimals", btc["szDecimals"])
print("mid", info.all_mids()["BTC"])
print("best", bid["px"], ask["px"], "levels", len(book["levels"][0]))Output on 4 November 2026 (testnet prices are not mainnet prices):
szDecimals 5
mid 79821.0
best 79808.0 79834.0 levels 20szDecimals matters because orders must fit the tick and lot rules. Sizes round to szDecimals. Perpetual prices may have up to 5 significant figures and at most 6 minus szDecimals decimal places (8 minus for spot), and integer prices are always valid. So round(float(f"{px:.5g}"), 6 - sz_decimals) turns a hypothetical 80031.37 into 80031.0 when szDecimals is 5. The L2 snapshot returns at most 20 levels per side, and a candle request can reach only the most recent 5,000 candles.
Hyperliquid API wallet: how the agent key works
An API wallet, also called an agent wallet, is a separate key that a master account approves to sign on its behalf, so the master key can stay away from the bot.
- Setup. Create and authorize one at
app.hyperliquid.xyz/API. In the SDK, the API wallet's key signs andaccount_addressstays the main wallet's public address, as the README states. - Limits. An account can have 1 unnamed wallet and up to 3 named ones, plus 2 named per sub-account.
valid_untilsets an expiry of at most 180 days. - Queries. API wallets only sign. Query with the master or sub-account address; the docs call using the agent address a common pitfall that returns an empty result.
- Permissions. The SDK's
basic_agent.pyexample states that the agent has no permission to transfer or withdraw funds. The exchange endpoint page labels two balance actions as agent-signed:agentSendAsset, whose destination must match the source address, and a staking deposit. This was not tested on a funded account. An agent can also trade, and trading can lose the balance. - No reuse. A wallet and its nonce state may be pruned when it is deregistered, when it expires, or when the registering account has no funds. The docs warn that previously signed actions can then be replayed, so generate a new agent instead of reusing an address.
This sketch places a hypothetical post-only order (Alo) and cancels it if it rests. The other time-in-force values are Ioc and Gtc.
import os
import eth_account
from hyperliquid.exchange import Exchange
from hyperliquid.utils import constants
MASTER = os.environ["HL_TESTNET_ACCOUNT"] # public address of the main wallet
agent = eth_account.Account.from_key(os.environ["HL_TESTNET_AGENT_KEY"])
ex = Exchange(agent, constants.TESTNET_API_URL, account_address=MASTER)
result = ex.order("BTC", True, 0.001, 60000.0, {"limit": {"tif": "Alo"}})
print(result)
if result["status"] == "ok":
status = result["response"]["data"]["statuses"][0]
if "resting" in status:
print(ex.cancel("BTC", status["resting"]["oid"]))Run on 4 November 2026 with a generated, unapproved key on testnet, it returned this, with the address masked:
{'status': 'err', 'response': 'User or API Wallet 0x... does not exist.'}The address matched the signer here because the wallet was never approved. A bad signature gives the same error, with an address that differs from your wallet and changes with each input.
L1 actions vs user-signed actions
Hyperliquid has two signing schemes, and the docs put confusing them first on their list of common errors.
| Scheme | SDK function | Examples | Signed data |
|---|---|---|---|
| L1 action | sign_l1_action | Orders and cancels | A hash of the msgpack-encoded action, wrapped in a typed message with chain ID 1337 |
| User-signed action | sign_user_signed_action | USDC transfers, withdraw3, approveAgent | Typed data carrying hyperliquidChain and signatureChainId |
The documentation recommends an existing SDK over manual signing, and an incorrect signature gives no reason why it failed. Besides mixing up the schemes, its list covers msgpack field order, trailing zeros on numbers, uppercase characters in addresses, and trusting a local signer-recovery check whose payload may not match the real one. Lowercase addresses before signing. Porting to another language means comparing your output with the Python SDK until it matches.
Nonce rules for a Hyperliquid trading bot
Nonces are not a strict sequence here: the 100 highest are stored per signer, and a new one must exceed the smallest of them and never have been used.
A nonce must also fall within (T - 2 days, T + 1 day), where T is the block's Unix millisecond timestamp. Nonces belong to the signer, so sub-accounts signed by one API wallet share one set. The docs therefore suggest one API wallet per trading process and, for parallel sub-accounts, one per sub-account. Their porting pattern is a task that batches orders and cancels every 0.1 seconds, with IOC and GTC orders batched apart from ALO orders, and an atomic counter for nonces:
import threading, time
class NonceCounter:
"""One per API wallet: unique, increasing, close to the clock."""
def __init__(self):
self._last = 0
self._lock = threading.Lock()
def next(self):
with self._lock:
self._last = max(self._last + 1, int(time.time() * 1000))
return self._lastEight threads drew 40,000 values from this class in a local test, and all were unique. SDK 0.24.0 stamps its Exchange calls with the current millisecond itself, so with the SDK the simple rule is one sending thread per API wallet. The counter applies if you build and sign batches yourself.
Hyperliquid API rate limit: IP weight and address budget
Two separate limits apply: 1,200 REST weight per minute per IP, and a per-address budget for actions that grows with traded volume.
| Limit | Value |
|---|---|
| REST weight per IP | 1,200 per minute |
exchange request weight | 1 + floor(batch_length / 40) |
info weight 2 | l2Book, allMids, clearinghouseState, orderStatus, spotClearinghouseState, exchangeStatus |
Other documented info requests | Weight 20 (userRole 60), plus extra weight per items returned on some, such as candles and fills |
| WebSocket per IP | 10 connections, 30 new per minute, 1,000 subscriptions, 10 unique users, 2,000 messages per minute |
| Address budget | 1 request per 1 USDC traded since inception, plus a 10,000 buffer; one request every 10 seconds once limited |
| Cancel budget | min(limit + 100000, limit * 2) |
| Open orders | 1,000 plus 1 per 5M USDC of volume, capped at 5,000 |
Hypothetical example. An address has traded 2,000 USDC in total. Its budget is 2,000 + 10,000 = 12,000 requests, and its cancel budget is min(112,000, 24,000) = 24,000. A batch of 40 orders has an IP weight of 1 + floor(40 / 40) = 2 but costs 40 requests from the address budget.
A bot that quotes a lot and fills little runs out of budget first; the docs note that a 100 USDC order needs a 1% fill rate to break even on requests. The userRateLimit info request returns cumVlm, nRequestsUsed and nRequestsCap. On testnet on 4 November 2026, an address with no volume returned a cap of 10,000.
Hyperliquid WebSocket in Python
Leave out skip_ws=True and the same Info client opens a WebSocket that delivers subscriptions to a callback.
import time
from hyperliquid.info import Info
from hyperliquid.utils import constants
info = Info(constants.TESTNET_API_URL) # also opens the WebSocket
def on_book(msg):
bid, ask = msg["data"]["levels"][0][0], msg["data"]["levels"][1][0]
print(msg["channel"], bid["px"], ask["px"])
sub = {"type": "l2Book", "coin": "BTC"}
sub_id = info.subscribe(sub, on_book)
time.sleep(5)
info.unsubscribe(sub, sub_id)
info.disconnect_websocket()On testnet on 4 November 2026 this printed lines such as l2Book 79808.0 79832.0. The server closes a connection it has sent nothing to for 60 seconds, so quiet channels need a {"method": "ping"} heartbeat, which the SDK sends every 50 seconds. The docs say disconnects can happen without announcement and that automated clients should handle them and reconnect. The websocket_manager.py source has no reconnect code, so your process must notice silence, reconnect and resubscribe. Missed data comes in the reconnect snapshot or through the matching info request, the docs say. Keeping a correct local book is covered in the order book guide.
Hyperliquid API private key: the rules to keep
A wallet private key is the account's authority, so treat it as money rather than as a credential.
- Never put the main wallet's key on the bot's server. The bot gets an API wallet key only.
- No server can revoke a wallet key the way an exchange revokes an API key. If the main key leaks, move funds to a new wallet.
- Set an expiry on API wallets. To replace one, approve a new unnamed wallet, or a named wallet with the same name, and the old one is deregistered.
- Keep keys out of code, repositories, chats and logs. General storage and logging practice is in API key security for trading bots.
- The API pages read on 4 November 2026 describe no IP allowlist for API wallets, so do not assume one exists.
Add limits outside the strategy code. scheduleCancel is a documented dead man's switch: it cancels all open orders at a set time at least 5 seconds ahead, with at most 10 triggers per day. It does not close positions. One third-party analysis of the node software reports that it is refused until an address has traded $1,000,000, which the docs do not mention, so confirm it works on your account first. Independent size and loss limits belong in a risk layer, and testnet success says nothing about strategy results. The same terms prohibit wash trading, spoofing and automation that bypasses rate limits. Perpetual contracts carry funding costs and liquidation risk, explained in perpetual futures and funding rate.
Frequently asked questions
Does Hyperliquid have an API key?
Not in the usual key-and-secret sense. Every action is signed with a wallet private key. What the SDK README calls an API key is the private key of an approved API wallet.
Can a Hyperliquid API wallet withdraw funds?
The official SDK's agent example says it has no permission to transfer or withdraw funds, though the docs label a same-address asset move and a staking deposit as agent-signed. This was not tested on a funded account, and a leaked agent key can still lose money through trading.
What does User or API Wallet does not exist mean?
The address recovered from your signature is not a known user or approved wallet. If the address shown is your signer, the wallet is probably not approved. If it differs and changes with each request, the signature is wrong.
What is the Hyperliquid API rate limit?
As of 4 November 2026, 1,200 REST weight per minute per IP, plus an address budget of 1 request per 1 USDC traded with a 10,000 starting buffer. A batch counts once for the IP limit, with weight rising every 40 items, but per order for the address budget.
How do I get Hyperliquid testnet funds?
The faucet page says the same address must already have deposited on mainnet, after which it can receive 1,000 mock USDC.
Sources
- Hyperliquid Docs: API overview (hosts and SDKs) (opens in a new tab)
- Hyperliquid Docs: Nonces and API wallets (opens in a new tab)
- Hyperliquid Docs: Signing (opens in a new tab)
- Hyperliquid Docs: Rate limits and user limits (opens in a new tab)
- Hyperliquid Docs: Tick and lot size (opens in a new tab)
- Hyperliquid Docs: Info endpoint (opens in a new tab)
- Hyperliquid Docs: Exchange endpoint (orders, approveAgent, scheduleCancel) (opens in a new tab)
- Hyperliquid Docs: WebSocket (opens in a new tab)
- Hyperliquid Docs: WebSocket timeouts and heartbeats (opens in a new tab)
- Hyperliquid Docs: Testnet faucet (opens in a new tab)
- hyperliquid-python-sdk on GitHub (README, MIT license) (opens in a new tab)
- hyperliquid-python-sdk: examples/basic_agent.py (opens in a new tab)
- Hyperliquid: Terms of Use, last updated 15 June 2026 (opens in a new tab)
- PyPI: hyperliquid-python-sdk 0.24.0 (opens in a new tab)
- Capital Markets Law No. 6362, consolidated text, Article 99/A (Turkish) (opens in a new tab)
This guide is general information about software and markets, not investment, legal or tax advice. Automated trading can lose money, past or backtested performance does not guarantee future results, and the rules that apply depend on your jurisdiction and your exchange's terms. Sources, checks and corrections: see the editorial policy.