tests.ws

Binance WebSocket API Guide

binance websocket cryptocurrency trading real-time api

The Binance WebSocket API provides real-time access to cryptocurrency market data and trading updates with minimal latency. Unlike REST APIs that require polling for updates, the binance websocket api establishes a persistent connection that pushes data to your application as events occur. This guide covers everything you need to connect to Binance WebSocket streams, handle different data types, and implement robust reconnection logic.

Binance operates one of the largest cryptocurrency exchanges globally, and their WebSocket infrastructure is designed to handle millions of concurrent connections. Whether you’re building a trading bot, price tracker, or market analysis tool, understanding how to properly implement binance websocket connections is essential for reliable real-time data access.

Understanding Binance WebSocket Endpoints

Binance provides separate WebSocket endpoints for different markets and data types. The primary distinction is between spot trading and futures trading, each with dedicated base URLs.

Spot Market WebSocket Endpoints

For spot trading data, Binance offers these endpoints:

  • Main Spot Stream: wss://stream.binance.com:9443/ws
  • Combined Streams: wss://stream.binance.com:9443/stream

The difference between these endpoints relates to how you subscribe to streams. The /ws endpoint is for single stream connections, while /stream allows you to combine multiple streams in one connection.

Futures Market WebSocket Endpoints

Binance futures trading uses different endpoints:

  • USD-M Futures Stream: wss://fstream.binance.com/ws
  • USD-M Combined Streams: wss://fstream.binance.com/stream
  • COIN-M Futures Stream: wss://dstream.binance.com/ws
  • COIN-M Combined Streams: wss://dstream.binance.com/stream

USD-M futures are settled in USDT, while COIN-M futures are settled in cryptocurrency. Choose the appropriate endpoint based on your trading requirements.

Connecting to Binance WebSocket Streams

The binance api websocket uses standard WebSocket protocol connections. You can connect using any WebSocket client library in your preferred programming language.

Basic Connection in JavaScript

Here’s how to establish a basic connection to a Binance WebSocket stream:

const symbol = 'btcusdt';
const streamName = `${symbol}@trade`;
const wsUrl = `wss://stream.binance.com:9443/ws/${streamName}`;

const ws = new WebSocket(wsUrl);

ws.onopen = () => {
  console.log('Connected to Binance WebSocket');
};

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('Trade event:', data);
};

ws.onerror = (error) => {
  console.error('WebSocket error:', error);
};

ws.onclose = (event) => {
  console.log('Connection closed:', event.code, event.reason);
};

This example connects to the trade stream for Bitcoin/USDT. The connection remains open until explicitly closed or a network error occurs.

Node.js WebSocket Connection

For Node.js applications, you’ll need the ws library:

const WebSocket = require('ws');

const symbol = 'ethusdt';
const streamName = `${symbol}@ticker`;
const wsUrl = `wss://stream.binance.com:9443/ws/${streamName}`;

const ws = new WebSocket(wsUrl);

ws.on('open', () => {
  console.log('Connected to Binance');
});

ws.on('message', (data) => {
  const ticker = JSON.parse(data);
  console.log(`${ticker.s} Price: ${ticker.c}`);
});

ws.on('error', (error) => {
  console.error('Error:', error.message);
});

ws.on('close', () => {
  console.log('Connection closed');
});

For more details on JavaScript WebSocket implementation patterns, see our JavaScript WebSocket guide.

Individual Stream Types

The websocket binance infrastructure supports multiple stream types, each providing different market data. Understanding each stream type helps you choose the right data source for your application.

Ticker Streams

Ticker streams provide 24-hour rolling window statistics for a symbol. There are two types: individual symbol tickers and mini tickers.

Individual Symbol Ticker (<symbol>@ticker):

const ws = new WebSocket('wss://stream.binance.com:9443/ws/btcusdt@ticker');

ws.onmessage = (event) => {
  const ticker = JSON.parse(event.data);
  console.log({
    symbol: ticker.s,
    priceChange: ticker.p,
    priceChangePercent: ticker.P,
    lastPrice: ticker.c,
    volume: ticker.v,
    quoteVolume: ticker.q,
    openPrice: ticker.o,
    highPrice: ticker.h,
    lowPrice: ticker.l
  });
};

Mini Ticker (<symbol>@miniTicker):

const ws = new WebSocket('wss://stream.binance.com:9443/ws/btcusdt@miniTicker');

