Public WebSocket API

WebSocket Connection Management

WebSocket endpoint is wss://api.whitebit.com/ws

The API is based on JSON RPC of WebSocket protocol.

⚠️ Connection Timeout ⚠️

  • Server closes websocket connection after 60 seconds of inactivity
  • Inactivity is defined as no messages sent by the client

Maintaining Connection

To keep the websocket connection active:

  • Send periodic ping messages every 50 seconds
  • Handle potential connection closures gracefully in your application logic

Example Implementation

// Establish websocket connection
const socket = new WebSocket("wss://api.whitebit.com/ws");
 
// Set up periodic ping
setInterval(() => {
  if (socket.readyState === WebSocket.OPEN) {
    socket.send(JSON.stringify({
            id: 0,
            method: "ping",
            params: [],
        }));
  }
}, 50000); // Every 50 seconds

❗ Rate limit 1000 ws connections per minute and 200 requests per minute in one connection.❗

All endpoints return time in Unix-time format.

⤴️ Request message

JSON Structure of request message:

  • id - Integer. Should be unique to handle response for your request.
  • method - String. Name of request.
  • params - Array. Here you pass params for method.

🚫 WebSocket connection will be closed if invalid JSON was sent.

Types of request messages

  • Query (ping, candles_request, etc)
  • Subscription (candles_subscribe, lastprice_subscribe, etc). Repeated subscription will be cancelled for the same data type.

⤵️ Response message

JSON Structure of response message:

  • id - Integer. Id of request.
  • result - Null for failure, for success - look for responses below
  • error - Null for success, JSON Object for failure:
    • message - Detailed text
    • code - Error code
CodeMessage
1invalid argument
2internal error
3service unavailable
4method not found
5service timeout

Types of response messages

  • Query result
  • Subscription status (success/failed)
  • Update events

Examples

Example messages for request with response:

⤴️ Request

{
  "id": 0,
  "method": "ping",
  "params": []
}

⤵️ Response

{
  "id": 0,
  "result": "pong",
  "error": null
}

Example subscription:

⤴️ Request

{
  "id": 0,
  "method": "candles_subscribe",
  "params": []
}

⤵️ Response

{
  "id": 0,
  "result": {
    "status": "success"
  },
  "error": null
}

🔄 Update events

{
  "id": null,
  "method": "candles_update",
  "params": [] // look below for params
}

API

Service

Ping

⤴️ Request
{
  "id": 0,
  "method": "ping",
  "params": []
}
⤵️ Response
{
  "id": 0,
  "result": "pong",
  "error": null
}

Time

⤴️ Request
{
  "id": 1,
  "method": "time",
  "params": []
}
⤵️ Response
{
  "id": 1,
  "result": 1493285895,
  "error": null
}

Kline

Query

The requested interval must meet the following conditions:

  • If the number is less than 60, then 60 must be divisible by the requested number without a remainder;
  • Less than 3600 (1 hour) - the number must be divisible by 60 without a remainder, and 3600 must be divisible by the requested number without a remainder;
  • Less than 86400 (day) - the number must be whitened by 3600 without a remainder, and 86400 must be divisible by the number without a remainder;
  • Less than 86400 * 7 (week) - the number must be divisible by 86400 without a remainder;
  • Equal to 86400 * 7;
  • Equal to 86400 * 30.
⤴️ Request
{
  "id": 2,
  "method": "candles_request",
  "params": [
    "ETH_BTC", // market
    1659569940, // start time
    1660894800, // end time
    3600 // interval in seconds
  ]
}
⤵️ Response
{
    "id": 2,
    "result": [
        [
            1580860800,       // time
            "0.020543",       // open
            "0.020553",       // close
            "0.020614",       // highest
            "0.02054",        // lowest
            "7342.597",       // volume in stock
            "151.095481849",  // volume in deal
            "ETH_BTC"         // market
        ],
        ...
    ],
    "error": null
}

Subscribe

Update interval: 0.5 sec

