Hyperliquid Testnet: Faucet, API URLs and Mainnet Gotchas

How to test a Hyperliquid app on testnet: test USDC and HYPE, the API, WebSocket and HyperEVM addresses, and what breaks when you move to mainnet.

HydromancerTeam
Hyperliquid Testnet: Faucet, API URLs and Mainnet Gotchas

Hyperliquid's testnet is a separate copy of the exchange where you trade with mock USDC. Use it to test order logic, signing and error handling before real money is involved.

Setup is quick. The harder part is moving your code to mainnet, where several things work differently. This guide covers both. For endpoints, API keys and limits in general, see our Hyperliquid API guide. If you don't write code, skip the code blocks and read the results.

Hyperliquid testnet addresses

Part Testnet Mainnet
Appapp.hyperliquid-testnet.xyzapp.hyperliquid.xyz
API (info and exchange)https://api.hyperliquid-testnet.xyzhttps://api.hyperliquid.xyz
WebSocketwss://api.hyperliquid-testnet.xyz/wswss://api.hyperliquid.xyz/ws
HyperEVM RPChttps://rpc.hyperliquid-testnet.xyz/evmhttps://rpc.hyperliquid.xyz/evm
HyperEVM chain ID998999
Block explorerapp.hyperliquid-testnet.xyz/explorerapp.hyperliquid.xyz/explorer

The Python SDK has both API addresses built in as constants.TESTNET_API_URL and constants.MAINNET_API_URL.

Hyperliquid testnet faucet: how to get test USDC

Hyperliquid's own faucet is the easiest, but you can claim only once, and only after a mainnet deposit. Circle's faucet works without a deposit. Your options:

Source What you get The catch
Hyperliquid faucet1,000 mock USDC, straight
into your testnet trading
account
The same address must have
deposited on mainnet. One
claim per address
Circle faucet, network "HyperEVM Testnet"20 USDC per request, no
account or deposit needed
One request every 2 hours
per network. It arrives on
HyperEVM, so you move it
into your trading account
yourself (see below)
Circle faucet on Arbitrum Sepolia, then Circle's bridge (CCTP)20 USDC per request, less a
0.20 USDC forwarding fee,
sent to your trading account
Needs Arbitrum Sepolia ETH
for gas. Mostly for teams
testing deposit flows
Another testerAny amountA teammate who already has
test USDC can send it to
your address

If you sign in to the Hyperliquid app with email, it creates a different wallet address on mainnet and testnet. Export the mainnet wallet and import it into a browser wallet so you use the same address on both, or the faucet won't see your mainnet deposit.

Moving Circle's USDC into your trading account. Testnet USDC from Circle's faucet lands on HyperEVM, at 0x2B3370eE501B4a559b57D449569354196457D8Ab. To trade with it, approve and deposit it into Circle's deposit contract, 0x0B80659a4076E9E93C7DbE0f10675A16a3e5C206, which credits your Hyperliquid testnet account. Testnet spotMeta lists this contract as USDC's link between HyperEVM and the trading layer. We saw other testers' deposits going through it, but didn't send one ourselves.

How to get testnet HYPE for HyperEVM

Mock USDC is for trading on HyperCore, Hyperliquid's trading layer. To send transactions or deploy contracts on HyperEVM testnet, you need testnet HYPE to pay gas. You can make your own with test USDC:

  1. Get test USDC into your trading account, with Hyperliquid's faucet or Circle's Arbitrum Sepolia route above. Neither needs HyperEVM gas.
  2. Sell a few dollars of it for HYPE on the testnet HYPE/USDC spot market (@1035).
  3. Send the HYPE to 0x2222222222222222222222222222222222222222, which moves it to your address on HyperEVM.

From then on, you can claim 20 USDC from Circle's HyperEVM faucet, move it into your trading account and sell it for more HYPE whenever you need to.

