# Binance WebSocket API and streams: limits and reconnects

URL: https://fincodelabs.com/en/blog/binance-websocket-api · Publisher: Fincode (Fincode Yazılım ve Teknoloji A.Ş.), Istanbul, Turkey · Category: Exchange APIs · Published: 2026-11-05 · Updated: 2026-11-05

Binance WebSocket Streams and WebSocket API compared: URLs, limits, ping and pong, 24-hour disconnects, serverShutdown and a reconnect loop tested on testnet.

**Short answer:** **Binance WebSocket** means two separate products: WebSocket Streams push market data from `wss://stream.binance.com`, while the WebSocket API at `wss://ws-api.binance.com:443/ws-api/v3` answers requests, including orders. On both, as the Spot documentation read on 4 November 2026 states, a connection lasts at most 24 hours, the server pings every 20 seconds and disconnects without a pong within a minute, and connection attempts are limited to 300 every 5 minutes per IP. Reconnecting is therefore routine, not an edge case.

Searches for the Binance WebSocket documentation often mix up two products with similar names, and older tutorials may describe methods that no longer work. This guide separates them, then covers the limits and the reconnect logic that production code needs. Facts come from the official Spot documentation, whose changelog was last updated on 30 October 2026, read on 4 November 2026. The Python samples ran that day against the public Spot Testnet, without keys.

This is a technical guide, not a recommendation to use Binance or any strategy, and automated trading can lose money. Availability depends on your jurisdiction and the platform's terms. In Turkey, Article 99/A of Capital Markets Law No. 6362 treats activity by foreign-based crypto-asset platforms directed at Turkish residents as unauthorized service provision, and the Capital Markets Board (SPK) has stated that appearing on its list of operating providers does not mean a firm is authorized.

## Binance WebSocket API vs streams: which do you need?

Use WebSocket Streams to receive market data as it changes, and the WebSocket API to send a request and get a reply over an open connection, as you would with REST.

**The two Spot products, as documented on 4 November 2026**

| Aspect | WebSocket Streams | WebSocket API |
| --- | --- | --- |
| Job | Server pushes trades, candles, book updates | Request and response: market data, orders, account queries |
| Base URL | `wss://stream.binance.com:9443` or `:443` | `wss://ws-api.binance.com:443/ws-api/v3` (9443 also works) |
| Testnet | `wss://stream.testnet.binance.vision` | `wss://ws-api.testnet.binance.vision/ws-api/v3` |
| Market data only host | `wss://data-stream.binance.vision` | None documented |
| Authentication | None for market data | Signature per request, or an Ed25519 `session.logon`; public methods need none |
| Rate limit | 5 incoming messages per second; 1,024 streams per connection | Request weight per IP, shared with REST |