⤴️ Request
{
  "id": 3,
  "method": "candles_subscribe",
  "params": [
    "BTC_USD", // market
    900 // interval in seconds
  ]
}
⤵️ Response
{
  "id": 3,
  "result": {
    "status": "success"
  },
  "error": null
}
🔄 Update events
{
  "id": null,
  "method": "candles_update",
  "params": [
    [
      1580895000, // time
      "0.020683", // open
      "0.020683", // close
      "0.020683", // high
      "0.020666", // low
      "504.701", // volume in stock
      "10.433600491", // volume in money (deal)
      "ETH_BTC" // market
    ]
  ]
}

Unsubscribe

⤴️ Request
{
  "id": 4,
  "method": "candles_unsubscribe",
  "params": []
}
⤵️ Response
{
  "id": 4,
  "result": {
    "status": "success"
  },
  "error": null
}

Last price

Query

⤴️ Request
{
  "id": 5,
  "method": "lastprice_request",
  "params": [
    "ETH_BTC" // market
  ]
}
⤵️ Response
{
  "id": 5,
  "result": "0.020553",
  "error": null
}

Subscribe

Update interval: 1 sec

⤴️ Request
{
    "id": 6,
    "method": "lastprice_subscribe",
    "params": [
        "ETH_BTC", // markets
        "BTC_USDT",
        ...
    ]
}
⤵️ Response
{
  "id": 6,
  "result": {
    "status": "success"
  },
  "error": null
}
🔄 Update events
{
  "id": null,
  "method": "lastprice_update",
  "params": [
    "ETH_BTC", // market
    "0.020683" // price
  ]
}

Unsubscribe

⤴️ Request
{
  "id": 7,
  "method": "lastprice_unsubscribe",
  "params": []
}
⤵️ Response
{
  "id": 7,
  "result": {
    "status": "success"
  },
  "error": null
}

Market statistics

Query

⤴️ Request
{
  "id": 5,
  "method": "market_request",
  "params": [
    "ETH_BTC", // market
    86400 // period in seconds
  ]
}
⤵️ Response
{
  "id": 5,
  "result": {
    "period": 86400, // period in seconds
    "last": "0.020981", // last price
    "open": "0.02035", // open price that was at 'now - period' time
    "close": "0.020981", // price that closes this period
    "high": "0.020988", // highest price
    "low": "0.020281", // lowest price
    "volume": "135220.218", // volume in stock
    "deal": "2776.587022649" // volume in money
  },
  "error": null
}

Subscribe

You can subscribe only for 86400s (24h from now).

Update interval: 1 sec

⤴️ Request
{
    "id": 6,
    "method": "market_subscribe",
    "params": [
        "ETH_BTC", // markets
        "BTC_USDT",
        ...
    ]
}
⤵️ Response
{
  "id": 6,
  "result": {
    "status": "success"
  },
  "error": null
}
🔄 Update events
{
  "id": null,
  "method": "market_update",
  "params": [
    "ETH_BTC", // market
    {
      // response same as 'market_request'
      "period": 86400, // period in seconds
      "last": "0.020964", // last price
      "open": "0.020349", // open price that was at 'now - period' time
      "close": "0.020964", // price that closes this period
      "high": "0.020997", // highest price
      "low": "0.020281", // lowest price
      "volume": "135574.476", // volume in stock
      "deal": "2784.413999488" // volume in money
    }
  ]
}

Unsubscribe

⤴️ Request
{
  "id": 7,
  "method": "market_unsubscribe",
  "params": []
}
⤵️ Response
{
  "id": 7,
  "result": {
    "status": "success"
  },
  "error": null
}

Market statistics for current day UTC

Query

⤴️ Request
{
  "id": 14,
  "method": "marketToday_query",
  "params": [
    "ETH_BTC" // only one market per request
  ]
}
⤵️ Response
{
  "id": 14,
  "result": {
    "last": "0.020981", // last price
    "open": "0.02035", // open price that was at 'now - period' time
    "high": "0.020988", // highest price
    "low": "0.020281", // lowest price
    "volume": "135220.218", // volume in stock
    "deal": "2776.587022649" // volume in money
  },
  "error": null
}

Subscribe

Update interval: 1 sec

