---
title: "Bitget Futures API Node.js SDK"
description: "Signing Bitget requests by hand is a common time sink."
canonical: "https://siebly.io/blog/bitget-futures-api-nodejs-sdk-a-professional-integration-guide"
---

# Bitget Futures API Node.js SDK: A Professional Integration Guide

Signing Bitget requests by hand is a common time sink.

## Technical Disclaimer

These articles are software engineering references for exchange API integrations. They are not financial, investment, legal, tax, compliance, or trading advice. Use public data, demo, testnet, or paper workflows first. Keep API credentials out of frontend code and disable withdrawal permissions for automation keys.

## Overview {#overview}

Signing Bitget requests by hand is a common time sink. HMAC strings, RSA keys, clock drift, and the split between classic V2 endpoints and V3 Unified Trading Account (UTA) APIs all sit between you and actual order logic. Raw HTTP plus a homemade WebSocket client usually fails in the same places: reconnects, heartbeats, and mismatched REST vs stream payloads.

This guide shows how to integrate Bitget futures in Node.js with the [bitget-api](https://www.npmjs.com/package/bitget-api) package. You get request signing, typed REST clients, and an awaitable WebSocket API for order placement. Rate limits stay your problem. The SDK does not throttle for you.

New work should target V3/UTA via `RestClientV3`. Use `RestClientV2` only if the account is still on classic mode. After you upgrade to UTA, V2 account endpoints stop working unless you revert.

## Key Takeaways {#key-takeaways}

- Use [bitget-api](/sdk/bitget/javascript) so HMAC and RSA signing, timestamps, and reconnects are handled in one client instead of custom crypto.
- Pick the right class: `RestClientV3` and `WebsocketClientV3` for UTA, `RestClientV2` and `WebsocketClientV2` if you have not upgraded yet.
- Place futures orders over REST with `submitNewOrder`, not `placeOrder`. The WebSocket API client uses the same method name, with the market type as the first argument.
- Bitget does not ship a separate testnet. Paper trade with `demoTrading: true` and a Demo API key.
- Public market data is capped around 20 requests per second. Private limits are per endpoint. The SDK will not queue or throttle those calls for you.



## The Challenge of Manual Bitget Futures API Integration {#the-challenge-of-manual-bitget-futures-api-integration}

Bitget currently runs two generations of APIs. Classic accounts talk to V2. Unified Trading Account talks to V3. Docs, parameter names, and WebSocket topic shapes differ between the two. If you mix them, you get auth errors or empty payloads, not a helpful compiler warning.

Manual HMAC-SHA256 signing is the other tax. Each private REST call signs `timestamp + METHOD + requestPath + body`. One encoding mismatch and you get an invalid signature. RSA is worse if you are assembling PEM headers yourself. Clock skew on the signing timestamp is a frequent source of 40004 / 40008 failures.

### The Problem with DIY API Wrappers {#the-problem-with-diy-api-wrappers}

A thin axios wrapper looks cheap until Bitget changes a field name, or a WebSocket drops and you have to resubscribe, re-auth, and rebuild local order state. Heartbeats, pong handling, and reconnect logic are the parts people underestimate. TypeScript types for every futures order shape are also tedious to keep current.

### Siebly.io as the Preferred Implementation Layer {#siebly-io-as-the-preferred-implementation-layer}

The [Bitget Futures API Node.js SDK](/sdk/bitget/javascript) is the `bitget-api` npm package. It signs REST and WebSocket traffic, exposes V2 and V3 clients, and ships TypeScript types for request and response bodies.

It does not implement rate-limit queues. Bitget documents limits per endpoint, plus a global public cap of about 20 market-data requests per second and 6000 requests per IP per minute. Stay inside those yourself.

## Initializing the bitget-api SDK for Futures Trading {#initializing-the-bitget-api-sdk-for-futures-trading}

Install the package:

```bash title="Imported example"
npm install bitget-api
```

Or `yarn add bitget-api`. Put API credentials in environment variables. Do not hardcode keys. When you create a Bitget key for bots, leave withdrawals disabled.

HMAC keys need three values: API key, secret, and passphrase. In this SDK those map to `apiKey`, `apiSecret`, and `apiPass`.

```js title="Imported example"
import { RestClientV3 } from "bitget-api";
// or if you prefer require:
// const { RestClientV3 } = require('bitget-api');

const client = new RestClientV3({
  apiKey: process.env.API_KEY_COM,
  apiSecret: process.env.API_SECRET_COM,
  apiPass: process.env.API_PASS_COM,
});
```

For public market data only, skip credentials:

```js title="Imported example"
const publicClient = new RestClientV3();
```

V3 methods are available after the account is on UTA. If it is still classic, use `RestClientV2` instead.

### Authentication and Client Configuration {#authentication-and-client-configuration}

HMAC is the default. Pass a normal API secret string.

RSA is detected from the secret itself. There is no `signMethod` flag. Put the PEM private key in `apiSecret`, including the `BEGIN PRIVATE KEY` / `END PRIVATE KEY` lines. Bitget never sees that private key. You upload the matching public key when you create the API key.

```js title="Imported example"
import { RestClientV3 } from "bitget-api";

const rsaPrivateKey = `-----BEGIN PRIVATE KEY-----
...your RSA private key...
-----END PRIVATE KEY-----`;

const client = new RestClientV3({
  apiKey: process.env.API_KEY_COM,
  apiSecret: rsaPrivateKey,
  apiPass: process.env.API_PASS_COM,
});
```

Demo trading is not a separate base URL you toggle as "testnet". Create a Demo API key on Bitget (switch the UI to demo mode, then create the key), then set `demoTrading: true`. The SDK adds the `paptrading: 1` header on REST and routes WebSockets to the demo hosts.

```js title="Imported example"
const client = new RestClientV3({
  apiKey: process.env.API_KEY_COM,
  apiSecret: process.env.API_SECRET_COM,
  apiPass: process.env.API_PASS_COM,
  demoTrading: true,
});
```

Behind a proxy, pass axios options as the second constructor argument:

```js title="Imported example"
const client = new RestClientV3(
  {
apiKey: process.env.API_KEY_COM,
apiSecret: process.env.API_SECRET_COM,
apiPass: process.env.API_PASS_COM,
  },
  {
proxy: { host: "127.0.0.1", port: 8080 },
  },
);
```

You can also override `baseUrl` in the first argument if you need a custom REST host.

### TypeScript Integration and Type Safety {#typescript-integration-and-type-safety}

[bitget-api](/sdk/bitget/javascript) ships type declarations. Futures orders use string quantities and prices (`qty`, `price`), which matches Bitget's API. That avoids JavaScript number rounding if you keep those values as strings. The SDK is not a BigNumber library. If you convert `'0.001'` to a float and back, that is on you.

Public and private calls share the same `RestClientV3` instance. Spot vs futures is a `category` field, not a second client class. REST futures use `USDT-FUTURES`, `COIN-FUTURES`, or `USDC-FUTURES`. WebSocket market topics use lowercase `usdt-futures`. Mix those strings and the call fails. For a full method list, see the [Bitget JavaScript SDK](/sdk/bitget/javascript) docs.

## Executing REST API Workflows for Market and Private Data {#executing-rest-api-workflows-for-market-and-private-data}

REST is still the right tool for snapshots: balances, open positions, instrument rules, historical candles. `RestClientV3` methods return promises and throw on Bitget error payloads (when `code` is not `00000`).

### Market Data Ingestion Patterns {#market-data-ingestion-patterns}

No keys needed for these:

```js title="Imported example"
import { RestClientV3 } from "bitget-api";

const restClient = new RestClientV3();
const symbol = "BTCUSDT";

const candles = await restClient.getCandles({
  symbol,
  category: "USDT-FUTURES",
  interval: "1m",
});

const tickers = await restClient.getTickers({
  category: "USDT-FUTURES",
  symbol,
});

const book = await restClient.getOrderBook({
  category: "USDT-FUTURES",
  symbol,
  limit: "20",
});
```

Use REST candles and books as a backup, not as your live feed. For history, `getHistoryCandles` is the sibling of `getCandles`.

### Private Account and Order Management {#private-account-and-order-management}

Check balance and positions before you send size:

```js title="Imported example"
import { RestClientV3 } from "bitget-api";

const client = new RestClientV3({
  apiKey: process.env.API_KEY_COM,
  apiSecret: process.env.API_SECRET_COM,
  apiPass: process.env.API_PASS_COM,
});

const balanceResult = await client.getBalances();
const usdtAsset = balanceResult.data.assets?.find(
  (asset) => asset.coin === "USDT",
);

const positionsResult = await client.getCurrentPosition({
  category: "USDT-FUTURES",
});
const openPositions = positionsResult.data.list.filter(
  (pos) => pos.total !== "0",
);
```

The place-order method is `submitNewOrder`. Cancel is `cancelOrder`. Amend is `modifyOrder`. There is no `placeOrder` helper on this client.

```js title="Imported example"
const order = await client.submitNewOrder({
  category: "USDT-FUTURES",
  orderType: "market",
  side: "buy",
  qty: "0.001",
  symbol: "BTCUSDT",
});

await client.cancelOrder({
  orderId: order.data.orderId,
});
```

Instrument filters (`minOrderQty`, tick size) come from `getInstruments({ category: 'USDT-FUTURES', symbol: 'BTCUSDT' })`. Size your order from that, not from a hardcoded lot.

Bitget error codes worth knowing:

- `40012`: API key or passphrase is wrong
- `40014`: API key is missing a required permission
- `25202`: insufficient balance (V3)
- `25227`: no position to close
- `43012`: insufficient balance on older V2 paths

The SDK throws a parsed error object. Bitget's code lives on `error.body.code`, not as a magic `40012` for "not enough margin".

Rate limits: the SDK will not slow you down. Public market endpoints are documented at 20 requests per second. Private endpoints each have their own cap in Bitget's docs. Do not treat "10 private requests per second" as a single global budget.



## Leveraging Awaitable WebSockets for Low-Latency Execution {#leveraging-awaitable-websockets-for-low-latency-execution}

REST is fine for account checks. For live execution you want a persistent socket. The Bitget Futures API Node.js SDK splits that into two classes on V3:

- `WebsocketClientV3` for market and account streams
- `WebsocketAPIClient` for order placement that returns a promise

### Awaitable vs. Event-Driven WebSockets {#awaitable-vs-event-driven-websockets}

`WebsocketClientV3` is event-driven: you `subscribe()`, then handle `update`. That is the right shape for tickers, books, fills, and position pushes.

`WebsocketAPIClient` wraps the V3 WebSocket trade API. You call `submitNewOrder(category, params)` and await the matching response. Under the hood it still uses a long-lived connection. You skip the HTTP handshake on every order.

REST category values are uppercase (`USDT-FUTURES`). The WebSocket API category is lowercase (`usdt-futures`).

```js title="Imported example"
import { WebsocketAPIClient } from "bitget-api";

const wsClient = new WebsocketAPIClient({
  apiKey: process.env.API_KEY_COM,
  apiSecret: process.env.API_SECRET_COM,
  apiPass: process.env.API_PASS_COM,
  // demoTrading: true,
});

async function start() {
  await wsClient.getWSClient().connectWSAPI();

  const res = await wsClient.submitNewOrder("usdt-futures", {
orderType: "limit",
price: "50000",
qty: "0.001",
side: "buy",
symbol: "BTCUSDT",
timeInForce: "gtc",
  });

  console.log("WS API submitNewOrder result: ", res);

  await wsClient.cancelOrder("usdt-futures", {
orderId: res.args?.[0]?.orderId,
  });
}

start().catch((e) => console.error("Exception in example: ", e));
```

Batch place and batch cancel exist as `placeBatchOrders` and `cancelBatchOrders`. Batch place can return `code: "0"` on the envelope while individual rows fail. Read each row's `code` and `msg`.

For market and account data, use `WebsocketClientV3`:

```js title="Imported example"
import { WebsocketClientV3, WS_KEY_MAP } from "bitget-api";

const wsClient = new WebsocketClientV3({
  apiKey: process.env.API_KEY_COM,
  apiSecret: process.env.API_SECRET_COM,
  apiPass: process.env.API_PASS_COM,
});

wsClient.on("update", (data) => {
  console.log("WS raw message received ", data);
});

wsClient.on("open", (data) => {
  console.log("WS connection opened:", data.wsKey);
});

wsClient.on("exception", (data) => {
  console.log("WS error", data);
});

wsClient.subscribe(
  {
topic: "ticker",
payload: {
instType: "usdt-futures",
symbol: "BTCUSDT",
},
  },
  WS_KEY_MAP.v3Public,
);

wsClient.subscribe(
  [
{
topic: "account",
payload: { instType: "UTA" },
},
{
topic: "position",
payload: { instType: "UTA" },
},
{
topic: "order",
payload: { instType: "UTA" },
},
{
topic: "fill",
payload: { instType: "UTA" },
},
  ],
  WS_KEY_MAP.v3Private,
);
```

Account-level private topics use `instType: 'UTA'`, not `usdt-futures`.

If you are still on classic V2, use `WebsocketClientV2` and `subscribeTopic('USDT-FUTURES', 'ticker', symbol)`. V2 has no `WebsocketAPIClient`.

### Maintaining WebSocket Stability {#maintaining-websocket-stability}

The SDK sends pings and reconnects on drop. After a reconnect it resubscribes to topics it already tracked. You do not need to call `subscribe()` again for those topics.

You still need to reconcile state. A fill can happen while the socket is down. Listen for `reconnected` and then REST-check positions and open orders:

```js title="Imported example"
wsClient.on("reconnect", ({ wsKey }) => {
  console.log("WS automatically reconnecting.... ", wsKey);
});

wsClient.on("reconnected", async (data) => {
  console.log("WS reconnected ", data?.wsKey);
  const positions = await client.getCurrentPosition({
category: "USDT-FUTURES",
  });
  console.log("positions after reconnect: ", positions.data.list);
});
```

Bitget's documented WebSocket caps are 100 connections per IP, 300 connection attempts per IP per 5 minutes, and a recommendation to keep under 50 channels on one connection. Group tickers and books for the same symbols on one public socket instead of opening a socket per market.

More walkthroughs: [bitget-api package](/sdk/bitget/javascript) and the [Bitget Node.js SDK tutorial](/sdk/bitget/javascript/tutorial).

## Architectural Best Practices for Resilient Trading Systems {#architectural-best-practices-for-resilient-trading-systems}

API connectivity is the easy part. Put limits in your own process: max position, max order size, kill switch on stale data, keys with trade permission and no withdraw. The SDK gives you typed calls and a stable socket. Risk checks stay in your code.

Typed clients also help when you generate glue code. Request shapes for `submitNewOrder` are in the package, so missing `category` or a numeric `qty` shows up in the editor instead of at the matching engine.

### Simulation and Testnet Workflows {#simulation-and-testnet-workflows}

There is no Bitget testnet flag in this SDK. Use demo trading:

1. Switch the Bitget UI to demo mode and create a Demo API key.
2. Pass that key to `RestClientV3` / `WebsocketAPIClient` / `WebsocketClientV3`.
3. Set `demoTrading: true`.

Same method names as live. Different funds. For broader Node.js system design, see [Algorithmic Trading System Architecture](/blog/algorithmic-trading-system-architecture-in-nodejs-a-2026-engineering-guide).

### Scaling Your Integration {#scaling-your-integration}

Keep market sockets independent from order logic so a blocked REST call cannot stall ticker handling. One `RestClientV3` can serve spot and futures through `category`. You do not need a client per market type.

If you run several accounts, give each one its own client instance and its own rate-limit budget. The package will not share a global throttle across processes.

For production, treat the [Siebly Bitget SDK](/sdk/bitget/javascript) as the transport layer and keep strategy code away from signing and URL details. That is the point of V2 vs V3 clients: swap the class when the account model changes, without rewriting HMAC.

## Building Resilient Execution Infrastructure on Bitget {#building-resilient-execution-infrastructure-on-bitget}

A working Bitget futures stack in Node is: `RestClientV3` for state, `WebsocketClientV3` for streams, `WebsocketAPIClient` when you want awaitable orders on the V3 socket. Signing and reconnects are already done. You still own throttling, demo vs live keys, and what happens after `reconnected`.

Use TypeScript, keep qty and price as strings, and run the flow on demo trading before you point keys at live futures. Official Bitget docs remain the source of truth for endpoint-level rate limits and error codes.

If you want the package and examples in one place, [start building with the Siebly Bitget SDK](/sdk/bitget/javascript).

## Frequently Asked Questions {#frequently-asked-questions}

### Does the bitget-api SDK support Bitget V3 and UTA APIs? {#does-the-bitget-api-sdk-support-bitget-v3-and-uta-apis}

Yes. `RestClientV3`, `WebsocketClientV3`, and `WebsocketAPIClient` are the UTA/V3 surface. Classic accounts should use `RestClientV2` and `WebsocketClientV2`. After you migrate to UTA, V2 account APIs are no longer valid for that account.

### How do I handle Bitget API rate limits in Node.js? {#how-do-i-handle-bitget-api-rate-limits-in-node-js}

The SDK does not throttle. Public market data is documented at 20 requests per second. Private limits are listed per endpoint in Bitget's API docs, not as one number for every call. HTTP 429 means back off. Put a queue in your process if you burst.

### Can I use the bitget-api SDK for both Spot and Futures trading? {#can-i-use-the-bitget-api-sdk-for-both-spot-and-futures-trading}

Yes, on the same client. REST uses `category: 'SPOT'` or `category: 'USDT-FUTURES'` (also `COIN-FUTURES` and `USDC-FUTURES`). WebSocket market topics use `instType: 'spot'` or `instType: 'usdt-futures'`. You do not construct a separate futures-only class.

### Does this SDK support RSA authentication for enhanced security? {#does-this-sdk-support-rsa-authentication-for-enhanced-security}

Yes. Pass the PEM private key as `apiSecret`, including the begin/end private key lines. HMAC secrets stay a normal string. The SDK picks the algorithm from the key material.

### How does the bitget-api SDK handle WebSocket reconnections? {#how-does-the-bitget-api-sdk-handle-websocket-reconnections}

Pings and reconnects are automatic. Topics you already subscribed to are resubscribed after the socket comes back. The client emits `reconnected`. You should still fetch positions and open orders over REST after that event, because you can miss fills during the gap.

### Is TypeScript support included in the bitget-api package? {#is-typescript-support-included-in-the-bitget-api-package}

Yes. Request and response types ship with the package. Futures order fields such as `qty` and `price` are strings.

### How can I place futures orders via WebSockets using this SDK? {#how-can-i-place-futures-orders-via-websockets-using-this-sdk}

Use `WebsocketAPIClient`. Call `submitNewOrder('usdt-futures', {... })` and await the result. Cancel with `cancelOrder('usdt-futures', { orderId })`. This is V3/UTA only. Classic V2 has streaming, not this awaitable trade API.

### What is the best way to synchronize timestamps with Bitget servers? {#what-is-the-best-way-to-synchronize-timestamps-with-bitget-servers}

REST signing uses your local `Date.now()`. The SDK does not fetch server time on startup and does not apply an automatic offset. Keep the machine clock accurate. If you want to measure drift, `RestClientV3` has `fetchLatencySummary()` and `getServerTime()`. WebSocket clients expose `setTimeOffsetMs` if you need to nudge auth timestamps.

Disclaimer

*Technical and legal disclaimer: Siebly.io provides software development tools, SDKs, documentation, and educational engineering content for crypto exchange API integrations. This content is for software engineering education only and is not financial, investment, legal, tax, accounting, compliance, or trading advice.

Nothing in this article is a recommendation, invitation, or inducement to buy, sell, hold, trade, long, short, or allocate to any cryptoasset, exchange product, strategy, bot, or automated workflow. Examples, code patterns, simulations, backtests, and architecture diagrams are illustrative only and must not be treated as trading signals, investment recommendations, or evidence of future performance.

Cryptoasset markets are high risk and volatile. If you choose to build or operate exchange-connected software, you are responsible for your own legal, regulatory, tax, security, exchange-account, API-key, and risk-management obligations. Use public data, testnet, demo, dry-run, or paper-trading workflows before any live execution. Keep API keys server-side, use least-privilege permissions, and never enable withdrawals for automation keys unless you fully understand and accept the risks.

Siebly.io is not an exchange, custodian, investment adviser, trading-signal provider, or managed trading service. Official exchange documentation remains the source of truth for exchange-specific rules, API behavior, and terms of use. Use of Siebly.io content and software is also subject to the Siebly.io terms and conditions.*

## Related articles

- [Bitget API SDK TypeScript: Building Production-Ready Node.js Integrations in 2026](/blog/bitget-api-sdk-typescript-building-production-ready-nodejs-integrations-in-2026)
- [Bitget API SDK for TypeScript: Node.js Guide (2026)](/blog/bitget-api-sdk-for-typescript-nodejs-guide-2026)
- [KuCoin Futures API Node.js SDK: 2026 Integration Guide](/blog/kucoin-futures-api-nodejs-sdk-2026-integration-guide)


## Related Siebly Resources

- [Bitget JavaScript SDK](/sdk/bitget/javascript)
- [Siebly SDK directory](/sdk)
- [Exchange State Management](/ai/exchange-state)
- [Runnable exchange API examples](/examples)
- [SDK security and release integrity](/security)
