๐Ÿ”Œ API ๐ŸŸก Intermediate

Binance API Documentation for Node.js: Complete Trading Guide

Master the Binance API with Node.js โ€” from authentication and market data to placing orders and building real-time trading bots with practical code examples.

Table of Contents
  1. Setting Up Your Binance API Environment in Node.js
  2. Fetching Market Data from Binance REST API
  3. Placing Orders Through the Binance API with Node.js
  4. Real-Time Data with Binance WebSocket Streams
  5. Error Handling and Rate Limits in Production
  6. Building a Simple Signal-Based Trading Bot
  7. Key Takeaways

The Binance API is one of the most powerful tools available to crypto traders who want to go beyond manual clicking. Whether you're pulling real-time market data, automating trade execution, or building a full-blown trading bot, Node.js gives you the speed and flexibility to make it happen. The official Binance API documentation covers REST endpoints, WebSocket streams, and account management โ€” but reading raw docs and actually writing working code are two different things. This guide bridges that gap.

Setting Up Your Binance API Environment in Node.js

Before writing a single line of trading logic, you need API keys and a proper project setup. Head to your Binance account, navigate to API Management, and create a new key pair. Store your API key and secret securely โ€” never hardcode them into your source files. Use environment variables or a .env file with the dotenv package.

bash
mkdir binance-trader && cd binance-trader
npm init -y
npm install axios crypto-js dotenv ws

Create a .env file in your project root with your credentials:

bash
BINANCE_API_KEY=your_api_key_here
BINANCE_API_SECRET=your_api_secret_here

Now set up your base client. The Binance REST API requires HMAC SHA256 signatures for authenticated endpoints. Here's a reusable module that handles signing automatically:

javascript
// client.js
require('dotenv').config();
const axios = require('axios');
const crypto = require('crypto-js');

const BASE_URL = 'https://api.binance.com';
const API_KEY = process.env.BINANCE_API_KEY;
const API_SECRET = process.env.BINANCE_API_SECRET;

function sign(queryString) {
  return crypto.HmacSHA256(queryString, API_SECRET).toString();
}

async function publicRequest(endpoint, params = {}) {
  const query = new URLSearchParams(params).toString();
  const url = `${BASE_URL}${endpoint}${query ? '?' + query : ''}`;
  const { data } = await axios.get(url);
  return data;
}

async function signedRequest(method, endpoint, params = {}) {
  params.timestamp = Date.now();
  params.recvWindow = 5000;
  const queryString = new URLSearchParams(params).toString();
  const signature = sign(queryString);
  const url = `${BASE_URL}${endpoint}?${queryString}&signature=${signature}`;

  const { data } = await axios({
    method,
    url,
    headers: { 'X-MBX-APIKEY': API_KEY }
  });
  return data;
}

