Skip to main content
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. The walkthrough covers connecting to the WebSocket endpoint, subscribing to public price updates, subscribing to orderbook depth, authenticating for private channels, and receiving balance updates.

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
  • No special setup for public channels — same zero-authentication principle as the Market Data API
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.

Connection parameters

DetailValue
Endpointwss://api.whitebit.com/ws
ProtocolJSON-RPC 2.0 over WebSocket
Inactivity timeout60 seconds — send ping every 50 seconds or less
Rate limit200 requests per minute per connection
1

Connect to the WebSocket endpoint

Establish a WebSocket connection.
import websocket
import json

ws = websocket.create_connection("wss://api.whitebit.com/ws")
print("Connected to WebSocket API")
A successful connection produces no immediate response. The server is ready to receive subscription messages.For Go and PHP examples, see SDKs.
2

Subscribe to last price updates

Subscribe to real-time price updates for BTC_USDT.
# 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}")
Subscribe confirmation:
{"id": 1, "result": {"status": "success"}, "error": null}
Update message:
{
  "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.
3

Subscribe to orderbook depth

Subscribe to orderbook depth updates for BTC_USDT with 5 price levels.
# 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}")
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. Full parameter details: Depth channel.Update message:
{
  "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.
4

Authenticate for private channels

Steps 1–3 cover public channels (no authentication required). Steps 4–5 require an API key — see Authentication for key creation and HMAC-SHA512 signing details.
Private channels (balance updates, order updates) require authentication. First obtain a WebSocket token via the REST API, then send an authorize message.
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": str(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}")
Token endpoint: POST /api/v4/profile/websocket_token (authenticated REST call).Authorization response:
{"id": 3, "result": {"status": "success"}, "error": null}
5

Subscribe to spot balance updates

After authentication, subscribe to real-time spot balance changes.
# 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}")
Update message (triggered by a trade or transfer):
{
  "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).
6

Send a keepalive ping

The server closes the connection after 60 seconds of inactivity. Send a ping message to verify the connection is alive.
ping_msg = {"id": 0, "method": "ping", "params": []}
ws.send(json.dumps(ping_msg))
pong = json.loads(ws.recv())
print(f"Ping result: {pong}")
Expected response:
{"id": 0, "result": "pong", "error": null}
In production, send a ping every 50 seconds or less to prevent disconnection.
The walkthrough covered connecting to the WebSocket API, receiving real-time price updates and orderbook depth on public channels, authenticating for private data, and subscribing to balance updates. The WebSocket API provides the real-time data layer for trading bots, price feeds, and account monitoring.

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

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": str(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 a production-grade implementation with fill monitoring, grid strategy logic, and error recovery, see Building a Trading Bot — Auto-resubscription.

State recovery after reconnect

Different channels use different update models. The recovery strategy depends on the channel type:
Update modelChannelsRecovery strategy
Snapshot on subscribeDepthFirst message after subscribe is a full snapshot — local state auto-recovers. Apply incremental updates from subsequent messages. Detect gaps via update_id sequencing.
Periodic full snapshotPositions, Borrows, Market, Market TodayFull state arrives within 1–1.5s of subscribing — no gap to fill.
Incremental deltaBalance Spot, Balance MarginQuery first, then subscribe. Send a one-time query (balanceSpot_request / balanceMargin_request) to get full state, then subscribe for incremental changes.
Event streamOrders Pending, Orders Executed, DealsQuery 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, Borrows EventsPair with the snapshot channel (Positions / Borrows) for complete state awareness.
Simple replacementLast Price, Book Ticker, KlineEach message replaces previous value — no gap handling needed.
For incremental-delta and event-stream channels, use a query-then-subscribe pattern:
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

Market Data Overview

REST API counterpart — polling-based market data for simpler integrations.

Building a Trading Bot

Combine WebSocket data with REST order placement for automated trading.

WebSocket Channels

Full channel catalog — 10 market streams and 11 account streams with schema references.