# Hyperliquid API in Python: API wallets, signing, limits

URL: https://fincodelabs.com/en/blog/hyperliquid-api-python · Publisher: Fincode (Fincode Yazılım ve Teknoloji A.Ş.), Istanbul, Turkey · Category: Exchange APIs · Published: 2026-11-05 · Updated: 2026-11-05

Hyperliquid API Python guide on testnet: the official SDK, API wallets, L1 and user-signed actions, nonce rules, rate limits, WebSocket and private-key risk.

**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).

**Hyperliquid API hosts**

| 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](https://fincodelabs.com/en/blog/ccxt-python-trading-bot).

## 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.

```python
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 20
```

`szDecimals` 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 and `account_address` stays 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_until` sets 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.py` example 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`.

```python
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.

**Signing schemes in SDK 0.24.0**

| 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:

```python
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._last
```

Eight 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.

**Limits in the documentation, 4 November 2026**

| 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.

```python
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](https://fincodelabs.com/en/blog/websocket-order-book).

## 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](https://fincodelabs.com/en/blog/api-key-security-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](https://fincodelabs.com/en/blog/trading-bot-risk-management), 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](https://fincodelabs.com/en/blog/perpetual-futures-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)](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api)
- [Hyperliquid Docs: Nonces and API wallets](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/nonces-and-api-wallets)
- [Hyperliquid Docs: Signing](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/signing)
- [Hyperliquid Docs: Rate limits and user limits](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/rate-limits-and-user-limits)
- [Hyperliquid Docs: Tick and lot size](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/tick-and-lot-size)
- [Hyperliquid Docs: Info endpoint](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint)
- [Hyperliquid Docs: Exchange endpoint (orders, approveAgent, scheduleCancel)](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint)
- [Hyperliquid Docs: WebSocket](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket)
- [Hyperliquid Docs: WebSocket timeouts and heartbeats](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/timeouts-and-heartbeats)
- [Hyperliquid Docs: Testnet faucet](https://hyperliquid.gitbook.io/hyperliquid-docs/onboarding/testnet-faucet)
- [hyperliquid-python-sdk on GitHub (README, MIT license)](https://github.com/hyperliquid-dex/hyperliquid-python-sdk)
- [hyperliquid-python-sdk: examples/basic_agent.py](https://github.com/hyperliquid-dex/hyperliquid-python-sdk/blob/master/examples/basic_agent.py)
- [Hyperliquid: Terms of Use, last updated 15 June 2026](https://app.hyperliquid.xyz/terms)
- [PyPI: hyperliquid-python-sdk 0.24.0](https://pypi.org/project/hyperliquid-python-sdk/)
- [Capital Markets Law No. 6362, consolidated text, Article 99/A (Turkish)](https://www.mevzuat.gov.tr/mevzuatmetin/1.5.6362.pdf)

> 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](https://fincodelabs.com/en/editorial-policy).

---

Published by Fincode (Fincode Yazılım ve Teknoloji A.Ş.), a software company in Istanbul, Turkey: https://fincodelabs.com/en/blog/hyperliquid-api-python

Cite as: Fincode, "Hyperliquid API in Python: API wallets, signing, limits", updated November 5, 2026, https://fincodelabs.com/en/blog/hyperliquid-api-python
