Binance splits Spot, Margin, Wallet, Futures and Portfolio Margin across different REST prefixes, WebSocket endpoints and auth flows. Signing, listen-key refresh, reconnects and response matching add up quickly if you wire that yourself. This tutorial walks through binance, the Node.js and TypeScript SDK for those APIs, from install to a live public stream.
The SDK comes from the same maintainers as the bybit-api and okx-api JS SDKs, with over 4.5 million npm downloads across 10+ SDKs. Everything below uses classes and methods from the package. The overview lives in the Binance JavaScript SDK docs, and the longer walkthrough is the Binance API JavaScript tutorial for Node.js.
What you get
-
MainClientfor Spot, margin, wallet and many account REST APIs. -
USDMClient,CoinMClientandPortfolioClientfor Futures and Portfolio Margin. -
WebsocketClientfor public market streams and listen-key user data where Binance still uses that flow. -
WebsocketAPIClientfor awaitable WebSocket API commands and Spot user data subscriptions. - TypeScript declarations, dual ESM and CommonJS builds, HMAC/RSA/Ed25519 auth, and optional response beautification.
Install
npm install binance
Or with another npm-compatible package manager:
pnpm install binance
yarn add binance
TypeScript is optional. The published type declarations make request shapes easier to discover in your editor.
Public market data needs no keys. Private REST and user data need an API key and secret from Binance. Keep live, demo and testnet credentials separate, grant the minimum permissions you need, and never put secrets in frontend code.
export BINANCE_API_KEY='your-api-key'
export BINANCE_API_SECRET='your-api-secret-or-private-key'
For HMAC keys, api_secret is the Binance API secret. For RSA or Ed25519, pass your PEM private key as api_secret. The SDK detects the key type and signs accordingly.
Your first public REST calls
MainClient covers Spot market data under Binance's main REST family. No credentials are required for public reads:
import { MainClient } from 'binance';
const client = new MainClient();
async function main() {
const serverTime = await client.getServerTime();
const exchangeInfo = await client.getExchangeInfo({ symbol: 'BTCUSDT' });
const ticker = await client.getSymbolPriceTicker({ symbol: 'BTCUSDT' });
const orderBook = await client.getOrderBook({ symbol: 'BTCUSDT', limit: 10 });
const candles = await client.getKlines({
symbol: 'BTCUSDT',
interval: '1m',
limit: 5,
});
console.log({
serverTime,
symbol: exchangeInfo.symbols?.[0]?.symbol,
ticker,
orderBook,
candles,
});
}
main().catch(console.error);
That confirms connectivity, parsing and the Spot product group. Other useful public helpers on the same client include testConnectivity(), getRecentTrades(), getAggregateTrades(), getAvgPrice() and getSymbolOrderBookTicker().
As a rule of thumb for REST clients:
-
MainClientfor paths underapi/orsapi/(Spot, margin, wallet, many account APIs). -
USDMClientforfapi/(USD-M Futures). -
CoinMClientfordapi/(COIN-M Futures). -
PortfolioClientforpapi/(Portfolio Margin).
Authenticated client setup
Private methods take api_key and api_secret. Read them from the environment on a trusted backend:
import { MainClient } from 'binance';
const client = new MainClient({
api_key: process.env.BINANCE_API_KEY!,
api_secret: process.env.BINANCE_API_SECRET!,
beautifyResponses: true,
});
async function main() {
const account = await client.getAccountInformation();
const balances = await client.getBalances();
const openOrders = await client.getOpenOrders({ symbol: 'BTCUSDT' });
console.log({
canTrade: account.canTrade,
balances: balances.length,
openOrders: openOrders.length,
});
}
main().catch(console.error);
Private REST methods are signed automatically. You do not add timestamps, signatures or X-MBX-APIKEY headers yourself.
When you are ready to validate an order shape without sending it to the matching engine, use testNewOrder():
import { MainClient } from 'binance';
const client = new MainClient({
api_key: process.env.BINANCE_API_KEY!,
api_secret: process.env.BINANCE_API_SECRET!,
});
async function placeOrder() {
const orderRequest = {
symbol: 'BTCUSDT',
side: 'BUY',
type: 'LIMIT',
quantity: 0.001,
price: 10000,
timeInForce: 'GTC',
newOrderRespType: 'FULL',
} as const;
// Validate the request without sending it to the matching engine.
await client.testNewOrder(orderRequest);
// Remove this comment when you are ready to place a real order.
// const result = await client.submitNewOrder(orderRequest);
// console.log(result);
}
placeOrder().catch(console.error);
If your system needs a client order ID before the request leaves, prefer client.generateNewOrderId() so the product prefix stays correct. Omit newClientOrderId when you do not need one.
For USD-M Futures, create a USDMClient instead. Demo trading uses live market data with simulated fills:
import { USDMClient } from 'binance';
const client = new USDMClient({
api_key: process.env.BINANCE_API_KEY!,
api_secret: process.env.BINANCE_API_SECRET!,
demoTrading: true,
});
Do not enable both demoTrading and testnet on the same client.
Subscribing to WebSocket streams
WebsocketClient opens the right Binance endpoint for the product group, sends heartbeats, reconnects after drops and resubscribes cached topics. WS_KEY_MAP tells the SDK which endpoint family to use. Spot public topics go to WS_KEY_MAP.main:
import { WebsocketClient, WS_KEY_MAP } from 'binance';
const ws = new WebsocketClient({
beautify: true,
});
ws.on('open', (data) => console.log('connected', data.wsKey, data.wsUrl));
ws.on('message', (data) => console.log('raw message', JSON.stringify(data)));
ws.on('formattedMessage', (data) => console.log('formatted', data));
ws.on('response', (data) => console.log('response', JSON.stringify(data)));
ws.on('reconnecting', (data) => console.log('reconnecting', data?.wsKey));
ws.on('reconnected', (data) => console.log('reconnected', data?.wsKey));
ws.on('exception', console.error);
ws.subscribe(['btcusdt@trade', 'btcusdt@bookTicker'], WS_KEY_MAP.main);
Public streams need no API keys. With beautify: true, use formattedMessage for readable event keys; raw frames still arrive on message.
Other common Spot topics include btcusdt@aggTrade, btcusdt@kline_1m and btcusdt@depth10@100ms. USD-M Futures public data uses WS_KEY_MAP.usdmPublic or WS_KEY_MAP.usdmMarket, depending on the stream family. COIN-M topics use WS_KEY_MAP.coinm. Stream names are usually lowercase and product-specific (btcusdt@trade on Spot versus btcusd_perp@markPrice on COIN-M).
Spot user data over the WebSocket API
For Spot account events, prefer WebsocketAPIClient.subscribeUserDataStream(). Binance retired the older Spot listen-key workflow for new streams, so start Spot user data on the WebSocket API path:
import { WebsocketAPIClient, WS_KEY_MAP } from 'binance';
const wsApi = new WebsocketAPIClient({
api_key: process.env.BINANCE_API_KEY!,
api_secret: process.env.BINANCE_API_SECRET!,
beautify: true,
});
const wsClient = wsApi.getWSClient();
wsClient.on('open', (data) => {
console.log('ws api open:', data.wsKey);
});
wsClient.on('formattedUserDataMessage', (data) => {
console.log('user data:', data);
});
wsClient.on('reconnecting', ({ wsKey }) => {
console.log('spot user data reconnecting', wsKey);
});
wsClient.on('reconnected', ({ wsKey }) => {
console.log('spot user data reconnected', wsKey);
// Fetch account state, open orders or recent fills here if needed.
});
wsClient.on('exception', console.error);
async function main() {
await wsApi.subscribeUserDataStream(WS_KEY_MAP.mainWSAPI);
}
main().catch(console.error);
Futures and Portfolio Margin user data still use listen-key helpers on WebsocketClient (subscribeUsdFuturesUserDataStream(), subscribeCoinFuturesUserDataStream(), subscribePortfolioMarginUserDataStream()). The SDK creates, refreshes and tears those keys down for you.
Error handling and production tips
-
Listen for reconnects. Treat
reconnectingandreconnectedas normal. Onreconnected, backfill account state, open orders, fills or positions over REST before you resume risky actions. -
Watch clocks and
recvWindow. Private requests are timestamp-sensitive. Keep the host clock synced. If you see timestamp errors, fix clock sync first.fetchLatencySummary()onMainClienthelps measure drift; adjustrecvWindowonly after you understand the latency. -
Inspect rate-limit state. After REST calls,
client.getRateLimitStates()exposes the Binance rate-limit headers the SDK has seen. Reduce polling or batch where the API allows it when those counters climb. - Scope credentials. Keep live, demo, Spot testnet and Futures testnet secrets separate. A market-data key should not trade; a trading key should not withdraw. Use IP whitelisting on every key.
-
Preserve large integers when needed.
JSON.parsecannot represent integers aboveNumber.MAX_SAFE_INTEGER. If large order IDs matter, passcustomParseJSONFnintoWebsocketClientso those fields stay as strings. -
Log deliberately. Pass a custom logger as the second argument to
WebsocketClientif you want pings, reconnects and exceptions in your own monitoring stack. Avoid logging signed request bodies.
Next steps
- Read the Binance JavaScript SDK overview for clients, features and package links.
- Work through the full Binance API JavaScript tutorial for Node.js and TypeScript for Margin, Futures, Portfolio Margin, proxies, WebSocket API commands and rollout checks.
- Browse examples and the endpoint map in the sieblyio/binance repository, or install from npm.
Questions or bug reports are welcome on the GitHub issues tab.
Top comments (0)