ws.onmessage = (event) => {
  const mini = JSON.parse(event.data);
  console.log({
    symbol: mini.s,
    close: mini.c,
    open: mini.o,
    high: mini.h,
    low: mini.l,
    volume: mini.v,
    quoteVolume: mini.q
  });
};

Mini tickers use less bandwidth and are ideal when you only need basic price information.

Depth Streams (Order Book)

Depth streams provide order book updates in real-time. Binance offers partial book depth at specific levels and full order book diff streams.

Partial Book Depth (<symbol>@depth<levels>):

const ws = new WebSocket('wss://stream.binance.com:9443/ws/btcusdt@depth10');

ws.onmessage = (event) => {
  const depth = JSON.parse(event.data);
  console.log('Bids:', depth.bids.slice(0, 5));
  console.log('Asks:', depth.asks.slice(0, 5));
  console.log('Last Update ID:', depth.lastUpdateId);
};

Available levels are 5, 10, and 20. These streams send full snapshots at regular intervals (100ms or 1000ms).

Diff Depth Stream (<symbol>@depth):

const ws = new WebSocket('wss://stream.binance.com:9443/ws/btcusdt@depth');

ws.onmessage = (event) => {
  const diff = JSON.parse(event.data);
  console.log({
    eventType: diff.e,
    symbol: diff.s,
    firstUpdateId: diff.U,
    finalUpdateId: diff.u,
    bids: diff.b,
    asks: diff.a
  });
};

The diff stream sends only changes to the order book, requiring you to maintain a local order book state.

Kline/Candlestick Streams

Kline streams provide candlestick data for technical analysis. The binance websocket stream supports multiple timeframes.

const interval = '1m'; // 1m, 5m, 15m, 1h, 4h, 1d, 1w, 1M
const ws = new WebSocket(`wss://stream.binance.com:9443/ws/btcusdt@kline_${interval}`);

ws.onmessage = (event) => {
  const message = JSON.parse(event.data);
  const kline = message.k;

  console.log({
    symbol: kline.s,
    interval: kline.i,
    openTime: new Date(kline.t),
    closeTime: new Date(kline.T),
    open: kline.o,
    high: kline.h,
    low: kline.l,
    close: kline.c,
    volume: kline.v,
    isClosed: kline.x
  });
};

The isClosed field indicates whether the kline is finalized. Use this to avoid processing incomplete candles.

Trade Streams

Trade streams provide real-time information about individual trades as they execute on the exchange.

const ws = new WebSocket('wss://stream.binance.com:9443/ws/btcusdt@trade');

ws.onmessage = (event) => {
  const trade = JSON.parse(event.data);
  console.log({
    symbol: trade.s,
    tradeId: trade.t,
    price: trade.p,
    quantity: trade.q,
    buyerOrderId: trade.b,
    sellerOrderId: trade.a,
    tradeTime: new Date(trade.T),
    isBuyerMaker: trade.m
  });
};

The isBuyerMaker field indicates market direction. When true, the buyer placed a maker order (limit order), and the seller took it with a market order.

Combined Streams

Combined streams allow you to subscribe to multiple data streams through a single WebSocket connection. This approach is more efficient than opening separate connections for each stream.

Subscribing to Multiple Streams

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

const ws = new WebSocket(wsUrl);

ws.onmessage = (event) => {
  const message = JSON.parse(event.data);
  console.log('Stream:', message.stream);
  console.log('Data:', message.data);
};

Combined stream messages wrap the actual data in an object with stream and data properties.

Dynamic Stream Subscription

You can also subscribe and unsubscribe from streams dynamically after the connection is established:

const ws = new WebSocket('wss://stream.binance.com:9443/ws');

ws.onopen = () => {
  // Subscribe to streams
  ws.send(JSON.stringify({
    method: 'SUBSCRIBE',
    params: [
      'btcusdt@trade',
      'ethusdt@ticker'
    ],
    id: 1
  }));
};

ws.onmessage = (event) => {
  const message = JSON.parse(event.data);

  if (message.result === null) {
    console.log('Subscription confirmed');
  } else {
    console.log('Stream data:', message);
  }
};

// Later, unsubscribe from a stream
function unsubscribe(streams) {
  ws.send(JSON.stringify({
    method: 'UNSUBSCRIBE',
    params: streams,
    id: 2
  }));
}

Dynamic subscription is useful for applications that need to change their data sources based on user actions or trading strategies.

User Data Streams

