All articles
Hyperliquid·June 11, 2026·6 min read

Hyperliquid API Trading Bot: Python & Rust Setup Guide

Build a Hyperliquid API trading bot in Python or Rust: SDK setup, EIP-712 order signing, nonce management and rate limits for a live perps bot.

Your first live order on Hyperliquid will get rejected, and it will almost always be for one of three reasons: a stale nonce, an EIP-712 payload that doesn't match what the exchange expects, or an asset index you hardcoded wrong. Get past those three and the rest of the build is mechanical. This guide walks the full path — Python for the first version, Rust when latency starts to matter — including the order signing that trips up everyone the first time.

Which SDK, and why start in Python

The official hyperliquid-python-sdk handles signing, the info endpoints, and websocket subscriptions out of the box. For a first bot, it's the fastest way to a working order. Install it and you're a few lines from a live position:

from hyperliquid.exchange import Exchange
from hyperliquid.info import Info
from eth_account import Account

wallet = Account.from_key(PRIVATE_KEY)
info = Info(base_url="https://api.hyperliquid.xyz")
exchange = Exchange(wallet, base_url="https://api.hyperliquid.xyz")

# market-ish limit order: buy 0.01 ETH, IOC, price-capped
result = exchange.order("ETH", True, 0.01, 3200.0,
                        {"limit": {"tif": "Ioc"}})
print(result)

That True is the is_buy flag. The price is a hard cap, not a market fill — Hyperliquid has no true market order, so you send an aggressive IOC limit and let it cross the book. If you skip the price cap you'll get a rejection, not a fill at any price.

Rust (hyperliquid-rust-sdk) is where you go once the bot needs sub-millisecond signing or you're running dozens of instruments in one process. The API surface mirrors the Python one closely enough that porting logic across is boring rather than painful. Most teams I've built for prototype the strategy in Python, then move only the hot path to Rust once the behavior is nailed down.

EIP-712 signing is the part that bites

Every order, cancel, and transfer is an L1 action signed as EIP-712 typed data. The SDK builds the struct for you, but you need to understand what it's signing because a mismatched field is an opaque "error": "..." with no hint about which byte is wrong.

The action gets serialized with msgpack, hashed, and combined with the nonce and an optional vault address into the connection payload. Two gotchas that cost real time:

  • Chain matters. Mainnet and testnet sign against different domains. Point the SDK at the wrong base_url and your testnet signature is valid but meaningless to mainnet, which rejects it as garbage.
  • Agent wallets. For production you almost never sign with your main key. You approve an API wallet (an "agent") that can trade but not withdraw. The agent signs orders; the master account still owns the funds. If you're running any strategy touching real size, this is non-negotiable — a leaked trading key should never be a leaked withdrawal key.

If you're hand-rolling the signing in Rust instead of trusting the SDK, dump the exact bytes the Python SDK produces for an identical order and diff them. That side-by-side has saved me hours more than once.

Nonces: the rule people learn the hard way

Hyperliquid nonces are millisecond timestamps, and the exchange enforces a strict window. The current rule: a nonce must be larger than the smallest nonce among your last 20 in-flight actions, and within roughly the last two days but not more than a day in the future. Practically, that means:

  • Generate nonces from a monotonic clock, not time.time() raw. Two orders fired in the same millisecond collide, and the second one dies.
  • If you fan out orders across threads or async tasks, serialize nonce assignment behind a single counter. A shared max(last_nonce + 1, now_ms) is the whole fix.
  • Running two processes on the same API wallet is asking for nonce interleaving. Give each agent wallet its own process, or funnel everything through one order gateway.

That last point is why a clean architecture puts a single signing service in front of the exchange, even in a Python prototype. It's also the pattern that pays off when you scale a Hyperliquid perps execution bot to many markets at once.

Rate limits are address-weighted, not per-endpoint

The limit model is weight-based and tied to your account, not a flat requests-per-second cap. Info requests cost weight; exchange actions cost weight; the budget refills continuously and scales with your traded volume. A cold wallet with no volume gets a small budget, which surprises people testing a fresh account.

Two habits keep you under the ceiling:

  • Prefer websockets for state. Subscribe to l2Book, userFills, and userEvents instead of polling the info endpoint. Polling burns your weight budget for data the stream gives you for free.
  • Batch cancels. bulk_cancel and bulk_orders cost far less weight than firing individual requests in a loop. A market maker replacing quotes should always batch.

When you do hit a 429, back off with jitter and stop hammering — repeated violations get your IP throttled harder than the account limit alone.

Wiring it into a real loop

A live bot is four moving parts: a websocket feed keeping local state, a strategy deciding target orders, a reconciler diffing desired versus resting orders, and the signer pushing the delta. The reconciler is where correctness lives — never blindly cancel-all-and-resubmit on every tick. Diff, cancel only what changed, place only what's new. That single decision cuts your action count by an order of magnitude and keeps you comfortably inside the rate budget.

For anything that quotes both sides, the same skeleton underpins a Hyperliquid market-making system; the inventory and skew logic sits on top of exactly this plumbing. If you're chasing funding instead, the mechanics of holding delta-neutral inventory are covered in our writeup on funding-rate arbitrage against a CEX, and the funding-arb bot service uses the same signer and reconciler described here.

A few things that will still surprise you

  • Tick and lot sizes are per-asset. Query meta and round prices to the asset's szDecimals before signing, or you'll eat rejections that look like signing failures but aren't.
  • Reduce-only means reduce-only. Set the flag when closing; without it a "close" that overshoots flips you to the other side.
  • The asset is an index, not a ticker, in the raw payload. The SDK maps "ETH" to its index for you — if you drop to raw calls, that mapping is on you, and getting it wrong sends your order to the wrong market.

Once the entry and reconciliation are solid, the same event stream feeds risk logic — liquidation monitoring, for one. The clearinghouse mechanics behind that are worth reading in our liquidation-bot and clearinghouse breakdown, and if you want the yield side, the HLP vault strategy explainer covers where a lot of this order flow ultimately lands. Watching fills and margin in real time is a lot easier with a proper trading dashboard than with print statements.

Build the Python version first, prove the strategy, then decide what actually needs Rust — and if you'd rather skip straight to a production-grade signer and reconciler, that's the core of what we ship in a Hyperliquid perps bot build.

Need a bot like this built?

We design, build and run trading bots on Solana, Hyperliquid and Polymarket.

Start a project
#Hyperliquid#Python#Rust#Perps#API