Binance Futures WebSocket: How to Subscribe to Live Streams
Binance Futures WebSocket lets you tap into live market data without polling REST endpoints. Learn to subscribe to trade feeds, depth updates, and mark price streams using Python.
Binance Futures WebSocket lets you tap into live market data without polling REST endpoints. Learn to subscribe to trade feeds, depth updates, and mark price streams using Python.
Binance Futures WebSocket gives you a direct pipeline to live market data — trades, order book depth, mark prices, liquidations — delivered the moment they happen. Unlike REST APIs where you poll every few seconds and hope you're not rate-limited, WebSocket maintains a persistent connection and pushes updates to you. For traders who need to react in milliseconds rather than seconds, this difference is everything. This guide covers the practical mechanics: how to connect, how to subscribe to multiple streams at once, and how to write reconnection logic that doesn't leave you blind during a volatile move.
When you call a REST endpoint on Binance — say GET /fapi/v1/ticker/price — you get a snapshot of the market at that instant. To approximate real-time data, you'd need to poll that endpoint dozens of times per second. Binance will rate-limit you well before you get close to tick-level resolution, and even if you could poll fast enough, you'd still have HTTP connection overhead on every single request.
WebSocket flips the model entirely. You open a single persistent TCP connection to wss://fstream.binance.com and Binance pushes every trade, every order book update, and every funding rate change directly to your client as it happens. The latency difference is substantial: REST polling at one-second intervals puts you 1000ms behind the market at best. A WebSocket aggTrade stream typically delivers within 5-50ms of the actual trade occurring on the exchange.
Exchanges like Bybit and OKX use the same WebSocket-push architecture in their derivatives APIs. But Binance Futures stands out for the depth of its stream catalog — it exposes mark price updates, liquidation orders, composite index prices, and multi-asset mode data that most competitors don't surface via WebSocket at this granularity. If you're building a cross-exchange system that also pulls from Bybit or Gate.io, you'll find Binance's documentation the most complete starting point.
Public market data streams on Binance Futures require no authentication — no API key, no signature. That's one of the underappreciated advantages: you can get a working data feed running in under twenty lines of Python. Install the websockets library first, then connect directly to the stream endpoint.
pip install websockets
The single-stream URL pattern is wss://fstream.binance.com/ws/<streamName>. For aggregate trades on the BTCUSDT perpetual contract, that becomes wss://fstream.binance.com/ws/btcusdt@aggTrade. Here's the minimal working connection:
import asyncio
import websockets
import json
async def connect_binance_futures():
uri = "wss://fstream.binance.com/ws/btcusdt@aggTrade"
async with websockets.connect(uri) as websocket:
print("Connected to Binance Futures WebSocket")
while True:
message = await websocket.recv()
data = json.loads(message)
price = data['p']
qty = data['q']
is_sell = data['m'] # True = buyer is market maker = sell hit a bid
side = "SELL" if is_sell else "BUY"
print(f"{side} {qty} BTC @ {price} USDT")
asyncio.run(connect_binance_futures())
A few things worth knowing about the aggTrade stream: it delivers aggregated trades, not individual fills. If ten orders fill at the same price in the same millisecond, you get one event summarizing them all. The 'p' field is price, 'q' is quantity, 'm' indicates whether the buyer was the market maker (true means a sell order hit a resting bid). When you're using this for trade flow analysis — tracking buy vs. sell pressure — the 'm' field is your primary signal.
Real trading systems need more than one data feed. At minimum you'll want aggregate trades for price action, order book depth for liquidity context, and mark price for futures-specific data like funding rates. Binance supports two approaches: individual stream URLs (one connection per stream) or combined streams via the /stream endpoint (one connection, multiple subscriptions). The combined approach is almost always better — it's more efficient and easier to manage.
| Stream Name | Data Type | Update Frequency |
|---|---|---|
| btcusdt@aggTrade | Aggregate trades | Real-time per trade |
| btcusdt@depth20@100ms | Order book top 20 levels | Every 100ms |
| btcusdt@markPrice@1s | Mark price + funding rate | Every 1 second |
| btcusdt@liquidationOrder | Forced liquidations | Real-time |
| btcusdt@kline_1m | 1-minute OHLCV candles | Real-time updates |
With combined streams, you connect to wss://fstream.binance.com/stream and send a SUBSCRIBE message specifying which streams you want. Every message comes back wrapped with a stream key so you can route it to the right handler:
import asyncio
import websockets
import json
FUTURES_WSS = "wss://fstream.binance.com/stream"
async def subscribe_streams():
async with websockets.connect(FUTURES_WSS) as ws:
subscribe_msg = {
"method": "SUBSCRIBE",
"params": [
"btcusdt@aggTrade",
"btcusdt@depth20@100ms",
"ethusdt@aggTrade",
"btcusdt@markPrice@1s"
],
"id": 1
}
await ws.send(json.dumps(subscribe_msg))
# Binance confirms the subscription with a result message
response = await ws.recv()
print(f"Subscription confirmed: {response}")
async for raw_msg in ws:
data = json.loads(raw_msg)
stream = data.get("stream", "")
payload = data.get("data", {})
if "aggTrade" in stream:
print(f"[Trade] {stream}: {payload['p']} x {payload['q']}")
elif "depth" in stream:
print(f"[Depth] Bids: {len(payload['b'])}, Asks: {len(payload['a'])}")
elif "markPrice" in stream:
print(f"[Mark] Price: {payload['p']}, Funding: {payload['r']}")
asyncio.run(subscribe_streams())
Important: when using the /stream combined endpoint, every message is wrapped as {"stream": "...", "data": {...}}. If you connect directly to /ws/btcusdt@aggTrade, the payload arrives unwrapped. Mixing these approaches in the same codebase is a common source of KeyError bugs.
WebSocket connections drop. Network blips, Binance server-side maintenance, router timeouts — any of these can close your socket without warning. A production system needs automatic reconnection logic, or you'll miss data at exactly the worst moments. High-volatility moves that trigger liquidation cascades are also the moments most likely to spike server load and cause disconnections.
Binance's limits are worth knowing before you scale: 300 connections per IP address, 1024 stream subscriptions per connection. For individual traders monitoring a handful of pairs, neither limit matters. But if you're running a strategy across thirty pairs on both Binance and Bybit simultaneously, connection management becomes part of your architecture planning rather than an afterthought.
import asyncio
import websockets
import json
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
STREAMS = [
"btcusdt@aggTrade",
"btcusdt@markPrice@1s",
"ethusdt@aggTrade"
]
async def process_message(data):
stream = data.get("stream", "")
payload = data.get("data", {})
if "markPrice" in stream:
logger.info(f"Mark Price: {payload['p']}, Funding Rate: {payload['r']}")
elif "aggTrade" in stream:
side = "SELL" if payload["m"] else "BUY"
logger.info(f"{side} {payload['q']} @ {payload['p']}")
async def run_websocket():
uri = "wss://fstream.binance.com/stream"
subscribe_msg = {
"method": "SUBSCRIBE",
"params": STREAMS,
"id": 1
}
while True:
try:
# ping_interval sends keepalive pings every 20s
async with websockets.connect(uri, ping_interval=20) as ws:
await ws.send(json.dumps(subscribe_msg))
logger.info("Connected and subscribed to streams")
async for raw in ws:
await process_message(json.loads(raw))
except websockets.ConnectionClosed as e:
logger.warning(f"Connection closed: {e}. Reconnecting in 5s...")
await asyncio.sleep(5)
except Exception as e:
logger.error(f"Unexpected error: {e}. Reconnecting in 5s...")
await asyncio.sleep(5)
asyncio.run(run_websocket())
The ping_interval=20 parameter tells the websockets library to send a keepalive ping every 20 seconds, which prevents idle connection timeouts on both sides. The outer while True loop with exception handling means a dropped connection triggers a clean reconnect rather than killing your process. Separate the exception types — ConnectionClosed is expected behavior during reconnects; a generic Exception catching everything else logs unexpected bugs without masking them.
Raw WebSocket data is just a stream of numbers. The value comes from what you do with it. A common pattern for futures traders: use the aggTrade stream to track real-time buy and sell volume by grouping trades on the 'm' (market maker) field, and use the markPrice stream to watch funding rate direction. When funding turns significantly negative while buy volume is still dominant, you have a structural tension — longs are paying shorts, but buyers haven't backed off yet. That setup has a finite lifespan.
Platforms like VoiceOfChain are built on exactly this kind of WebSocket infrastructure at scale. Their signal engine processes live feeds from multiple exchanges — Binance, Bybit, OKX, and Gate.io — aggregating trade flow, order book imbalances, and on-chain metrics into signals that individual traders can act on without building the data pipeline themselves. If you're building your own system, you're essentially constructing pieces of what VoiceOfChain does at the data ingestion layer.
The Binance Futures WebSocket server sends a ping frame roughly every 3 minutes. The websockets Python library responds to these automatically. If you're using a raw WebSocket library or another language, you must handle pong responses manually or Binance will close your connection after 10 minutes of missed pings.
Binance Futures WebSocket is one of the most capable (and free) real-time data tools available to crypto traders. The combination of sub-100ms latency, a comprehensive stream catalog, and no authentication requirement for public data makes it a solid foundation for everything from simple price alerts to full algorithmic trading systems. The reconnection handling covered here is the piece most tutorials skip — don't skip it. A strategy that silently loses its data feed during peak volatility is more dangerous than one that simply trades a bit slower. Start with a single aggTrade stream, get the reconnection logic working reliably, then layer in additional streams as your use case demands. Build incrementally, test with real market hours, and you'll have a stable foundation to work from.