Hyperliquid exposes two main HTTP endpoints (Info and Exchange) plus a WebSocket feed. You can call them directly, but signing, nonces, asset IDs and reconnect handling add up quickly. This quickstart walks through @siebly/hyperliquid-api, an MIT-licensed SDK for Node.js, JavaScript and TypeScript, from install to a live WebSocket subscription.

Everything below uses classes and methods exported by the package. The full reference lives in the Hyperliquid JavaScript SDK docs.

What you get

  • One RestClient for public market data, account information, orders, transfers and more.
  • A WebsocketClient with heartbeats, automatic reconnect and resubscribe.
  • A WebsocketAPIClient that mirrors the REST methods over Hyperliquid's WebSocket POST protocol.
  • TypeScript declarations for requests and responses, with dual ESM and CommonJS builds.
  • Testnet support through a single testnet: true option.

Install

The package supports Node.js 22 or newer.

npm install @siebly/hyperliquid-api 

TypeScript is optional, but the type declarations make the request shapes much easier to discover in your editor.

Your first public REST call

Public Info methods need no credentials. Start by checking the exchange status, then pull mid prices and an order book:

import { RestClient } from '@siebly/hyperliquid-api'; const client = new RestClient(); async function main() { const status = await client.getExchangeStatus(); console.log('Exchange status:', status); // AllMidsResponse is Record<string, string>: coin to mid price const mids = await client.getAllMids(); console.log('Coins with a mid price:', Object.keys(mids).length); console.log('BTC mid:', mids['BTC'] ?? 'not listed'); const book = await client.getOrderBook({ coin: 'BTC', nSigFigs: 5, }); if (book) { console.log('Best bid level:', book.levels[0][0]); } } main().catch((error) => { console.error('Request failed:', error); }); 

The SDK uses readable get... names rather than Hyperliquid's internal request type names, so getAllMids() maps to the allMids Info request and getOrderBook() to l2Book.

You can also read any public account by address, without a key:

const user = '0x1234567890123456789012345678901234567890'; const perpSummary = await client.getPerpAccountSummary({ user }); const spotBalances = await client.getSpotBalances({ user }); 

Authenticated client setup

Signed methods take a private key, and that key belongs on a trusted backend only. Read it from an environment variable rather than hard-coding it:

import { RestClient } from '@siebly/hyperliquid-api'; const privateKey = process.env.HYPERLIQUID_PRIVATE_KEY; if (!privateKey) { throw new Error('Set HYPERLIQUID_PRIVATE_KEY before starting.'); } const client = new RestClient({ privateKey, testnet: true, }); const balances = await client.getBalances(); console.log('Account:', balances.user); console.log('Spot:', balances.spot.balances); console.log('Perpetuals:', balances.perp.assetPositions); const openOrders = await client.getOpenOrders(); console.log('Open orders:', openOrders.length); 

A few notes:

  • Use testnet first. testnet: true points the client at Hyperliquid's testnet API, so you can exercise signing and order flows without touching a real account.
  • Keep keys server-side. Never put a private key in frontend code, browser storage or a public environment variable. Public REST calls and public subscriptions are browser-safe; signed calls are not.
  • Custom signers. If you would rather not hand the SDK a raw key, the constructor also accepts a signer with a portable signTypedData implementation.

Once that works, the client exposes helpers for common order workflows. This places a post-only limit order on testnet:

const order = await client.submitLimitOrder({ symbol: 'BTC', side: 'buy', size: '0.001', price: '50000', timeInForce: 'GTC', }); if (order.status === 'ok') { console.log(order.response.data); } 

Exchange methods return an ExchangeResponse wrapper, so check status === 'ok' before reading response.data. For the full order surface, including submitOrder() with raw Hyperliquid order fields and submitMarketOrder(), see the Hyperliquid API JavaScript tutorial.

The README also documents how the SDK handles Hyperliquid referral and builder codes on orders; read that section before running anything on mainnet.

Subscribing to WebSocket streams

WebsocketClient opens connections when they are first needed, deduplicates matching subscriptions, sends heartbeats, and reconnects then resubscribes after a drop. You register listeners once and subscribe to topics separately:

import { WebsocketClient } from '@siebly/hyperliquid-api'; const ws = new WebsocketClient({ testnet: true }); ws.on('message', (event) => { if (event.channel === 'l2Book') { console.log(event.wsKey, event.data.coin, event.data.levels); } else if (event.channel === 'trades') { console.log(event.wsKey, event.data); } }); ws.on('response', (event) => { console.log('Acknowledgement:', event.wsKey, event.channel, event.data); }); ws.on('reconnected', ({ wsKey }) => { console.log('Reconnected:', wsKey); }); ws.on('exception', ({ wsKey, event, data }) => { console.error('WebSocket exception:', wsKey, event ?? data); }); const book = { type: 'l2Book', coin: 'BTC', nSigFigs: 5, } as const; const trades = { type: 'trades', coin: 'ETH' } as const; await ws.subscribe([book, trades]); // Later, when the feeds are no longer needed: // await ws.unsubscribe([book, trades]); // ws.closeAll(); 

subscribe() resolves once the socket is open and the request has been sent. The server's acknowledgement arrives on the response event, and data arrives on message with the upstream envelope intact plus a wsKey.

Coin identifiers such as BTC, @107 or PURR/USDC pass straight through. If you want to resolve a readable alias first, there is a helper:

const coin = await ws.nameToCoin('HYPE/USDC'); await ws.subscribe({ type: 'trades', coin }); 

Reconnect timing, ping interval and pong deadline are all configurable through the constructor options if the defaults do not suit your deployment.

Request and response over WebSockets

If you want the REST-style methods but over a persistent socket, WebsocketAPIClient provides the same Info and Exchange methods as RestClient:

import { WebsocketAPIClient } from '@siebly/hyperliquid-api'; const client = new WebsocketAPIClient({ testnet: true }); const mids = await client.getAllMids(); console.log(mids); client.close(); 

Debugging tips

  • Pass a custom logger as the second argument to WebsocketClient to see pings, subscriptions and messages at trace level.
  • RestClient accepts Axios-compatible network options as its second argument, which lets you route requests through a local debugging proxy. Avoid logging signed /exchange bodies.
  • If Hyperliquid ships a new operation before the SDK has a typed wrapper, submitRawInfoRequest() keeps it usable in the meantime.

Next steps

Questions or bug reports are welcome on the GitHub issues tab.