⤴️ Request
{
    "id": 15,
    "method": "marketToday_subscribe",
    "params": [
        "ETH_BTC", // markets
        "BTC_USDT",
        ...
    ]
}
⤵️ Response
{
  "id": 15,
  "result": {
    "status": "success"
  },
  "error": null
}
🔄 Update events
{
  "id": null,
  "method": "marketToday_update",
  "params": [
    "ETH_BTC", // market
    {
      // response same as 'market_request'
      "last": "0.020964", // last price
      "open": "0.020349", // open price that was at 'now - period' time
      "high": "0.020997", // highest price
      "low": "0.020281", // lowest price
      "volume": "135574.476", // volume in stock
      "deal": "2784.413999488" // volume in money
    }
  ]
}

Unsubscribe

⤴️ Request
{
  "id": 16,
  "method": "marketToday_unsubscribe",
  "params": []
}
⤵️ Response
{
  "id": 16,
  "result": {
    "status": "success"
  },
  "error": null
}

Market trades

Query

⤴️ Request
{
  "id": 8,
  "method": "trades_request",
  "params": [
    "ETH_BTC", // market
    100, // limit
    41358445 // largest id from which you want to request trades
  ]
}
⤵️ Response
{
    "id": 8,
    "result": [
        {
            "id": 41358530,           // trade id
            "time": 1580905394.70332, // time in milliseconds
            "price": "0.020857",      // trade price
            "amount": "5.511",        // trade amount
            "type": "sell"            // type of trade (buy/sell)
        },
        ...
    ],
    "error": null
}
 

Subscribe

Update interval: real-time

❗ For each websocket connection, you can subscribe to either one or several markets. Every following subscription will replace the existing one.

⤴️ Request
{
    "id": 9,
    "method": "trades_subscribe",
    "params": [
        "ETH_BTC", // markets
        "BTC_USDT",
        ...
    ]
}
⤵️ Response
{
  "id": 9,
  "result": {
    "status": "success"
  },
  "error": null
}
🔄 Update events
{
    "id": null,
    "method": "trades_update",
    "params": [
        "ETH_BTC",                         // market
         [                                 // response same as 'market_request'
             {
                 "id": 41358530,           // trade id
                 "time": 1580905394.70332, // time in milliseconds
                 "price": "0.020857",      // trade price
                 "amount": "5.511",        // trade amount
                 "type": "sell"            // type of trade (buy/sell)
             },
             ...
         ]
    ]
}

Unsubscribe

⤴️ Request
{
  "id": 10,
  "method": "trades_unsubscribe",
  "params": []
}
⤵️ Response
{
  "id": 10,
  "result": {
    "status": "success"
  },
  "error": null
}

Market depth

Query

This endpoint allows clients to request the current market depth for a specific cryptocurrency pair.

⤴️ Request
{
  "id": 11,
  "method": "depth_request",
  "params": [
    "ETH_BTC", // market
    100, // limit, max value is 100
    "0" // price interval units. "0" - no interval, available values - "0.00000001", "0.0000001", "0.000001", "0.00001", "0.0001", "0.001", "0.01", "0.1"
  ]
}
⤵️ Response
{
    "id": 11,
    "result": {
        "timestamp": 1689600180.5164471,
        "asks": [                   // sorted ascending
            ["0.020846", "29.369"], // [price, amount]
            ...
        ],
        "bids": [                   // sorted descending
            ["0.02083", "9.598"],   // [price, amount]
            ...
        ]
    },
    "error": null
}
 

Subscribe

This endpoint allows clients to subscribe to real-time updates of market depth data.

Update interval: 100 ms

⤴️ Request
{
  "id": 12,
  "method": "depth_subscribe",
  "params": [
    "ETH_BTC",  // market
    100,        // limit. available values - 1, 5, 10, 20, 30, 50, 100
    "0",        // price interval units. "0" - no interval, available values - "0.00000001", "0.0000001", "0.000001", "0.00001", "0.0001", "0.001", "0.01", "0.1"
    true        // multiple subscription flag. true - add, false - unsubscribe from all
  ]
}

The last parameter - Multiple subscription flag - allows you to subscribe to market depths as many markets as you want. The only restriction is one subscription with specific parameters per market.

⤵️ Response
{
  "id": 12,
  "result": {
    "status": "success"
  },
  "error": null
}
🔄 Update events

Update events provide real-time updates to the subscribed market depth.