module.exports = { publicRequest, signedRequest };
Always use the testnet (https://testnet.binance.vision) for development. One typo in a market order on mainnet can cost you real money. Switch the BASE_URL when you're ready for production.

Fetching Market Data from Binance REST API

Public endpoints don't require authentication and are your starting point for any trading strategy. The Binance API documentation lists dozens of market data endpoints, but three cover 90% of what traders need: ticker prices, order book depth, and candlestick (kline) data.

javascript
// market-data.js
const { publicRequest } = require('./client');

async function main() {
  // Current price for a symbol
  const ticker = await publicRequest('/api/v3/ticker/price', {
    symbol: 'BTCUSDT'
  });
  console.log(`BTC Price: $${ticker.price}`);

  // Order book depth โ€” top 5 bids and asks
  const depth = await publicRequest('/api/v3/depth', {
    symbol: 'ETHUSDT',
    limit: 5
  });
  console.log('Best bid:', depth.bids[0]);
  console.log('Best ask:', depth.asks[0]);

  // Candlestick data โ€” last 10 hourly candles
  const klines = await publicRequest('/api/v3/klines', {
    symbol: 'BTCUSDT',
    interval: '1h',
    limit: 10
  });

  const formatted = klines.map(k => ({
    open: parseFloat(k[1]),
    high: parseFloat(k[2]),
    low: parseFloat(k[3]),
    close: parseFloat(k[4]),
    volume: parseFloat(k[5])
  }));
  console.log('Recent candles:', formatted);
}

main().catch(console.error);
Key Binance REST API Endpoints for Market Data
EndpointMethodDescriptionAuth Required
GET /api/v3/ticker/priceGETCurrent price for one or all symbolsNo
GET /api/v3/depthGETOrder book bids and asksNo
GET /api/v3/klinesGETCandlestick/OHLCV dataNo
GET /api/v3/ticker/24hrGET24-hour rolling statisticsNo
GET /api/v3/avgPriceGET5-minute average priceNo

The klines endpoint is particularly valuable for technical analysis. Each candle returns an array of 12 values โ€” open time, OHLCV data, close time, quote volume, number of trades, and taker buy volumes. Parse them into objects for cleaner code downstream.

Placing Orders Through the Binance API with Node.js

This is where the Binance API documentation for Node.js gets serious. Placing orders requires signed requests, and the margin for error is thin. The /api/v3/order endpoint supports market, limit, stop-limit, and OCO order types. Let's start with the essentials.

javascript
// trading.js
const { signedRequest } = require('./client');

async function getAccountInfo() {
  const account = await signedRequest('GET', '/api/v3/account');
  const nonZero = account.balances.filter(
    b => parseFloat(b.free) > 0 || parseFloat(b.locked) > 0
  );
  console.log('Balances:', nonZero);
  return account;
}

async function placeLimitOrder(symbol, side, quantity, price) {
  try {
    const order = await signedRequest('POST', '/api/v3/order', {
      symbol,
      side,        // 'BUY' or 'SELL'
      type: 'LIMIT',
      timeInForce: 'GTC',
      quantity: quantity.toString(),
      price: price.toString()
    });
    console.log(`Order placed: ${order.orderId}`, {
      status: order.status,
      executedQty: order.executedQty
    });
    return order;
  } catch (err) {
    const apiError = err.response?.data;
    if (apiError) {
      console.error(`Binance error ${apiError.code}: ${apiError.msg}`);
      // Common errors:
      // -1013: Filter failure (LOT_SIZE, PRICE_FILTER, MIN_NOTIONAL)
      // -2010: Insufficient balance
      // -1021: Timestamp outside recvWindow
    } else {
      console.error('Network error:', err.message);
    }
    throw err;
  }
}

async function placeMarketOrder(symbol, side, quantity) {
  const order = await signedRequest('POST', '/api/v3/order', {
    symbol,
    side,
    type: 'MARKET',
    quantity: quantity.toString()
  });
  console.log(`Market order filled at avg price:`,
    order.fills.reduce((sum, f) => sum + parseFloat(f.price), 0) / order.fills.length
  );
  return order;
}

// Example usage โ€” uncomment to execute
// getAccountInfo();
// placeLimitOrder('BTCUSDT', 'BUY', 0.001, 60000);
// placeMarketOrder('ETHUSDT', 'BUY', 0.05);
Binance enforces strict filters on every trading pair โ€” minimum order size (LOT_SIZE), price precision (PRICE_FILTER), and minimum notional value (MIN_NOTIONAL). Call GET /api/v3/exchangeInfo to fetch these filters before placing orders, or you'll hit -1013 errors constantly.

A common mistake in the Binance API documentation for Node.js tutorials is ignoring quantity precision. Each symbol has a stepSize โ€” if BTC's stepSize is 0.00001, sending 0.000015 will get rejected. Always round your quantities to match the filter rules.

Real-Time Data with Binance WebSocket Streams

REST polling is fine for periodic checks, but real-time trading demands WebSocket streams. Binance offers individual streams for trades, klines, depth updates, and ticker data. The Node.js ws library makes connecting straightforward.

javascript
// websocket-stream.js
const WebSocket = require('ws');

function startTickerStream(symbol, onTick) {
  const wsUrl = `wss://stream.binance.com:9443/ws/${symbol.toLowerCase()}@trade`;
  const ws = new WebSocket(wsUrl);

  ws.on('open', () => console.log(`Connected to ${symbol} trade stream`));

  ws.on('message', (raw) => {
    const trade = JSON.parse(raw);
    onTick({
      symbol: trade.s,
      price: parseFloat(trade.p),
      quantity: parseFloat(trade.q),
      time: new Date(trade.T),
      isBuyerMaker: trade.m
    });
  });

  ws.on('error', (err) => console.error('WebSocket error:', err.message));
  ws.on('close', () => {
    console.log('Connection closed, reconnecting in 5s...');
    setTimeout(() => startTickerStream(symbol, onTick), 5000);
  });

  return ws;
}

// Track real-time trades with a simple moving price tracker
let prices = [];
startTickerStream('BTCUSDT', (trade) => {
  prices.push(trade.price);
  if (prices.length > 100) prices.shift();

  const avg = prices.reduce((a, b) => a + b, 0) / prices.length;
  const direction = trade.isBuyerMaker ? 'SELL' : 'BUY';

  process.stdout.write(
    `\r${trade.symbol} ${direction} $${trade.price.toFixed(2)} | ` +
    `MA(100): $${avg.toFixed(2)} | Trades tracked: ${prices.length}`
  );
});

For monitoring multiple symbols simultaneously, use combined streams instead of opening separate connections. Binance allows up to 1024 streams on a single WebSocket connection:

javascript
// Combined stream for multiple symbols
const streams = ['btcusdt@ticker', 'ethusdt@ticker', 'solusdt@ticker'];
const wsUrl = `wss://stream.binance.com:9443/stream?streams=${streams.join('/')}`;

const ws = new WebSocket(wsUrl);
ws.on('message', (raw) => {
  const { stream, data } = JSON.parse(raw);
  console.log(`[${data.s}] Price: ${data.c} | 24h Change: ${data.P}%`);
});

Error Handling and Rate Limits in Production

The Binance API documentation specifies strict rate limits: 1200 request weight per minute for REST endpoints and 10 orders per second. Exceeding these gets you a 429 response and eventually a temporary IP ban (HTTP 418). In production Node.js applications, you need to track your usage and back off proactively.

  • Each endpoint has a weight โ€” GET /api/v3/depth with limit=500 costs 5 weight, while limit=5000 costs 50
  • Response headers include X-MBX-USED-WEIGHT-1M showing your current minute's consumption
  • WebSocket connections don't count toward REST rate limits but have their own 5 messages/second limit for sending
  • Implement exponential backoff: wait 1s, then 2s, then 4s on consecutive failures โ€” don't hammer a failing endpoint
javascript
// rate-limiter.js โ€” Simple rate-aware request wrapper
class RateLimiter {
  constructor(maxWeight = 1100, windowMs = 60000) {
    this.maxWeight = maxWeight;
    this.windowMs = windowMs;
    this.requests = [];
  }

  getCurrentWeight() {
    const cutoff = Date.now() - this.windowMs;
    this.requests = this.requests.filter(r => r.time > cutoff);
    return this.requests.reduce((sum, r) => sum + r.weight, 0);
  }

  async throttle(weight = 1) {
    while (this.getCurrentWeight() + weight > this.maxWeight) {
      const oldestExpiry = this.requests[0].time + this.windowMs - Date.now();
      console.log(`Rate limit approaching, waiting ${oldestExpiry}ms...`);
      await new Promise(r => setTimeout(r, oldestExpiry + 100));
    }
    this.requests.push({ time: Date.now(), weight });
  }
}

// Usage with your client
const limiter = new RateLimiter();
async function safeFetch(endpoint, params, weight = 1) {
  await limiter.throttle(weight);
  return publicRequest(endpoint, params);
}

Beyond rate limits, handle these common failure modes in your Node.js Binance API integration: network timeouts (set axios timeout to 10 seconds), clock drift causing signature errors (sync your server time with GET /api/v3/time), and exchange maintenance windows. A resilient bot handles all three gracefully.

Building a Simple Signal-Based Trading Bot

Let's tie everything together. Here's a basic structure that listens to WebSocket price data, applies a simple moving average crossover strategy, and executes trades. In practice, you'd want to combine this with signals from platforms like VoiceOfChain to validate entries with real-time on-chain data and market sentiment before executing.

javascript
// bot.js โ€” SMA crossover with Binance API
const { signedRequest, publicRequest } = require('./client');
const WebSocket = require('ws');

const SYMBOL = 'ETHUSDT';
const TRADE_QTY = '0.05';
const SHORT_PERIOD = 10;
const LONG_PERIOD = 30;

let closes = [];
let position = null; // 'long' | null

function sma(arr, period) {
  if (arr.length < period) return null;
  const slice = arr.slice(-period);
  return slice.reduce((a, b) => a + b, 0) / period;
}

async function executeTrade(side) {
  try {
    const order = await signedRequest('POST', '/api/v3/order', {
      symbol: SYMBOL, side, type: 'MARKET',
      quantity: TRADE_QTY
    });
    console.log(`\n${side} executed: ${order.executedQty} @ avg ${
      (order.fills.reduce((s, f) => s + parseFloat(f.price) * parseFloat(f.qty), 0) /
       parseFloat(order.executedQty)).toFixed(2)
    }`);
    return order;
  } catch (err) {
    console.error(`Trade failed: ${err.response?.data?.msg || err.message}`);
  }
}

// Listen to 1-minute kline closes
const ws = new WebSocket(
  `wss://stream.binance.com:9443/ws/${SYMBOL.toLowerCase()}@kline_1m`
);

ws.on('message', async (raw) => {
  const { k: kline } = JSON.parse(raw);
  if (!kline.x) return; // Only act on closed candles

  closes.push(parseFloat(kline.c));
  if (closes.length > LONG_PERIOD + 10) closes.shift();

  const shortSMA = sma(closes, SHORT_PERIOD);
  const longSMA = sma(closes, LONG_PERIOD);
  if (!shortSMA || !longSMA) return;

  console.log(`\rSMA(${SHORT_PERIOD}): ${shortSMA.toFixed(2)} | ` +
    `SMA(${LONG_PERIOD}): ${longSMA.toFixed(2)} | Position: ${position || 'none'}`);

  // Crossover logic
  if (shortSMA > longSMA && !position) {
    await executeTrade('BUY');
    position = 'long';
  } else if (shortSMA < longSMA && position === 'long') {
    await executeTrade('SELL');
    position = null;
  }
});

console.log(`Bot started on ${SYMBOL} โ€” SMA(${SHORT_PERIOD}/${LONG_PERIOD}) crossover`);
This is a simplified example for learning purposes. A production trading bot needs risk management (stop losses, position sizing), persistence (restart recovery), proper logging, and multiple confirmation signals. Never run unattended bots with funds you can't afford to lose.

Key Takeaways

The Binance API documentation paired with Node.js gives you everything needed to build serious trading infrastructure. Start with public market data endpoints to get comfortable with the request patterns. Move to authenticated endpoints on testnet before risking real capital. Use WebSocket streams for anything time-sensitive โ€” REST polling introduces latency that can cost you on volatile moves.

The code examples in this guide are production-ready patterns, not toy demos. The signed request handler, rate limiter, and WebSocket reconnection logic are the same foundations used in real trading systems. Layer in additional signal sources โ€” on-chain analytics from VoiceOfChain, order flow data, or sentiment indicators โ€” and you have the building blocks of a genuinely useful automated trading pipeline.

One final piece of advice: read the official Binance API documentation thoroughly. It's dense but comprehensive. The changelog section is especially important โ€” Binance regularly updates endpoints, deprecates old versions, and adjusts rate limits. Your Node.js integration is only as reliable as your understanding of what the API actually guarantees.