Many systems use streams for prices and either REST or the WebSocket API for orders. The wider comparison of channels is in [crypto exchange API integration](https://fincodelabs.com/en/blog/crypto-exchange-api-integration).

## Binance WebSocket stream: connect and subscribe

You can name streams in the URL or subscribe after connecting; a raw connection delivers bare events and a combined one wraps each event with its stream name.

- **Raw:** `/ws/btcusdt@trade`.
- **Combined:** `/stream?streams=btcusdt@trade/ethusdt@trade`. Events arrive as `{"stream": name, "data": event}`.
- **Live control:** send `{"method": "SUBSCRIBE", "params": ["btcusdt@aggTrade"], "id": 1}`. A `result` of `null` means success.
- **Symbols are lowercase** in stream names, unlike REST.

Common names include `@trade`, `@aggTrade`, `@kline_1m`, `@bookTicker` and `@depth`. Using a diff depth stream correctly takes a snapshot and sequence checks, which the [local order book guide](https://fincodelabs.com/en/blog/websocket-order-book) covers. On the testnet on 4 November 2026, both `/ws` and `/stream` accepted a `SUBSCRIBE` and delivered bare and wrapped events respectively.

## Binance WebSocket limits

The limits that matter are incoming messages per second, streams per connection, connection attempts and connection lifetime, and they differ between Spot and USD-M futures.

**Connection limits; Spot from its documentation, USD-M from its Connect page (last modified 30 October 2026), both read 4 November 2026**

| Limit | Spot streams | Spot WebSocket API | USD-M streams |
| --- | --- | --- | --- |
| Connection lifetime | 24 hours | 24 hours | 24 hours |
| Server ping | Every 20 seconds | Every 20 seconds | Every 3 minutes |
| Disconnect with no pong within | 1 minute | 1 minute | 10 minutes |
| Incoming messages | 5 per second | Weight per IP | 10 per second |
| Streams per connection | 1,024 | Not applicable | 1,024 |
| Connection attempts per IP | 300 every 5 minutes | 300 every 5 minutes | Not stated on that page |

On Spot streams, a PING frame, a PONG frame and a JSON control message each count as an incoming message. Going past 5 per second disconnects you, and IPs that are repeatedly disconnected may be banned.

USD-M also routes by path: `/public` for high-frequency market data, `/market` for regular market data and `/private` for user data, and its documentation says unrouted connections receive only public streams. Production behaved that way in our test on 4 November 2026: an unrouted `/ws` connection delivered `bookTicker` but no `aggTrade` or `markPrice` event within 12 seconds, while `/market/ws` delivered `aggTrade`. The demo host `wss://demo-fstream.binance.com` delivered all three on an unrouted connection, so a stream that works on demo can stay silent in production. For REST budgets, see the [Binance API rate limit guide](https://fincodelabs.com/en/blog/binance-api-rate-limit).

## Binance WebSocket ping and pong

Your client must answer each server ping with a pong carrying the same payload, and common WebSocket libraries do this for you as long as the event loop is free to run.

Over 65 seconds on the Spot Testnet stream on 4 November 2026, with debug logging on, pings arrived 20 seconds apart with a millisecond timestamp as payload, and the Python `websockets` library (version 13.1) answered each immediately with no application code involved. Three consequences follow:

- **Do not block the loop.** A long synchronous call in an async program delays the pong. Keep the receive loop to parsing and queueing.
- **Unsolicited pongs do not keep you connected.** Binance allows them on Spot but says they will not prevent disconnection.
- **Library keepalive is separate.** By default `websockets` also sends its own ping every 20 seconds and treats a missing reply within 20 seconds as a broken connection, which catches silent network failures.

A quiet symbol is not a dead connection, so do not reconnect on silence alone.

## Binance WebSocket reconnect loop in Python

Reconnect without delay on planned closures, back off with jitter on failures, and rotate before the 24-hour mark so that the disconnect happens on your schedule.

Besides the 24-hour limit, the server sends a `serverShutdown` event before it shuts down, and the changelog announced a stream infrastructure upgrade for 9 July 2026 during which connections could drop. This loop handles those cases:

```python
import asyncio, json, random, time
import websockets

URL = ("wss://stream.testnet.binance.vision/stream"
       "?streams=btcusdt@bookTicker/btcusdt@miniTicker")
MAX_LIFE = 23.5 * 3600                     # rotate before the 24-hour cut-off

async def consume(url, on_event, max_life):
    async with websockets.connect(url) as ws:   # answers server pings for you
        try:
            async with asyncio.timeout(max_life):   # Python 3.11+
                async for raw in ws:
                    msg = json.loads(raw)
                    event = msg["data"] if "data" in msg else msg  # unwrap combined streams
                    if isinstance(event, dict) and event.get("e") == "serverShutdown":
                        return "serverShutdown"
                    on_event(event)
        except TimeoutError:
            return "planned rotation"
    return "closed by server"

async def run(url, on_event, max_life=MAX_LIFE, sessions=None):
    n = failures = 0
    while sessions is None or n < sessions:
        n += 1
        started = time.monotonic()
        try:
            reason = await consume(url, on_event, max_life)
        except (OSError, websockets.WebSocketException) as err:
            reason = type(err).__name__
        planned = reason in ("planned rotation", "serverShutdown")
        stable = time.monotonic() - started > 60
        failures = 0 if planned or stable else failures + 1
        delay = 0 if planned else min(60, 2 ** failures) * random.uniform(0.5, 1)
        print(f"session {n}: {reason}, next connect in {delay:.1f} s")
        await asyncio.sleep(delay)         # then resync state before trusting it

if __name__ == "__main__":
    seen = []
    asyncio.run(run(URL, seen.append, max_life=6, sessions=2))     # short test run
    print(len(seen), "events")
```

Output on the testnet on 4 November 2026 (Python 3.11, `websockets` 13.1), with a 6-second rotation so that the test finishes quickly:

```
session 1: planned rotation, next connect in 0.0 s
session 2: planned rotation, next connect in 0.0 s
27 events
```

The failure branches were tested against a local server sending synthetic messages: a `serverShutdown` event reconnected at once, and refused connections waited 1.2, 3.7 and 7.2 seconds on one run. All-market array streams send lists rather than objects, and the loop accepts those too. Delays double up to 60 seconds, and the random factor stops a fleet from reconnecting in step. At one attempt per second, 300 attempts take 5 minutes, so several restarting processes on one IP can reach the limit. A rotation still leaves a short gap in this version; to close it, open the new connection first and drop the old one once the new one delivers events, discarding duplicates.

## What to rebuild after a Binance WebSocket reconnect

A new connection knows nothing about the old one, so everything you subscribed to or derived from it has to be restored before you trust it.

1. **Subscriptions.** Names in the URL come back automatically; live `SUBSCRIBE` calls must be sent again, in batches.
2. **Order books.** Discard the local book and resynchronize from a snapshot, as the local order book guide describes.
3. **Missed data.** Events during the gap are gone from the stream. Fetch what you need over REST, keeping weight in mind.
4. **Session state.** A WebSocket API `session.logon` and a user data subscription belong to their connection, so repeat both.
5. **Orders.** Query open orders and recent fills, because any fill during the gap is news to your program, and place no new orders until this is done.

> **Hypothetical example.** A bot holding 40 streams restarts after a crash. Replaying 40 single `SUBSCRIBE` messages at once breaks the limit of 5 per second, the connection drops, and the restart loop repeats the burst. Sent as one message with 40 names in `params`, or as a combined stream URL, the same subscription counts as one message at most.

## Binance WebSocket rate limit: requests and replies on the WebSocket API

Each WebSocket API request is a JSON text frame with an `id` and a `method`, and each reply reports the current weight, drawn from the same per-IP budget as REST.

```python
import asyncio, json
import websockets

WS_API = "wss://ws-api.testnet.binance.vision/ws-api/v3"
REQUESTS = [{"id": 1, "method": "time"},
            {"id": 2, "method": "depth", "params": {"symbol": "BTCUSDT", "limit": 5}},
            {"id": 3, "method": "ticker.price", "params": {"symbol": "NOPE"}}]

async def main():
    async with websockets.connect(WS_API) as ws:
        for request in REQUESTS:
            await ws.send(json.dumps(request))
            reply = json.loads(await ws.recv())
            weight = reply["rateLimits"][0]
            print(reply["id"], reply["status"], reply.get("error", {}).get("code"),
                  f"weight {weight['count']}/{weight['limit']}")

asyncio.run(main())
```

Output on the testnet on 4 November 2026 (the counts include earlier calls from that IP in the same minute):

```
1 200 None weight 173/6000
2 200 None weight 178/6000
3 400 -1121 weight 180/6000
```

The rejected request still used weight, and connecting costs 2 weight, per the documentation. Pass `returnRateLimits=false` to drop the `rateLimits` field; you remain limited either way. Going over returns status 429 with a `retryAfter` value. Be strict about the format too: in a test, an unexpected top-level field returned `-1135`, and the testnet then closed the connection with code 1008. Error codes are shared with REST; see [Binance API error codes](https://fincodelabs.com/en/blog/binance-api-error-codes).

Signed requests carry `apiKey`, `timestamp` and `signature` in `params`, computed over parameters sorted by name. Alternatively, `session.logon` authenticates the connection, for Ed25519 keys only. Signing is covered in the [Binance API Python guide](https://fincodelabs.com/en/blog/binance-api-python-trading-bot).

## User data stream and the official Binance WebSocket docs

Account events now arrive through the WebSocket API, so tutorials built on a `listenKey` for `stream.binance.com` are out of date.

Per the changelog, the listenKey endpoints stopped being available on 20 February 2026. The current methods are `userDataStream.subscribe` after a `session.logon`, and `userDataStream.subscribe.signature`.

The Binance WebSocket documentation is published on the developer site and in the `binance-spot-api-docs` repository on GitHub. The pages to bookmark are [WebSocket Streams](https://github.com/binance/binance-spot-api-docs/blob/master/web-socket-streams.md), [WebSocket API](https://github.com/binance/binance-spot-api-docs/blob/master/web-socket-api.md), [User Data Stream](https://github.com/binance/binance-spot-api-docs/blob/master/user-data-stream.md) and the [changelog](https://github.com/binance/binance-spot-api-docs/blob/master/CHANGELOG.md), which is where changes like these are announced.

## Frequently asked questions

### What is the difference between the Binance WebSocket API and WebSocket Streams?

Streams push market data to you after you subscribe. The WebSocket API is request and response, like REST over an open connection, and it can place orders and read account data.

### How long does a Binance WebSocket connection last?

At most 24 hours, per the Spot and USD-M documentation read on 4 November 2026. On Spot, the server also sends a serverShutdown event when it is about to shut down.

### Do I need to send ping messages to Binance?

You need to answer the server's pings with pongs, which libraries such as Python websockets do automatically. Sending unsolicited pongs alone will not prevent disconnection on Spot.

### Does a Binance WebSocket stream use request weight?

Not in our test: opening a Spot Testnet market stream and reading events added no weight. WebSocket API connections cost 2 weight, and their requests use the same per-IP budget as REST.

### Can I test Binance WebSockets without an account?

Yes for public data. The Spot Testnet stream and WebSocket API hosts serve market data without a key; signed requests need a testnet key.

## Sources

- [Binance Spot API documentation: WebSocket Streams](https://github.com/binance/binance-spot-api-docs/blob/master/web-socket-streams.md)
- [Binance Spot API documentation: WebSocket API](https://github.com/binance/binance-spot-api-docs/blob/master/web-socket-api.md)
- [Binance Spot API documentation: User Data Stream](https://github.com/binance/binance-spot-api-docs/blob/master/user-data-stream.md)
- [Binance Spot API documentation: Market Data Only URLs](https://github.com/binance/binance-spot-api-docs/blob/master/faqs/market_data_only.md)
- [Binance Spot Testnet: General info (hosts and URL table)](https://github.com/binance/binance-spot-api-docs/blob/master/testnet/general-info.md)
- [Binance Spot API CHANGELOG (listenKey removal, July 2026 stream upgrade)](https://github.com/binance/binance-spot-api-docs/blob/master/CHANGELOG.md)
- [Binance USD-M Futures: WebSocket market streams, Connect (last modified 30 October 2026)](https://developers.binance.com/en/docs/products/derivatives-trading-usds-futures/websocket-market-streams/Connect)
- [websockets library documentation: Keepalive and latency](https://websockets.readthedocs.io/en/stable/topics/keepalive.html)
- [Capital Markets Law No. 6362, Article 99/A (Turkish, consolidated text)](https://www.mevzuat.gov.tr/mevzuatmetin/1.5.6362.pdf)
- [SPK: Announcement on crypto-asset service providers, 5 August 2024 (Turkish)](https://spk.gov.tr/duyurular/basin-duyurulari/2024/kripto-varlik-hizmet-saglayicilara-iliskin-duyuru_05082024)

> This guide is general information about software and markets, not investment, legal or tax advice. Automated trading can lose money, past or backtested performance does not guarantee future results, and the rules that apply depend on your jurisdiction and your exchange's terms. Sources, checks and corrections: see the [editorial policy](https://fincodelabs.com/en/editorial-policy).

---

Published by Fincode (Fincode Yazılım ve Teknoloji A.Ş.), a software company in Istanbul, Turkey: https://fincodelabs.com/en/blog/binance-websocket-api

Cite as: Fincode, "Binance WebSocket API and streams: limits and reconnects", updated November 5, 2026, https://fincodelabs.com/en/blog/binance-websocket-api