{
    "id": null,
    "method": "depth_update",
    "params": [
        false,   // full_reload_flag | true - full reload, false - partial update
        {
            "timestamp": 1689600180.5164471,
            "asks": [
                ["0.020861", "0"],      // for partial update - finished orders will be [price, "0"]
                ["0.020900", "2.5"],
                ...
            ],
            "bids": [
                ["0.020844", "5.949"],
                ["0.020800", "0"],
                ...
            ]
        },
        "ETH_BTC"                       // market pair
    ]
}

Processing Update Messages

When the client subscribes with a limit (e.g., 100), the API sends updates for only the specified number of price levels (100 in this case) for both buy and sell sides. The client must process these updates and truncate the order book to maintain only the top 100 levels on each side.

Steps to Process Update Messages

  1. Receive the Update Message: Listen for depth_update messages from the WebSocket connection.
  2. Check Update Type: Determine if the update is a full reload or a partial update.
    • If it’s a full reload (full_reload_flag - first param in response is true), replace the current order book with the new data.
    • If it’s a partial update (full_reload_flag is false), update the existing order book with the new price levels.
  3. Update the Order Book: Apply the changes from the update message to the local order book.
    • For each price level in the asks and bids arrays:
      • If the quantity is 0, remove that price level from the order book.
      • Otherwise, update the price level with the new quantity.
  4. Truncate to Limit: Ensure that the order book contains only the top N (e.g., 100) price levels for both buy and sell sides.
💻 Code examples
type IDepth = [string, string];
 
interface OrderBook {
    asks: IDepth[];
    bids: IDepth[];
}
 
const ws = new WebSocket("wss://api.whitebit.com/ws");
const orderBook: OrderBook = { asks: [], bids: [] };
const LIMIT = 100;
 
ws.addEventListener("open", () => {
    ws.send(
        JSON.stringify({
            id: 1,
            method: "depth_subscribe",
            params: ["ETH_BTC", LIMIT, "0", true],
        }),
    );
});
 
ws.addEventListener("message", (event: MessageEvent) => {
    const message = JSON.parse(event.data.toString());
 
    if (message.method === "depth_update") {
        const [fullReload, updateData] = message.params as [boolean, Partial<OrderBook>];
 
        if (fullReload) {
            orderBook.asks = updateData.asks ?? [];
            orderBook.bids = updateData.bids ?? [];
        } else {
            applyUpdates(orderBook.asks, updateData.asks, "ask");
            applyUpdates(orderBook.bids, updateData.bids, "bid");
            truncateOrderBook(orderBook.asks);
            truncateOrderBook(orderBook.bids);
        }
    }
});
 
function applyUpdates(orderBookSide: IDepth[], updates: IDepth[] | undefined, side: "ask" | "bid") {
    if (updates === undefined) return;
    for (const [price, amount] of updates) {
        // Find the index of an entry in orderBookSide that matches the given price.
        const priceIndex = orderBookSide.findIndex((level) => level[0] === price);
 
        // If the amount is '0', it means this price level should be removed from the orderBookSide.
        if (amount === "0") {
            if (priceIndex !== -1) {
                // Remove the existing price level since the amount is '0'.
                orderBookSide.splice(priceIndex, 1);
            }
        } else {
            // If the amount is not '0', either update the existing price level or add a new one.
            if (priceIndex === -1) {
                // Find the position where the new price level should be inserted.
               const insertIndex = orderBookSide.findIndex((level) =>
                    side === "ask" ? level[0] > price : level[0] < price
                );
 
                if (insertIndex === -1) {
                    // Add to the end if there's no higher price level.
                    orderBookSide.push([price, amount]);
                } else {
                    // Insert at the correct sorted position.
                    orderBookSide.splice(insertIndex, 0, [price, amount]);
                }
            } else {
                // Update the amount for the existing price level.
                orderBookSide[priceIndex][1] = amount;
            }
        }
    }
}
 
function truncateOrderBook(orderBookSide: IDepth[]) {
    if (orderBookSide.length > LIMIT) {
        // Only truncate if the length exceeds the LIMIT
        orderBookSide.splice(LIMIT);
    }
}

Unsubscribe

This endpoint allows clients to unsubscribe from real-time updates of market depth data.

⤴️ Request
{
  "id": 13,
  "method": "depth_unsubscribe",
  "params": []
}
⤵️ Response
{
  "id": 13,
  "result": {
    "status": "success"
  },
  "error": null
}

Playground