User data streams provide real-time updates about your account activity, including order updates, balance changes, and execution reports. These streams require authentication through a listen key.

Creating a Listen Key

Before connecting to a user data stream, you must obtain a listen key from Binance’s REST API:

async function createListenKey() {
  const response = await fetch('https://api.binance.com/api/v3/userDataStream', {
    method: 'POST',
    headers: {
      'X-MBX-APIKEY': 'your_api_key_here'
    }
  });

  const data = await response.json();
  return data.listenKey;
}

Connecting to User Data Stream

async function connectUserDataStream() {
  const listenKey = await createListenKey();
  const ws = new WebSocket(`wss://stream.binance.com:9443/ws/${listenKey}`);

  ws.onmessage = (event) => {
    const update = JSON.parse(event.data);

    switch (update.e) {
      case 'executionReport':
        console.log('Order update:', {
          symbol: update.s,
          orderId: update.i,
          orderStatus: update.X,
          price: update.p,
          quantity: update.q,
          executedQty: update.z
        });
        break;

      case 'outboundAccountPosition':
        console.log('Account update:', update.B);
        break;

      case 'balanceUpdate':
        console.log('Balance changed:', {
          asset: update.a,
          delta: update.d
        });
        break;
    }
  };

  // Keep the listen key alive
  setInterval(async () => {
    await fetch(`https://api.binance.com/api/v3/userDataStream?listenKey=${listenKey}`, {
      method: 'PUT',
      headers: {
        'X-MBX-APIKEY': 'your_api_key_here'
      }
    });
  }, 30 * 60 * 1000); // Every 30 minutes

  return ws;
}

Listen keys expire after 60 minutes of inactivity. Send a keepalive request every 30 minutes to maintain the connection.

Reconnection Handling

WebSocket connections can drop due to network issues, server maintenance, or rate limiting. Implementing robust reconnection logic ensures your application maintains data continuity.

Automatic Reconnection Strategy

class BinanceWebSocket {
  constructor(streamName) {
    this.streamName = streamName;
    this.ws = null;
    this.reconnectDelay = 1000;
    this.maxReconnectDelay = 60000;
    this.reconnectAttempts = 0;
  }

  connect() {
    const url = `wss://stream.binance.com:9443/ws/${this.streamName}`;
    this.ws = new WebSocket(url);

    this.ws.onopen = () => {
      console.log('Connected to Binance');
      this.reconnectDelay = 1000;
      this.reconnectAttempts = 0;
    };

    this.ws.onmessage = (event) => {
      this.handleMessage(JSON.parse(event.data));
    };

    this.ws.onerror = (error) => {
      console.error('WebSocket error:', error);
    };

    this.ws.onclose = (event) => {
      console.log(`Connection closed: ${event.code}`);
      this.scheduleReconnect();
    };
  }

  scheduleReconnect() {
    this.reconnectAttempts++;
    const delay = Math.min(
      this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1),
      this.maxReconnectDelay
    );

    console.log(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
    setTimeout(() => this.connect(), delay);
  }

  handleMessage(data) {
    // Process incoming data
    console.log(data);
  }

  close() {
    if (this.ws) {
      this.ws.close();
    }
  }
}

// Usage
const binanceWs = new BinanceWebSocket('btcusdt@trade');
binanceWs.connect();

This implementation uses exponential backoff to avoid overwhelming the server during connection issues. For more information on close codes, see our guide on WebSocket close codes.

Handling Specific Close Codes

Different close codes require different handling strategies:

ws.onclose = (event) => {
  switch (event.code) {
    case 1000: // Normal closure
      console.log('Connection closed normally');
      break;

    case 1001: // Going away
    case 1006: // Abnormal closure
      console.log('Unexpected closure, reconnecting...');
      this.scheduleReconnect();
      break;

    case 1008: // Policy violation
      console.error('Policy violation, check rate limits');
      break;

    case 1011: // Server error
      console.log('Server error, waiting before reconnect');
      setTimeout(() => this.scheduleReconnect(), 5000);
      break;

    default:
      console.log(`Closed with code ${event.code}`);
      this.scheduleReconnect();
  }
};

Rate Limits and Connection Limits

Binance imposes limits on WebSocket connections to ensure fair resource distribution across all users.

Connection Limits

  • Maximum 5 connections per IP to stream.binance.com
  • Maximum 10 connections per IP for combined streams
  • Maximum 1024 streams per connection for combined streams
  • Message rate limit: 5 messages per second per connection