Step 3 comes from Hyperliquid's docs on moving HYPE between HyperCore and HyperEVM. We checked that the testnet HYPE/USDC market was listed and trading when we tested, but didn't run the transfer.

Create an API wallet on testnet

An API wallet is a separate key that signs trades for your account, so your main wallet's key stays out of your code. Approve one in the testnet app. Approvals don't carry across networks: the API wallet we approved on testnet isn't approved on mainnet.

Place and cancel a test order

This places a small buy far below the market, which should rest on the book rather than fill. It then cancels the order and lists your open orders. Set HL_SECRET_KEY to your testnet API wallet's key and HL_ACCOUNT_ADDRESS to your testnet main wallet.

code
import os
import eth_account
from hyperliquid.exchange import Exchange
from hyperliquid.info import Info
from hyperliquid.utils import constants

URL = constants.TESTNET_API_URL
wallet = eth_account.Account.from_key(os.environ["HL_SECRET_KEY"])   # an API wallet made on testnet
account = os.environ["HL_ACCOUNT_ADDRESS"]                          # your testnet main wallet

info = Info(URL, skip_ws=True)
exchange = Exchange(wallet, URL, account_address=account)

price = round(float(info.all_mids()["BTC"]) * 0.5)   # far below the market, so it won't fill
result = exchange.order("BTC", True, 0.0003, price, {"limit": {"tif": "Gtc"}})

if result["status"] != "ok":
    raise SystemExit(result)
status = result["response"]["data"]["statuses"][0]
print("Order:", status)               # can be {"error": ...} even when status is "ok"

if "resting" in status:
    print("Cancel:", exchange.cancel("BTC", status["resting"]["oid"])["response"]["data"]["statuses"])
print("Open orders now:", info.open_orders(account))

It printed this (your order ID will differ):

code
Order: {'resting': {'oid': 63428174589}}
Cancel: ['success']
Open orders now: []

Notice the order status check. A rejected order can still come back with a top-level "status": "ok", with the reason inside the order's status. Testnet is the safe place to learn that.

Stream testnet trades

The WebSocket works the same way as on mainnet, at the testnet address. This prints the subscription confirmation, then the first trade from each of the next two messages (a message can hold several trades):

code
import json
import websocket   # installed with the SDK (websocket-client)

ws = websocket.create_connection("wss://api.hyperliquid-testnet.xyz/ws", timeout=30)
ws.send(json.dumps({"method": "subscribe", "subscription": {"type": "trades", "coin": "BTC"}}))
print(ws.recv())                       # the subscription confirmation
for _ in range(2):
    print(json.loads(ws.recv())["data"][0])
ws.close()

On mainnet, a connection with no messages for 60 seconds is dropped, so send a ping on quiet subscriptions. Our WebSocket guide covers reconnecting.

What's different on testnet

Hardcoded testnet values can break your code, or send the wrong order, on mainnet. These are the differences we found:

Asset IDs don't match

Orders refer to markets by number, and the numbers differ between networks:

code
from hyperliquid.info import Info
from hyperliquid.utils import constants

def asset_ids(url):
    universe = Info(url, skip_ws=True).meta()["universe"]
    return {asset["name"]: i for i, asset in enumerate(universe)}

test, main = asset_ids(constants.TESTNET_API_URL), asset_ids(constants.MAINNET_API_URL)
for coin in ["BTC", "ETH", "SOL", "HYPE"]:
    print(f"{coin}: mainnet asset {main[coin]}, testnet asset {test[coin]}")
code
BTC: mainnet asset 0, testnet asset 3
ETH: mainnet asset 1, testnet asset 4
SOL: mainnet asset 5, testnet asset 0
HYPE: mainnet asset 159, testnet asset 135

Of the 207 perps on both networks, only one had the same ID. Spot pairs differ too: HYPE/USDC is named @107 on mainnet and @1035 on testnet (asset IDs 10107 and 11035 in orders). Look IDs up from meta and spotMeta on the network you're using, every time. For perps, using names through the SDK, as the examples here do, avoids the problem; spot pair names like @107 change between networks too, so look those up as well.

