# Hyperliquid JavaScript SDK quickstart for Node.js and TypeScript


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](https://siebly.io/sdk/hyperliquid/javascript).

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

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

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

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

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

```ts
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](https://siebly.io/sdk/hyperliquid/javascript/tutorial).


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

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

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

```ts
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

- Read the [Hyperliquid JavaScript SDK docs](https://siebly.io/sdk/hyperliquid/javascript) for the overview and feature list.
- Work through the [Hyperliquid API JavaScript tutorial for Node.js](https://siebly.io/sdk/hyperliquid/javascript/tutorial), which covers perpetual and Spot orders, account streams, the WebSocket API, testnet and proxies in more depth.
- Follow the [Hyperliquid SDK quickstart guide on GitHub](https://github.com/sieblyio/hyperliquid-api/blob/main/docs/HYPERLIQUID_SDK_QUICKSTART_GUIDE.md) for complete end-to-end workflows.
- Check [Hyperliquid SDK release notes](https://siebly.io/releases?sdk=hyperliquid-api) for recent changes.
- Browse the source and examples in the [sieblyio/hyperliquid-api repository](https://github.com/sieblyio/hyperliquid-api), or install from [npm](https://www.npmjs.com/package/@siebly/hyperliquid-api).

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