Best Practices

To work within these limits:

  1. Use combined streams when monitoring multiple symbols
  2. Batch subscriptions instead of making multiple requests
  3. Implement connection pooling for applications requiring many streams
  4. Cache data locally to reduce redundant subscriptions
  5. Monitor ping/pong frames to detect connection health
// Ping/pong monitoring
ws.on('ping', () => {
  console.log('Received ping from server');
  ws.pong();
});

ws.on('pong', () => {
  console.log('Pong received');
  this.lastPong = Date.now();
});

// Check connection health
setInterval(() => {
  if (Date.now() - this.lastPong > 60000) {
    console.warn('No pong received in 60s, connection may be stale');
    ws.terminate();
  }
}, 10000);

Python Implementation

For Python applications, the websockets library provides an async interface for binance real-time data connections.

Basic Python WebSocket Client

import asyncio
import json
import websockets

async def binance_websocket():
    url = "wss://stream.binance.com:9443/ws/btcusdt@trade"

    async with websockets.connect(url) as websocket:
        print("Connected to Binance WebSocket")

        while True:
            try:
                message = await websocket.recv()
                data = json.loads(message)
                print(f"Trade: {data['s']} Price: {data['p']} Qty: {data['q']}")
            except websockets.ConnectionClosed:
                print("Connection closed")
                break
            except Exception as e:
                print(f"Error: {e}")
                break

Advanced Python Client with Reconnection

import asyncio
import json
import websockets
from websockets.exceptions import ConnectionClosed

class BinanceWebSocketClient:
    def __init__(self, streams):
        self.streams = streams
        self.url = f"wss://stream.binance.com:9443/stream?streams={'/'.join(streams)}"
        self.reconnect_delay = 1
        self.max_reconnect_delay = 60

    async def connect(self):
        while True:
            try:
                async with websockets.connect(self.url) as websocket:
                    print("Connected to Binance")
                    self.reconnect_delay = 1

                    await self.handle_messages(websocket)

            except ConnectionClosed as e:
                print(f"Connection closed: {e.code}")
                await self.reconnect()
            except Exception as e:
                print(f"Error: {e}")
                await self.reconnect()

    async def handle_messages(self, websocket):
        async for message in websocket:
            data = json.loads(message)
            await self.process_message(data)

    async def process_message(self, data):
        stream = data.get('stream')
        stream_data = data.get('data')

        if 'trade' in stream:
            print(f"Trade on {stream}: {stream_data['p']}")
        elif 'ticker' in stream:
            print(f"Ticker {stream_data['s']}: {stream_data['c']}")
        elif 'kline' in stream:
            kline = stream_data['k']
            print(f"Kline {kline['s']}: O:{kline['o']} H:{kline['h']} L:{kline['l']} C:{kline['c']}")

    async def reconnect(self):
        delay = min(self.reconnect_delay, self.max_reconnect_delay)
        print(f"Reconnecting in {delay}s...")
        await asyncio.sleep(delay)
        self.reconnect_delay = min(self.reconnect_delay * 2, self.max_reconnect_delay)

# Usage
streams = ['btcusdt@trade', 'ethusdt@ticker', 'btcusdt@kline_1m']
client = BinanceWebSocketClient(streams)
asyncio.run(client.connect())

For more Python WebSocket patterns, see our Python WebSockets guide.

Handling User Data Streams in Python

import asyncio
import json
import aiohttp
import websockets

class BinanceUserDataStream:
    def __init__(self, api_key, api_secret):
        self.api_key = api_key
        self.api_secret = api_secret
        self.listen_key = None

    async def create_listen_key(self):
        async with aiohttp.ClientSession() as session:
            headers = {'X-MBX-APIKEY': self.api_key}
            async with session.post(
                'https://api.binance.com/api/v3/userDataStream',
                headers=headers
            ) as response:
                data = await response.json()
                self.listen_key = data['listenKey']
                return self.listen_key

    async def keep_alive(self):
        while True:
            await asyncio.sleep(30 * 60)  # 30 minutes
            async with aiohttp.ClientSession() as session:
                headers = {'X-MBX-APIKEY': self.api_key}
                await session.put(
                    f'https://api.binance.com/api/v3/userDataStream?listenKey={self.listen_key}',
                    headers=headers
                )
                print("Listen key refreshed")

    async def connect(self):
        await self.create_listen_key()
        url = f"wss://stream.binance.com:9443/ws/{self.listen_key}"

        # Start keepalive task
        asyncio.create_task(self.keep_alive())

        async with websockets.connect(url) as websocket:
            async for message in websocket:
                data = json.loads(message)
                await self.handle_event(data)

    async def handle_event(self, data):
        event_type = data.get('e')

        if event_type == 'executionReport':
            print(f"Order update: {data['s']} {data['X']} Price: {data['p']}")
        elif event_type == 'outboundAccountPosition':
            print(f"Account update: {len(data['B'])} balances")
        elif event_type == 'balanceUpdate':
            print(f"Balance update: {data['a']} Delta: {data['d']}")

