Public WebSocket API
- Service
- Ping
- Time
- Kline
- Last price
- Market statistics
- Market statistics for current day UTC
- Market trades
- Market depth
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 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
- Receive the Update Message: Listen for
depth_update
messages from the WebSocket connection. - 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 istrue
), replace the current order book with the new data. - If it’s a partial update (
full_reload_flag
isfalse
), update the existing order book with the new price levels.
- If it’s a full reload (
- Update the Order Book: Apply the changes from the update message to the local order book.
- For each price level in the
asks
andbids
arrays:- If the quantity is
0
, remove that price level from the order book. - Otherwise, update the price level with the new quantity.
- If the quantity is
- For each price level in the
- 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
}