Prices aren't mainnet prices

Testnet has its own order books and traders, so prices drift from mainnet. Here are both, read at the same moment:

code
from hyperliquid.info import Info
from hyperliquid.utils import constants

testnet = Info(constants.TESTNET_API_URL, skip_ws=True)
mainnet = Info(constants.MAINNET_API_URL, skip_ws=True)

for coin in ["BTC", "ETH", "HYPE"]:
    print(coin, "testnet:", testnet.all_mids()[coin], "| mainnet:", mainnet.all_mids()[coin])
code
BTC testnet: 81679.5 | mainnet: 81332.5
ETH testnet: 2379.2 | mainnet: 2383.95
HYPE testnet: 86.4825 | mainnet: 77.3055

Liquidity differs too. Don't judge slippage, fill rates or a strategy's results from testnet. Testnet is for checking that your code works. For backtests or data pipelines that need real market history, use mainnet data, such as the free Hyperliquid historical data in Reservoir (you only pay AWS transfer costs).

Market settings differ

Maximum leverage was different on 101 of the 207 shared perps, and size decimals (szDecimals, how finely you can size an order) on 7. Testnet also had 272 HIP-3 perp DEXs (markets deployed by third parties) against 10 on mainnet. Read limits from meta on the network you trade on.

Signatures are network-specific

Signatures are tied to the network they're made for, so a testnet signature won't work on mainnet. The SDK handles this from the URL you give it. If you sign requests yourself, check you've switched the network setting too.

The HyperEVM is a different chain

HyperEVM testnet is chain 998, and mainnet is 999. Check it before you deploy a contract or send a transaction:

code
import requests

for name, url in [("testnet", "https://rpc.hyperliquid-testnet.xyz/evm"),
                  ("mainnet", "https://rpc.hyperliquid.xyz/evm")]:
    chain = requests.post(url, json={"jsonrpc": "2.0", "id": 1, "method": "eth_chainId"}).json()["result"]
    print(name, "chain ID:", int(chain, 16))

Common Hyperliquid testnet errors and how to fix them

We triggered each of these on testnet with a real API wallet. The order rejections (minimum value, price, margin, Alo and Ioc) came back with a top-level "status": "ok", with the error inside the order's own status, so check each order's status, not just the response. The wallet and scheduled-cancel errors came back as "status": "err", and the SDK error never reaches the API.

