# KuCoin API JavaScript tutorial for Node.js: REST and WebSocket

Private KuCoin REST requests need an API key, a timestamp, an HMAC signature, an HMAC-signed passphrase and the key version. Classic WebSocket connections need a connection token first, and each subscription has to reach the right product connection. This tutorial walks through `kucoin-api`, an MIT-licensed Node.js, JavaScript and TypeScript SDK for KuCoin, from install to a test order that never reaches the matching engine.

The SDK comes from the same maintainers as the binance, bybit-api and okx-api JS SDKs, with over 4.5 million npm downloads across 10+ SDKs. The overview lives in the [KuCoin JavaScript SDK docs](https://siebly.io/sdk/kucoin/javascript), and the full walkthrough (Futures test orders, the WebSocket API, reconnect recovery, regional hosts and proxies) is the [KuCoin API JavaScript tutorial for Node.js](https://siebly.io/sdk/kucoin/javascript/tutorial).

## What you get

| Client | Use it for |
| --- | --- |
| `SpotClient` | Classic Spot, Margin, funding, transfer, Earn and Convert REST calls |
| `FuturesClient` | Classic Futures market, account, order, position and funding REST calls |
| `WebsocketClient` | Classic and Pro public and private WebSocket subscriptions |
| `WebsocketAPIClient` | Promise-based Spot, Margin and Futures order commands over WebSocket |

Start with the Classic clients unless you specifically need the newer Unified Account or Pro APIs.

## Install and API keys

```bash
npm install kucoin-api
```

Public market data needs no credentials. Private calls, private streams and order commands need three values: `KUCOIN_API_KEY`, `KUCOIN_API_SECRET` and `KUCOIN_API_PASSPHRASE`. The passphrase is the one you chose when creating the key, not your account password.

Use a General-only key while you build account views, and add Spot or Futures permission only when you start testing orders. Nothing here needs Withdrawal permission. Keep keys server-side and out of logs.

A few naming details save time later: Spot symbols look like `BTC-USDT`, Futures symbols like `XBTUSDTM`, and a Futures `size` is a number of contracts rather than an amount of BTC. Successful REST responses come back as `{ code: '200000', data }`.

## 1\. Public REST

```javascript
import { SpotClient } from 'kucoin-api';

const client = new SpotClient();

async function main() {
  const time = await client.getServerTime();
  console.log('Server time:', time.data);

  const ticker = await client.getTicker({ symbol: 'BTC-USDT' });
  console.log('BTC-USDT ticker:', ticker.data);

  const book = await client.getOrderBookLevel20({ symbol: 'BTC-USDT' });
  console.log('Best bid:', book.data.bids[0], 'best ask:', book.data.asks[0]);

  const candles = await client.getKlines({ symbol: 'BTC-USDT', type: '1hour' });
  console.log('Latest candle:', candles.data[0]);
}

main().catch(console.error);
```

Prices and sizes are usually strings. Keep them that way when sending values back to KuCoin, and convert deliberately when you calculate.

## 2\. Private REST

The same client takes credentials and signs every request.

```javascript
import { SpotClient } from 'kucoin-api';

const client = new SpotClient({
  apiKey: process.env.KUCOIN_API_KEY,
  apiSecret: process.env.KUCOIN_API_SECRET,
  apiPassphrase: process.env.KUCOIN_API_PASSPHRASE,
});

async function main() {
  const balances = await client.getBalances({ type: 'trade' });
  console.log('Spot trading balances:', balances.data);

  const openOrders = await client.getHFActiveOrdersPaginated({
    symbol: 'BTC-USDT',
    pageNum: 1,
    pageSize: 20,
  });
  console.log('Open orders:', openOrders.data.items);
}

main().catch(console.error);
```

The client throws when KuCoin returns a non-success business code. Parsed errors carry the HTTP status, body, headers and request context, with the key, secret and passphrase left out. Methods with `HF` in the name map to KuCoin's current high-frequency Spot endpoints.

## 3\. Public WebSocket streams

Classic subscriptions take a topic and a `wsKey`. The SDK fetches the connection token, opens the right connection, keeps it alive and resubscribes after a reconnect.

```javascript
import { WebsocketClient, WS_KEY_MAP } from 'kucoin-api';

const ws = new WebsocketClient();

ws.on('update', (update) => console.log(update.wsKey, update.topic, update.data));
ws.on('response', (response) => console.log('response', response.type));
ws.on('reconnected', ({ wsKey }) => console.log('reconnected', wsKey));
ws.on('exception', (error) => console.error('ws error', error));

ws.subscribe('/market/ticker:BTC-USDT', WS_KEY_MAP.spotPublicV1);
ws.subscribe('/contractMarket/tickerV2:XBTUSDTM', WS_KEY_MAP.futuresPublicV1);

process.once('SIGINT', () => ws.closeAll());
```

A `welcome` response means the connection is ready, and an `ack` confirms the subscription. Market data arrives through `update`.

## 4\. Private streams

Spot and Futures private topics use separate connections, so keep the `wsKey` with every event.

```javascript
import { WebsocketClient, WS_KEY_MAP } from 'kucoin-api';

const ws = new WebsocketClient({
  apiKey: process.env.KUCOIN_API_KEY,
  apiSecret: process.env.KUCOIN_API_SECRET,
  apiPassphrase: process.env.KUCOIN_API_PASSPHRASE,
});

ws.on('update', (update) => console.log('private', update.wsKey, update.topic));
ws.on('exception', (error) => console.error('ws error', error));

ws.subscribe(['/spotMarket/tradeOrdersV2', '/account/balance'], WS_KEY_MAP.spotPrivateV1);
ws.subscribe(
  ['/contractMarket/tradeOrders:XBTUSDTM', '/contractAccount/wallet'],
  WS_KEY_MAP.futuresPrivateV1,
);
```

## 5\. A Spot test order

KuCoin's test-order endpoint checks authentication and order fields without touching the matching engine. Read the symbol metadata first so the size meets the minimums.

```javascript
import { SpotClient } from 'kucoin-api';

const client = new SpotClient({
  apiKey: process.env.KUCOIN_API_KEY,
  apiSecret: process.env.KUCOIN_API_SECRET,
  apiPassphrase: process.env.KUCOIN_API_PASSPHRASE,
});

async function main() {
  const { data: symbol } = await client.getSymbol({ symbol: 'BTC-USDT' });
  const { data: ticker } = await client.getTicker({ symbol: 'BTC-USDT' });

  const price = Number(ticker.bestBid);
  const step = Number(symbol.baseIncrement);
  const minSize = Math.max(Number(symbol.baseMinSize), Number(symbol.minFunds) / price);
  const decimals = (symbol.baseIncrement.split('.')[1] || '').length;
  const size = (Math.ceil(minSize / step) * step).toFixed(decimals);

  const result = await client.submitHFOrderTest({
    clientOid: client.generateNewOrderID(),
    symbol: 'BTC-USDT',
    side: 'buy',
    type: 'limit',
    price: ticker.bestBid,
    size,
    timeInForce: 'GTC',
    postOnly: true,
  });
  console.log('Spot order test:', result);
}

main().catch(console.error);
```

The test endpoint is good for checking credentials, permissions, signing and order fields. It doesn't test matching, fills, cancellation or private order updates; the full tutorial covers those with the WebSocket API.

## Where to go next

- [KuCoin JavaScript SDK docs](https://siebly.io/sdk/kucoin/javascript) for the client overview.
- [KuCoin API JavaScript tutorial](https://siebly.io/sdk/kucoin/javascript/tutorial) for Futures test orders, the WebSocket API, reconnect recovery, regional hosts and proxies.
- [kucoin-api on GitHub](https://github.com/sieblyio/kucoin-api) for examples and the endpoint map.

## More docs

API reference and usage guides also live on the [Siebly docs portal](https://sieblyio.github.io/docs/) (Hyperliquid, Bybit and Binance pages so far).
