> ## Documentation Index
> Fetch the complete documentation index at: https://docs.whitebit.com/llms.txt
> Use this file to discover all available pages before exploring further.

# WebSocket Quickstart

> Connect to the WhiteBIT WebSocket API — subscribe to real-time prices, orderbook updates, and private balance changes.

Connect to the WhiteBIT WebSocket API for real-time streaming data. The guide targets developers building trading bots, live price feeds, or account monitoring dashboards.

## Prerequisites

* A terminal with a WebSocket client (Python `websocket-client` library, or `wscat` for interactive testing)
* For private channels: a WhiteBIT API key with **Trading** permission and familiarity with [Authentication](/api-reference/authentication)
* No special setup for public channels — same zero-authentication principle as the [Market Data API](/products/market-data/overview)

<Warning>
  WhiteBIT has no public testnet or sandbox. Private channel subscriptions
  expose live account data. Store API credentials securely — never commit
  secrets to version control or expose them in shared environments.
  For risk-free balance-change testing, use Demo Tokens (DBTC/DUSDT) —
  activate from the [WhiteBIT Codes page](https://whitebit.com/codes).
</Warning>

## Connection parameters

| Detail             | Value                                             |
| ------------------ | ------------------------------------------------- |
| Endpoint           | `wss://api.whitebit.com/ws`                       |
| Protocol           | JSON-RPC 2.0 over WebSocket                       |
| Inactivity timeout | 60 seconds — send `ping` every 50 seconds or less |
| Rate limit         | 200 requests per minute per connection            |

<Steps>
  <Step title="Connect to the WebSocket endpoint">
    Establish a WebSocket connection.

    <Tabs>
      <Tab title="Python">
        ```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
        import websocket
        import json

        ws = websocket.create_connection("wss://api.whitebit.com/ws")
        print("Connected to WebSocket API")
        ```
      </Tab>

      <Tab title="wscat">
        ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
        wscat -c wss://api.whitebit.com/ws
        ```
      </Tab>
    </Tabs>

    A successful connection produces no immediate response. The server is ready to receive subscription messages.

    For Go and PHP examples, see [SDKs](/sdks).
  </Step>

  <Step title="Subscribe to last price updates">
    Subscribe to real-time price updates for BTC\_USDT.

    <Tabs>
      <Tab title="Python">
        ```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
        # Subscribe to BTC_USDT last price
        subscribe_msg = {
            "id": 1,
            "method": "lastprice_subscribe",
            "params": ["BTC_USDT"]
        }
        ws.send(json.dumps(subscribe_msg))

        # Receive subscribe confirmation
        result = json.loads(ws.recv())
        print(f"Subscribe result: {result}")

        # Receive price updates
        update = json.loads(ws.recv())
        print(f"Price update: {update}")
        ```
      </Tab>

      <Tab title="wscat">
        ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
        > {"id": 1, "method": "lastprice_subscribe", "params": ["BTC_USDT"]}
        ```
      </Tab>
    </Tabs>

    **Subscribe confirmation:**

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {"id": 1, "result": {"status": "success"}, "error": null}
    ```

    **Update message:**

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "id": null,
      "method": "lastprice_update",
      "params": ["BTC_USDT", "65432.10"]
    }
    ```

    The `params` array contains: market name (index 0) and last traded price (index 1). Updates arrive whenever a trade executes on the pair.
  </Step>

  <Step title="Subscribe to orderbook depth">
    Subscribe to orderbook depth updates for BTC\_USDT with 5 price levels.

    <Tabs>
      <Tab title="Python">
        ```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
        # Subscribe to BTC_USDT depth, 5 levels, no aggregation
        depth_msg = {
            "id": 2,
            "method": "depth_subscribe",
            "params": ["BTC_USDT", 5, "0", True]
        }
        ws.send(json.dumps(depth_msg))

        # Receive subscribe confirmation
        result = json.loads(ws.recv())
        print(f"Depth subscribe: {result}")

        # Receive depth snapshot/update
        depth = json.loads(ws.recv())
        print(f"Depth update: {depth}")
        ```
      </Tab>

      <Tab title="wscat">
        ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
        > {"id": 2, "method": "depth_subscribe", "params": ["BTC_USDT", 5, "0", true]}
        ```
      </Tab>
    </Tabs>

    **Parameters:** market name, depth limit (`1`/`5`/`10`/`20`/`30`/`50`/`100`), price aggregation interval (`"0"` = no aggregation), and multiple subscription flag. Set the flag to `true` to add a subscription alongside existing depth subscriptions. Setting the flag to `false` unsubscribes from all active depth subscriptions. To drop a single market while keeping the others, send `depth_unsubscribe` with that market name in `params` instead. Full parameter details: [Depth channel](/websocket/market-streams/depth).

    **Update message:**

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "id": null,
      "method": "depth_update",
      "params": [
        true,
        {
          "asks": [["65450.00", "0.500"], ["65460.00", "1.200"]],
          "bids": [["65430.00", "0.800"], ["65420.00", "1.500"]],
          "timestamp": 1700000000,
          "update_id": 12345
        },
        "BTC_USDT"
      ]
    }
    ```

    The first element in `params` is a boolean: `true` = full snapshot, `false` = incremental update. For local orderbook maintenance, apply incremental updates to the initial snapshot.
  </Step>

  <Step title="Authenticate for private channels">
    <Note>
      Steps 1–3 cover public channels (no authentication required). Steps 4–5
      require an API key — see [Authentication](/api-reference/authentication)
      for key creation and HMAC-SHA512 signing details.
    </Note>

    Private channels (balance updates, order updates) require authentication. First obtain a WebSocket token via the REST API, then send an `authorize` message.

    <Tabs>
      <Tab title="Python">
        ```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
        import base64
        import hashlib
        import hmac
        import time
        import requests

        API_KEY = "YOUR_API_KEY"
        API_SECRET = "YOUR_API_SECRET"
        BASE_URL = "https://whitebit.com"

        # Part A — Get WebSocket token via REST API
        path = "/api/v4/profile/websocket_token"
        data = {"request": path, "nonce": int(time.time() * 1000)}
        data_json = json.dumps(data)
        payload_b64 = base64.b64encode(data_json.encode()).decode()
        signature = hmac.new(
            API_SECRET.encode(), payload_b64.encode(), hashlib.sha512
        ).hexdigest()
        headers = {
            "Content-Type": "application/json",
            "X-TXC-APIKEY": API_KEY,
            "X-TXC-PAYLOAD": payload_b64,
            "X-TXC-SIGNATURE": signature,
        }
        token_response = requests.post(BASE_URL + path, headers=headers, data=data_json)
        ws_token = token_response.json().get("websocket_token")
        print(f"WebSocket token obtained")

        # Part B — Send authorize message over WebSocket
        auth_msg = {
            "id": 3,
            "method": "authorize",
            "params": [ws_token, "public"]
        }
        ws.send(json.dumps(auth_msg))
        auth_result = json.loads(ws.recv())
        print(f"Auth result: {auth_result}")
        ```
      </Tab>

      <Tab title="wscat">
        ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
        # First obtain a token via REST (see Python example),
        # then send in wscat:
        > {"id": 3, "method": "authorize", "params": ["YOUR_WS_TOKEN", "public"]}
        ```
      </Tab>
    </Tabs>

    **Token endpoint:** `POST /api/v4/profile/websocket_token` (authenticated REST call).

    **Authorization response:**

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {"id": 3, "result": {"status": "success"}, "error": null}
    ```
  </Step>

  <Step title="Subscribe to spot balance updates">
    After authentication, subscribe to real-time spot balance changes.

    <Tabs>
      <Tab title="Python">
        ```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
        # Subscribe to USDT and BTC balance updates
        balance_msg = {
            "id": 4,
            "method": "balanceSpot_subscribe",
            "params": ["USDT", "BTC"]
        }
        ws.send(json.dumps(balance_msg))

        # Receive subscribe confirmation
        result = json.loads(ws.recv())
        print(f"Balance subscribe: {result}")

        # Balance updates arrive when trades execute or transfers occur
        update = json.loads(ws.recv())
        print(f"Balance update: {update}")
        ```
      </Tab>

      <Tab title="wscat">
        ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
        > {"id": 4, "method": "balanceSpot_subscribe", "params": ["USDT", "BTC"]}
        ```
      </Tab>
    </Tabs>

    **Update message (triggered by a trade or transfer):**

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "id": null,
      "method": "balanceSpot_update",
      "params": [
        {
          "USDT": {
            "available": "985.50",
            "freeze": "14.50"
          }
        }
      ]
    }
    ```

    Key fields: `available` (funds ready for trading), `freeze` (funds locked in open orders).
  </Step>

  <Step title="Send a keepalive ping">
    The server closes the connection after 60 seconds of inactivity. Send a ping message to verify the connection is alive.

    <Tabs>
      <Tab title="Python">
        ```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
        ping_msg = {"id": 0, "method": "ping", "params": []}
        ws.send(json.dumps(ping_msg))
        pong = json.loads(ws.recv())
        print(f"Ping result: {pong}")
        ```
      </Tab>

      <Tab title="wscat">
        ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
        > {"id": 0, "method": "ping", "params": []}
        ```
      </Tab>
    </Tabs>

    **Expected response:**

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {"id": 0, "result": "pong", "error": null}
    ```

    In production, send a ping every 50 seconds or less to prevent disconnection.
  </Step>
</Steps>

The six steps above cover a single connection. Production integrations need automatic reconnection and state recovery, covered next.

## Reconnection and state recovery

WebSocket connections can drop due to network issues or server maintenance. Production integrations require automatic reconnection, re-authentication, and state recovery logic.

### Reconnection with exponential backoff

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
import asyncio, json, random, time, websockets, requests, base64, hashlib, hmac

API_KEY = "YOUR_API_KEY"
API_SECRET = "YOUR_API_SECRET"
BASE_URL = "https://whitebit.com"

# Channels to subscribe after every (re)connect
PUBLIC_SUBS = [
    {"method": "depth_subscribe", "params": ["BTC_USDT", 20, "0", True]},
    {"method": "lastprice_subscribe", "params": ["BTC_USDT"]},
]
PRIVATE_SUBS = [
    {"method": "balanceSpot_subscribe", "params": ["USDT", "BTC"]},
    {"method": "ordersPending_subscribe", "params": ["BTC_USDT"]},
]

def get_ws_token():
    """Fetch a fresh WebSocket token via the REST API."""
    path = "/api/v4/profile/websocket_token"
    data = json.dumps({"request": path, "nonce": int(time.time() * 1000)})
    payload = base64.b64encode(data.encode()).decode()
    sig = hmac.new(API_SECRET.encode(), payload.encode(), hashlib.sha512).hexdigest()
    headers = {
        "Content-Type": "application/json",
        "X-TXC-APIKEY": API_KEY,
        "X-TXC-PAYLOAD": payload,
        "X-TXC-SIGNATURE": sig,
    }
    resp = requests.post(BASE_URL + path, headers=headers, data=data)
    return resp.json()["websocket_token"]

async def subscribe_all(ws):
    """Authenticate and subscribe to all channels."""
    # Authenticate for private channels
    token = get_ws_token()
    await ws.send(json.dumps({"id": 1, "method": "authorize", "params": [token, "public"]}))
    auth = json.loads(await ws.recv())
    if auth.get("error"):
        raise Exception(f"Auth failed: {auth['error']}")

    # Subscribe to all channels
    msg_id = 2
    for sub in PUBLIC_SUBS + PRIVATE_SUBS:
        await ws.send(json.dumps({"id": msg_id, **sub}))
        msg_id += 1

async def run():
    """Main loop with automatic reconnection."""
    delay = 1
    while True:
        try:
            async with websockets.connect("wss://api.whitebit.com/ws") as ws:
                delay = 1  # reset backoff on successful connection
                await subscribe_all(ws)
                while True:
                    msg = json.loads(await asyncio.wait_for(ws.recv(), timeout=55))
                    # Process updates here
        except (asyncio.TimeoutError, websockets.exceptions.ConnectionClosed):
            jitter = random.uniform(0, delay * 0.5)
            wait = min(delay + jitter, 30)
            print(f"Disconnected — reconnecting in {wait:.1f}s")
            await asyncio.sleep(wait)
            delay = min(delay * 2, 30)  # exponential backoff, max 30s

asyncio.run(run())
```

Key points:

* **Exponential backoff** — delays double on each failure (1s → 2s → 4s → ... → 30s max) with random jitter to prevent thundering herd
* **Fresh token** — obtain a new WebSocket token on every reconnect; tokens may expire during long disconnections
* **Full resubscription** — re-subscribe to all channels after reconnecting; the server does not remember previous subscriptions

For an implementation with fill monitoring, grid strategy logic, and error recovery, see [Building a Trading Bot — Auto-resubscription](/guides/building-a-trading-bot#auto-resubscription-after-reconnect).

### State recovery after reconnect

Different channels use different update models. The recovery strategy depends on the channel type:

| Update model             | Channels                                                                                                                                                                                             | Recovery strategy                                                                                                                                                                                                                                                          |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Snapshot on subscribe    | [Depth](/websocket/market-streams/depth)                                                                                                                                                             | First message after subscribe is a full snapshot — local state auto-recovers. Apply incremental updates from subsequent messages. Detect gaps via the `past_update_id` chain on incremental deltas; keepalive snapshots (`params[0]` is `true`) are full resets, not gaps. |
| Periodic full snapshot   | [Positions](/websocket/account-streams/positions), [Borrows](/websocket/account-streams/borrows), [Market](/websocket/market-streams/market), [Market Today](/websocket/market-streams/market-today) | Full state arrives within 1–1.5s of subscribing — no gap to fill.                                                                                                                                                                                                          |
| Incremental delta        | [Balance Spot](/websocket/account-streams/balance-spot), [Balance Margin](/websocket/account-streams/balance-margin)                                                                                 | **Query first, then subscribe.** Send a one-time query (`balanceSpot_request` / `balanceMargin_request`) to get full state, then subscribe for incremental changes.                                                                                                        |
| Event stream             | [Orders Pending](/websocket/account-streams/orders-pending), [Orders Executed](/websocket/account-streams/orders-executed), [Deals](/websocket/account-streams/deals)                                | **Query first, then subscribe.** Events during disconnection are lost. Use the one-time query operation or the REST equivalent to backfill, then subscribe.                                                                                                                |
| Event-only (no snapshot) | [Margin Positions Events](/websocket/account-streams/margin-positions-events), [Borrows Events](/websocket/account-streams/borrows-events)                                                           | Pair with the snapshot channel ([Positions](/websocket/account-streams/positions) / [Borrows](/websocket/account-streams/borrows)) for complete state awareness.                                                                                                           |
| Simple replacement       | [Last Price](/websocket/market-streams/lastprice), [Book Ticker](/websocket/market-streams/book-ticker), [Kline](/websocket/market-streams/kline)                                                    | Each message replaces previous value — no gap handling needed.                                                                                                                                                                                                             |

For incremental-delta and event-stream channels, use a **query-then-subscribe** pattern:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
async def recover_and_subscribe(ws):
    """Query current state, then subscribe for live updates."""
    # Step 1 — query full state
    await ws.send(json.dumps({
        "id": 10, "method": "balanceSpot_request", "params": []
    }))
    snapshot = json.loads(await ws.recv())
    # Initialize local state from snapshot["result"]

    # Step 2 — subscribe for incremental updates
    await ws.send(json.dumps({
        "id": 11, "method": "balanceSpot_subscribe", "params": ["USDT", "BTC"]
    }))
```

## What's next

<CardGroup cols={3}>
  <Card title="Market Data Overview" icon="chart-bar" href="/products/market-data/overview">
    REST API counterpart — polling-based market data for simpler integrations.
  </Card>

  <Card title="Building a Trading Bot" icon="robot" href="/guides/building-a-trading-bot">
    Combine WebSocket data with REST order placement for automated trading.
  </Card>

  <Card title="WebSocket Channels" icon="satellite-dish" href="/websocket/overview">
    Full channel catalog — 10 market streams and 11 account streams with schema references.
  </Card>
</CardGroup>