Error you see What it means Fix
Order must have minimum value of $10.Size times price is under
$10
Raise the size
Order has invalid price. or Price must be divisible by tick size.The price breaks the tick
rules (see below the table)
Round the price (see the
helper below)
ValueError: float_to_wire causes rounding (from the Python SDK, before anything is sent)A price or size has more
than 8 decimal places, which
the SDK won't send
Round the price and size to
the market's rules first
(see the helper below)
Insufficient margin to place order.Not enough test USDC in the
account for that size
Claim more test USDC, or cut
the size
Post only order would have immediately matched, bbo was …An add-liquidity-only ( Alo
) order was priced through
the best bid or ask
Price it behind the best bid
or ask
Order could not immediately match against any resting orders.An immediate-or-cancel ( Ioc
) order found nothing to
fill at that price
Use a price that crosses the
book, or a resting order
User or API Wallet 0x… does not exist.The signing key isn't
approved for this account on
this network, its approval
expired, or the request was
signed for the other network
Approve the API wallet in
the testnet app (More → API)
and sign for the network you
send to
Cannot set scheduled cancel time until enough volume traded. Required: $1000000.Scheduled cancel (the dead
man's switch) needs
$1,000,000 of traded volume.
Our testnet account had
$66,609
Test it on mainnet, with
care
KeyError in the SDK, or the body null from the raw APIThe coin name doesn't exist
on this network (see below
the table)
Read names from meta on the
network you're using
An account value of 0.0 when you query your accountYou queried the API wallet's
address, which holds nothing
Query your main account's
address

Tick rules. Prices can have at most 5 significant figures, and on perps at most 6 minus the market's szDecimals decimal places (8 minus on spot). Whole-number prices are always allowed. In our tests, too many decimals gave Order has invalid price., and 6 significant figures (40867.5 on BTC) gave Price must be divisible by tick size.

Unknown coins. Testnet's market list differs from mainnet's. For l2Book, testnet answered an unknown coin with HTTP 500 and mainnet with HTTP 200, both with the body null, so check the body, not just the status.

One more we didn't trigger ourselves: per Hyperliquid's docs, its faucet won't pay out to an address with no mainnet deposit. Use Circle's faucet instead.

Three of these, the minimum value, invalid price and SDK rounding errors, can be caught before you send anything. This helper rounds a perp order to the market's rules and checks the $10 minimum, using testnet's market data:

code
from hyperliquid.info import Info
from hyperliquid.utils import constants

info = Info(constants.TESTNET_API_URL, skip_ws=True)
sz_decimals = {asset["name"]: asset["szDecimals"] for asset in info.meta()["universe"]}

def prepare_order(coin, size, price):
    """Round a perp order to Hyperliquid's rules and check the $10 minimum."""
    decimals = sz_decimals[coin]                      # KeyError here means the coin isn't on this network
    size = round(size, decimals)
    price = float(f"{price:.5g}")                     # at most 5 significant figures
    if price != int(price):
        price = round(price, 6 - decimals)            # and at most 6 - szDecimals decimals on perps
    if size * price < 10:
        raise ValueError(f"Order value ${size * price:.2f} is under the $10 minimum")
    return size, price

print(prepare_order("BTC", 0.0012345678, 40887.123456))
print(prepare_order("ETH", 0.0123456, 1234.56789))
try:
    prepare_order("BTC", 0.0001, 40887)
except ValueError as e:
    print("Rejected before sending:", e)
code
(0.00123, 40887.0)
(0.0123, 1234.6)
Rejected before sending: Order value $4.09 is under the $10 minimum

For more API errors, including nonce errors and duplicate orders after a timeout, see the common mistakes section of the Hyperliquid API guide linked at the top. Testnet is the right place to hit all of them first.

Before you move to mainnet

  • Swap the API URL and WebSocket URL, and for HyperEVM, the RPC URL and chain ID.
  • Approve an API wallet on mainnet; testnet approvals don't carry over.
  • Remove any hardcoded asset IDs, prices, leverage or size rules.
  • Keep the per-order status checks you built on testnet.
  • Re-check rate limits under real traffic.

FAQ

How do I get testnet HYPE?

Buy it with test USDC on the testnet HYPE/USDC spot market, then send it to 0x2222222222222222222222222222222222222222 to move it to HyperEVM. Circle's faucet can top up your test USDC every 2 hours.

Is Hyperliquid testnet free?

Trading there costs nothing real, as testnet USDC is mock money. Hyperliquid's faucet needs a past mainnet deposit from the same address; Circle's faucet doesn't.

Why can't I get testnet USDC?

For Hyperliquid's faucet, check two things: that the address has deposited on mainnet, and, if you signed in with email, that you're using the same address on both networks. If you have no mainnet deposit, use Circle's faucet instead.

Do API wallets carry over from mainnet?

No. Approve an API wallet on each network. Using a separate key for testnet keeps the two apart.

What is the Hyperliquid testnet chain ID?

998 for HyperEVM testnet. Mainnet is 999.

Sources

[1] Hyperliquid docs: Testnet faucet
[2] Hyperliquid docs: API
[3] Hyperliquid docs: Nonces and API wallets
[4] Hyperliquid docs: Signing
[5] Hyperliquid docs: HyperEVM
[6] Hyperliquid Python SDK
[7] Hyperliquid docs: HyperCore and HyperEVM transfers
[8] Circle: Testnet faucet