Hyperliquid SDK
Complete reference for ctx.hyperliquid — programmatic access to the Hyperliquid on-chain perpetuals DEX from worker code.
Read-only adapter — by design
Hyperliquid is an on-chain DEX. Every order is signed in the user's browser via EIP-712 with their MetaMask / WalletConnect wallet. The private key never touches our server.
This means workers cannot place / cancel / modify orders directly. The ctx.hyperliquid adapter is read-only — it exposes every public
- per-user state endpoint, but trading must go through the canvas block (which has wallet access).
For trading flows, the recommended pattern is: worker computes signal → emits a BlockEvent → Hyperliquid block on the canvas catches it and signs the order in the browser. See Recipes.
Setup
- Add a Hyperliquid trading block to your workspace canvas.
- Click "Connect Wallet" inside the block — MetaMask / WalletConnect / Phantom.
- The wallet address is persisted to the block config.
- Connect the block to your Worker block via an edge.
ctx.hyperliquidis now available.
def tick(ctx):
state = ctx.hyperliquid.get_account_state()
equity = state["marginSummary"]["accountValue"]
ctx.log.info(f"HL equity: ${equity}")If no Hyperliquid block is connected, public methods still work, but per-user methods raise:
RuntimeError: No trading block connected. Connect a Hyperliquid block to this worker.Modes
| Mode | Endpoint | Notes |
|---|---|---|
live | api.hyperliquid.xyz | Real positions on mainnet |
testnet | api.hyperliquid-testnet.xyz | Free testnet — airdrop testnet USDC at the testnet UI |
Set on the API key — no per-call override. (You can pass api_mode="testnet" to public methods to override.)
Public Market Data
These methods don't need a connected block.
get_meta
Universe metadata: list of perp assets, max leverage, decimals.
ctx.hyperliquid.get_meta(api_mode: str = "live") -> dictmeta = ctx.hyperliquid.get_meta()
# meta["universe"] = [{"name": "BTC", "szDecimals": 5, "maxLeverage": 50, ...}, ...]get_meta_and_contexts
Meta + per-asset live context (mark, oracle, OI, funding) in one call. Most efficient way to scan the whole universe.
ctx.hyperliquid.get_meta_and_contexts(api_mode: str = "live") -> list
# Returns [meta_dict, [asset_ctx_dict, ...]] (parallel arrays by asset index)get_spot_meta / get_spot_meta_and_contexts
Same shape but for the spot universe.
ctx.hyperliquid.get_spot_meta(api_mode: str = "live") -> dict
ctx.hyperliquid.get_spot_meta_and_contexts(api_mode: str = "live") -> listget_all_mids
All-asset mid prices in one call. Fastest snapshot of the whole market.
ctx.hyperliquid.get_all_mids(api_mode: str = "live") -> dict
# {"BTC": "67234.5", "ETH": "3520.1", "SOL": "152.4", ...}Use this for scanners
Single REST call for every asset's mid — way faster than per-asset l2Book.
get_l2_book
L2 order book for a specific coin.
ctx.hyperliquid.get_l2_book(coin: str, api_mode: str = "live") -> dictReturns: {"coin", "time", "levels": [[bid_levels], [ask_levels]]} where each level is {"px", "sz", "n"}.
get_candles
OHLCV candles.
ctx.hyperliquid.get_candles(
coin: str,
interval: str = "1h",
start_time: int = None, # ms timestamp
end_time: int = None,
api_mode: str = "live",
) -> listinterval | Values |
|---|---|
| Sub-hour | "1m", "3m", "5m", "15m", "30m" |
| Hours | "1h", "2h", "4h", "8h", "12h" |
| Days+ | "1d", "3d", "1w", "1M" |
get_funding_history
Historical funding rate for a perpetual.
ctx.hyperliquid.get_funding_history(
coin: str,
start_time: int = None,
end_time: int = None,
api_mode: str = "live",
) -> listget_vault_details
Public vault info: APY, depositors, position composition.
ctx.hyperliquid.get_vault_details(vault_address: str, api_mode: str = "live") -> dictPer-User State
These need a Hyperliquid block connected so the SDK can read the wallet address from its cfg.
get_account_state
Full account state: positions, cross-margin summary, withdrawable balance.
ctx.hyperliquid.get_account_state() -> dictReturns: {"marginSummary": {"accountValue", "totalNtlPos", "totalRawUsd", "totalMarginUsed"}, "crossMarginSummary": {...}, "crossMaintenanceMarginUsed", "withdrawable", "assetPositions": [{...}], "time"}
s = ctx.hyperliquid.get_account_state()
equity = float(s["marginSummary"]["accountValue"])
free = float(s["withdrawable"])
ctx.log.info(f"Equity: ${equity:.2f}, free: ${free:.2f}")
for p in s["assetPositions"]:
pos = p["position"]
if float(pos["szi"]) != 0:
ctx.log.info(f"{pos['coin']}: size={pos['szi']}, "
f"entry={pos['entryPx']}, "
f"PnL={pos['unrealizedPnl']}")get_spot_balances
Spot wallet balances per token.
ctx.hyperliquid.get_spot_balances() -> dictget_open_orders / get_frontend_open_orders
Active (unfilled) orders. The frontend_* variant adds extra trigger-info fields used by the Hyperliquid UI for stop / TP-SL orders.
ctx.hyperliquid.get_open_orders() -> list
ctx.hyperliquid.get_frontend_open_orders() -> listget_user_fills
Recent fills (executed trades) with fees.
ctx.hyperliquid.get_user_fills() -> listget_user_fills_by_time
Fills within a time range.
ctx.hyperliquid.get_user_fills_by_time(start_time: int, end_time: int = None) -> list
# timestamps in msget_user_funding
Funding payments history.
ctx.hyperliquid.get_user_funding(
start_time: int = None,
end_time: int = None,
) -> listget_historical_orders
All orders ever placed (filled, cancelled, rejected).
ctx.hyperliquid.get_historical_orders() -> listget_ledger_updates
Non-funding ledger: deposits, withdrawals, transfers, internal moves.
ctx.hyperliquid.get_ledger_updates(start_time: int = None, end_time: int = None) -> listget_sub_accounts
User's Hyperliquid sub-accounts (their isolation primitive).
ctx.hyperliquid.get_sub_accounts() -> listget_referral
Referral code, rewards earned, downstream users.
ctx.hyperliquid.get_referral() -> dictget_user_vault_equities
Positions in vaults the user has deposited into.
ctx.hyperliquid.get_user_vault_equities() -> listCommon Patterns
How to "trade" from a worker
You can't sign orders from a worker (by design). The pattern is:
- Worker computes a signal and emits a
BlockEvent:pythonctx.emit("hl_signal", { "coin": "BTC", "side": "buy", "size": 0.001, "limit_price": 65000, }) - Hyperliquid block on the canvas listens for
hl_signalevents and signs+sends the order via the user's wallet.
This keeps the security boundary intact: the worker has read access to state for decision-making, the wallet does the signing.
Error handling
def tick(ctx):
try:
state = ctx.hyperliquid.get_account_state()
except Exception as e:
ctx.log.error(f"Hyperliquid unreachable: {e}")
returnHyperliquidAPIError is raised for HTTP errors with status and body attributes.
Cloud-Run proxy
PRO+ workers route Hyperliquid traffic through a Cloud Run proxy with rotating exit IPs. Transparent — no code changes.
Recipes
Position-aware risk monitor
Watch positions and alert on margin pressure.
def tick(ctx):
s = ctx.hyperliquid.get_account_state()
summary = s["marginSummary"]
used = float(summary["totalMarginUsed"])
equity = float(summary["accountValue"])
util = used / equity if equity else 0
if util > 0.7:
ctx.log.warn(f"Margin utilization {util:.1%} — getting hot")
ctx.monitor.alert("HL margin > 70%", level="warn")
elif util > 0.9:
ctx.log.error(f"Margin utilization {util:.1%} — danger")
ctx.monitor.alert("HL margin > 90%", level="critical")Funding-rate income tracker
def tick(ctx):
import time
now_ms = int(time.time() * 1000)
day_ago = now_ms - 86400 * 1000
funding = ctx.hyperliquid.get_user_funding(
start_time=day_ago, end_time=now_ms,
)
total = sum(float(f["delta"]["usdc"]) for f in funding)
ctx.log.info(f"24h net funding: ${total:+.4f}")
ctx.monitor.metric("hl_funding_24h", total)Cross-asset signal generator (worker → on-canvas trade)
def tick(ctx):
mids = ctx.hyperliquid.get_all_mids()
candles = ctx.hyperliquid.get_candles("BTC", interval="1h", start_time=...)
closes = [float(c["c"]) for c in candles]
sma_24 = sum(closes[-24:]) / 24
last = float(mids["BTC"])
# No direct trading from worker — emit signal for the canvas block
if last > sma_24 * 1.005:
ctx.emit("hl_signal", {"coin": "BTC", "side": "buy", "size": 0.001})
ctx.log.info(f"Long signal: {last} > SMA24 {sma_24:.2f}")
elif last < sma_24 * 0.995:
ctx.emit("hl_signal", {"coin": "BTC", "side": "sell", "size": 0.001})
ctx.log.info(f"Short signal: {last} < SMA24 {sma_24:.2f}")Vault performance scanner
def tick(ctx):
# The user's address would be in their Hyperliquid block cfg;
# for vault-finding we hardcode well-known vault addresses or
# discover via meta endpoints elsewhere.
famous = ["0x...vault1...", "0x...vault2..."]
for v in famous:
d = ctx.hyperliquid.get_vault_details(v)
apr = d.get("apr", 0)
tvl = d.get("tvl", 0)
ctx.log.info(f"{d.get('name', v[:10])} APR={apr:.1%} TVL=${tvl:,.0f}")Reference
| Hyperliquid endpoint | SDK method | Auth needed |
|---|---|---|
POST /info {type:"meta"} | get_meta | no |
POST /info {type:"metaAndAssetCtxs"} | get_meta_and_contexts | no |
POST /info {type:"spotMeta"} | get_spot_meta | no |
POST /info {type:"spotMetaAndAssetCtxs"} | get_spot_meta_and_contexts | no |
POST /info {type:"allMids"} | get_all_mids | no |
POST /info {type:"l2Book"} | get_l2_book | no |
POST /info {type:"candleSnapshot"} | get_candles | no |
POST /info {type:"fundingHistory"} | get_funding_history | no |
POST /info {type:"vaultDetails"} | get_vault_details | no |
POST /info {type:"clearinghouseState"} | get_account_state | wallet addr |
POST /info {type:"spotClearinghouseState"} | get_spot_balances | wallet addr |
POST /info {type:"openOrders"} | get_open_orders | wallet addr |
POST /info {type:"frontendOpenOrders"} | get_frontend_open_orders | wallet addr |
POST /info {type:"userFills"} | get_user_fills | wallet addr |
POST /info {type:"userFillsByTime"} | get_user_fills_by_time | wallet addr |
POST /info {type:"userFunding"} | get_user_funding | wallet addr |
POST /info {type:"historicalOrders"} | get_historical_orders | wallet addr |
POST /info {type:"userNonFundingLedgerUpdates"} | get_ledger_updates | wallet addr |
POST /info {type:"subAccounts"} | get_sub_accounts | wallet addr |
POST /info {type:"referral"} | get_referral | wallet addr |
POST /info {type:"userVaultEquities"} | get_user_vault_equities | wallet addr |
POST /exchange (signed) | not exposed — use canvas block | wallet signature |