# Usage
# user_stream = BinanceUserDataStream('your_api_key', 'your_api_secret')
# asyncio.run(user_stream.connect())

Common Issues and Solutions

Issue: Connection Immediately Closes

Problem: WebSocket connects but immediately closes with code 1006.

Solution: Check that your stream name is formatted correctly. Symbol names must be lowercase, and stream parameters must match Binance’s documentation exactly.

// Incorrect
const ws = new WebSocket('wss://stream.binance.com:9443/ws/BTCUSDT@trade');

// Correct
const ws = new WebSocket('wss://stream.binance.com:9443/ws/btcusdt@trade');

Issue: Data Appears Delayed

Problem: Prices from WebSocket lag behind the exchange website.

Solution: This usually indicates network latency or processing delays in your code. Minimize processing in the onmessage handler by offloading work to separate functions or workers.

ws.onmessage = (event) => {
  // Fast: queue for processing
  messageQueue.push(event.data);

  // Slow: parsing and complex logic blocks receiving next message
  // const data = JSON.parse(event.data);
  // performComplexCalculations(data);
};

Issue: Too Many Connections Error

Problem: Binance rejects new connections with “Too many connections” message.

Solution: Use combined streams to consolidate multiple subscriptions into fewer connections.

// Instead of 10 separate connections
// Use one combined stream
const streams = symbols.map(s => `${s}@ticker`);
const ws = new WebSocket(`wss://stream.binance.com:9443/stream?streams=${streams.join('/')}`);

Issue: Missing Messages During Reconnection

Problem: Data gaps occur when reconnecting after disconnection.

Solution: Use Binance’s REST API to fetch any missed data using timestamps from your last received message.

async function fillDataGap(symbol, lastUpdateTime) {
  const response = await fetch(
    `https://api.binance.com/api/v3/trades?symbol=${symbol.toUpperCase()}&limit=1000`
  );
  const trades = await response.json();

  return trades.filter(t => t.time > lastUpdateTime);
}

Frequently Asked Questions

What is the difference between Binance WebSocket and REST API?

The binance websocket api provides real-time data through a persistent connection, pushing updates as they occur. The REST API requires you to poll for data repeatedly, which introduces latency and consumes more resources. WebSockets are ideal for live price monitoring, order book tracking, and trading bots that need immediate notifications. REST APIs are better for historical data retrieval, account management operations, and one-time queries. Most production applications use both: WebSockets for real-time updates and REST for historical data and trading operations.

How many WebSocket connections can I open to Binance?

Binance limits connections to 5 per IP address for individual streams and 10 per IP for combined streams. Each combined stream connection can subscribe to up to 1024 different streams. For applications requiring more data sources, use combined streams to maximize efficiency. If you need more connections than these limits allow, consider using multiple IP addresses through a proxy service or VPS infrastructure. Always implement connection pooling and stream multiplexing before resorting to multiple IPs.

Do I need API keys to connect to Binance WebSocket streams?

Public market data streams (ticker, trades, depth, klines) do not require API keys and can be accessed anonymously. However, user data streams that provide account updates, order execution reports, and balance changes require authentication through a listen key. To obtain a listen key, you must have a Binance account and valid API credentials. The listen key is generated through the REST API and must be refreshed every 60 minutes to maintain the connection.

How do I handle Binance WebSocket disconnections in production?

Implement exponential backoff reconnection logic to handle temporary network issues without overwhelming the server. Store the timestamp of your last received message so you can fetch missed data using the REST API after reconnecting. Monitor ping/pong frames to detect stale connections before they fail. Use health check mechanisms to verify data is flowing and trigger reconnection if updates stop arriving. Consider implementing a circuit breaker pattern that temporarily disables reconnection attempts after repeated failures, preventing your application from entering an infinite reconnection loop during extended outages.