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 belowerror- Null for success, JSON Object for failure:message- Detailed textcode- Error code
| Code | Message |
|---|---|
| 1 | invalid argument |
| 2 | internal error |
| 3 | service unavailable |
| 4 | method not found |
| 5 | service 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 specific markets, or all markets. Every following subscription will replace the existing one.
Note: To subscribe to all markets, send the request with an empty params array.
⤴️ Request
Subscribe to specific markets:
{
"id": 9,
"method": "trades_subscribe",
"params": [
"ETH_BTC", // markets
"BTC_USDT",
]
}Subscribe to all markets:
{
"id": 9,
"method": "trades_subscribe",
"params": []
}⤵️ 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)
"rpi": true // Indicates whether the trade involved a Retail Price Improvement (RPI) order
}
]
]
}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, // timestamp of the update from matchengine
"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 market depth data updates. After successful subscription, the server immediately sends a full snapshot of the current order book as the first depth_update message. Subsequent messages are incremental updates containing only changes. The server pushes updates every 100ms to subscribed clients when there are actual changes to the order book. Note: For pairs with less frequent trades, if there are no updates to the order book, the server will not send duplicate update events with the previous state.
⤴️ 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. The first message after subscription is a full snapshot of the order book, while subsequent messages are incremental updates.
Note: For pairs with less frequent trades, if there are no actual changes to the order book, the server will not send duplicate update events. Updates are only sent when there are real changes to the order book state.
First message (full snapshot):
{
"id": null,
"method": "depth_update",
"params": [
{
"timestamp": 1689600180.5164471, // timestamp of the update from matchengine
"update_id": 214403,
"asks": [ // sorted ascending - full order book snapshot
["0.020846", "29.369"],
["0.020850", "15.123"],
["0.020855", "8.456"],
],
"bids": [ // sorted descending - full order book snapshot
["0.020844", "5.949"],
["0.020840", "12.345"],
["0.020835", "20.678"],
],
"event_time": 1749026542.817343
},
"ETH_BTC" // market pair
]
}Subsequent messages (incremental updates):
{
"id": null,
"method": "depth_update",
"params": [
{
"timestamp": 1689600180.5164471, // timestamp of the update from matchengine
"update_id": 214404,
"past_update_id": 214403, // present in incremental updates
"asks": [
["0.020861", "0"], // finished orders will be [price, "0"]
["0.020900", "2.5"],
],
"bids": [
["0.020844", "5.949"],
["0.020800", "0"],
],
"event_time": 1749026542.817343
},
"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
- Receive the Update Message: Listen for
depth_updatemessages from the WebSocket connection. - Check if First Message: Determine if this is the initial snapshot by checking if
past_update_idis missing ornull.- If first message (snapshot): Replace the entire order book with the snapshot data from
asksandbidsarrays. The snapshot already contains the correct number of price levels up to the limit. - If subsequent message (incremental update): Proceed to step 3.
- If first message (snapshot): Replace the entire order book with the snapshot data from
- Apply Updates (for incremental messages only): Apply the changes from the update message to the local order book.
- For each price level in the
asksandbidsarrays:- If the quantity is
"0", remove that price level from the order book. - Otherwise, update the price level with the new quantity or add it if it doesn’t exist.
- If the quantity is
- For each price level in the
- Truncate to Limit (for incremental messages only): 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 updateData = message.params[0] as Partial<OrderBook & { past_update_id?: number }>;
const isFirstMessage = !updateData.past_update_id;
if (isFirstMessage) {
// First message is a full snapshot - replace order book
orderBook.asks = updateData.asks ?? [];
orderBook.bids = updateData.bids ?? [];
} else {
// Subsequent messages are incremental updates
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
}Book Ticker
The Book Ticker stream provides instant snapshot updates to the best bid and ask prices and quantities for a market.
Subscribe
Update interval: instant snapshot of the current best bid and ask (BBO), followed by incremental updates.
⤴️ Request
{
"method": "bookTicker_subscribe",
"params": [
"SHIB_PERP" // Optional: market name. If empty, subscribes to all markets
],
"id": 1
}⤵️ Response
{
"id": 1,
"result": {
"status": "success"
},
"error": null
}🔄 Update events
{
"method": "bookTicker_update",
"params": [
[
1751958383.5933869, // transaction_time - timestamp of the update from matchengine
1751958383.5935569, // message_time - timestamp of the message from websocket
"SHIB_PERP", // market
80670102, // update_id
"0.000011751", // best_bid_price
"12547000", // best_bid_amount
"0.000011776", // best_ask_price
"17424000" // best_ask_amount
]
],
"id": null
}Unsubscribe
⤴️ Request
{
"method": "bookTicker_unsubscribe",
"params": [],
"id": 2
}⤵️ Response
{
"id": 2,
"result": {
"status": "success"
},
"error": null
}Try out WebSocket API directly in the documentation