> ## 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.

# Order Book Depth

> Subscribe to real-time order book depth updates for a trading pair via WhiteBIT WebSocket.

Real-time order book data with support for both one-time snapshots and continuous subscription updates. Each update contains changed price levels (asks and bids) as `[price, amount]` tuples. The server pushes incremental deltas every 100ms; if 10 seconds pass without an emitted `depth_update` on this subscription, a full snapshot is sent as a keepalive. Use this channel to maintain a live local order book for trading logic or display.

<Note>Connect to `wss://api.whitebit.com/ws` — see [WebSocket Overview](/websocket/overview) for protocol details and keepalive requirements.</Note>

## Rate limits

## Query order book depth (one-time)

Fetch a single order book snapshot without subscribing to updates.

**Request**

**Response**

## Subscribe to order book depth

<Note>
  Public `depth` updates exclude Retail Price Improvement (RPI) orders. The stream includes only regular order book liquidity.
</Note>

## Including RPI orders in a local order book

Public `depth` excludes RPI orders by design, so they never appear in this stream. RPI resting liquidity is not published on any market-data feed — it is visible only in private active orders and in the exchange UI order book.

To display an account's own RPI orders alongside the public book, overlay its private active orders onto the local depth book. Subscribe to [`ordersPending`](/websocket/account-streams/orders-pending) (private) alongside `depth_subscribe`, then merge:

1. **Overlay only orders with `rpi: true`.** Non-RPI orders already appear in the public `depth` aggregate — overlaying every order double-counts them.
2. **Aggregate by price before merging.** Public `depth` levels are price-aggregated `[price, total amount]`, while the private stream is per-order. Sum the RPI orders by price level, and use each order's `left` (remaining amount), not `amount` (original size).
3. **Map the side.** Order `side` `1` (sell) → ask; `2` (buy) → bid.
4. **Upsert by `order_id`.** Add or update on `event_id` `1` (new) and `2` (update); remove on `3` (finish). `order_id` changes on modify — correlate with `client_order_id`. Stop-order activation emits a second `event_id=1` for the same `order_id`, so upsert rather than append.
5. **Filter by `market`** to match the depth subscription.

This overlay reflects only the account's own RPI orders. No feed exposes other participants' RPI liquidity.

## Update frequency

The server pushes updates every **100ms** when the order book changes within the subscribed depth. If 10 seconds pass without an emitted `depth_update` on this subscription, a full snapshot is sent as a keepalive.

## Error codes

## Maintaining a local order book

To maintain an accurate local order book:

1. Subscribe and wait for the first message (`params[0]` is `true`) — initialize order book from snapshot
2. For each incremental update: if amount is `"0"` remove the price level; otherwise update or insert at the correct sorted position
3. Keep asks sorted ascending, bids sorted descending
4. Truncate to the configured limit after each update

## Update model

**First message** (`past_update_id` absent): Full order book snapshot. Replace any existing local state.

**Subsequent messages** (`past_update_id` present): Incremental deltas. Apply each price level change to the local book.

## Ordering guarantees

* Updates arrive in `update_id` order. Each incremental message's `past_update_id` matches the previous message's `update_id`.
* If 10 seconds pass without an emitted `depth_update` on this subscription, the server sends a full snapshot (`params[0]` is `true`, no `past_update_id`) as a keepalive. Treat this as a full reset.
* `update_id` reflects changes to the full order book for this market. A keepalive snapshot may carry an `update_id` greater than the last delta's `update_id` — this is expected and reflects book activity that did not produce a `depth_update` on this subscription, not a missed message.
* A gap between an incremental delta's `past_update_id` and the stored `update_id` indicates a missed message — re-subscribe to resync.

## Order book data object

* [Orderbook](/api-reference/market-data/orderbook) — full order book snapshot via REST for polling use cases.
* [Depth](/api-reference/market-data/depth) — lightweight depth snapshot within ±2% of last price via REST.

## Code examples

<CodeGroup>
  ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  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 or keepalive snapshot 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) {
          const priceIndex = orderBookSide.findIndex((level) => level[0] === price);

          if (amount === "0") {
              if (priceIndex !== -1) {
                  orderBookSide.splice(priceIndex, 1);
              }
          } else {
              if (priceIndex === -1) {
                 const insertIndex = orderBookSide.findIndex((level) =>
                      side === "ask" ? level[0] > price : level[0] < price
                  );

                  if (insertIndex === -1) {
                      orderBookSide.push([price, amount]);
                  } else {
                      orderBookSide.splice(insertIndex, 0, [price, amount]);
                  }
              } else {
                  orderBookSide[priceIndex][1] = amount;
              }
          }
      }
  }

  function truncateOrderBook(orderBookSide: IDepth[]) {
      if (orderBookSide.length > LIMIT) {
          orderBookSide.splice(LIMIT);
      }
  }
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import asyncio
  import json
  import websockets

  class OrderBook:
      def __init__(self):
          self.asks = []
          self.bids = []

  LIMIT = 100

  async def depth_subscribe():
      async with websockets.connect('wss://api.whitebit.com/ws') as ws:
          await ws.send(json.dumps({
              'id': 1,
              'method': 'depth_subscribe',
              'params': ['ETH_BTC', LIMIT, '0', True]
          }))

          async for message in ws:
              data = json.loads(message)
              if data.get('method') == 'depth_update':
                  handle_depth_update(data['params'])

  def handle_depth_update(params):
      update_data = params[0]
      is_first_message = update_data.get('past_update_id') is None

      if is_first_message:
          order_book.asks = update_data.get('asks', [])
          order_book.bids = update_data.get('bids', [])
      else:
          apply_updates(order_book.asks, update_data.get('asks', []), "ask")
          apply_updates(order_book.bids, update_data.get('bids', []), "bid")
          truncate_order_book(order_book.asks)
          truncate_order_book(order_book.bids)

  def apply_updates(order_book_side, updates, side):
      for price, amount in updates:
          price = str(price)
          amount = str(amount)

          price_index = next((i for i, level in enumerate(order_book_side) if level[0] == price), -1)

          if amount == '0':
              if price_index != -1:
                  order_book_side.pop(price_index)
          else:
              if price_index == -1:
                  insert_index = next((i for i, level in enumerate(order_book_side)
                                       if (side == "ask" and level[0] > price) or
                                       (side == "bid" and level[0] < price)), -1)
                  if insert_index == -1:
                      order_book_side.append([price, amount])
                  else:
                      order_book_side.insert(insert_index, [price, amount])
              else:
                  order_book_side[price_index][1] = amount

  def truncate_order_book(order_book_side):
      if len(order_book_side) > LIMIT:
          del order_book_side[LIMIT:]

  order_book = OrderBook()

  asyncio.get_event_loop().run_until_complete(depth_subscribe())
  ```

  ```java Java theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import java.io.ByteArrayInputStream;
  import java.net.URI;
  import java.net.URISyntaxException;
  import java.nio.charset.StandardCharsets;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.concurrent.CountDownLatch;
  import java.util.concurrent.TimeUnit;
  import javax.json.*;
  import javax.websocket.*;

  @ClientEndpoint
  public class OrderBookClient {
      private static final int LIMIT = 100;
      private static List<IDepth> asks = new ArrayList<>();
      private static List<IDepth> bids = new ArrayList<>();
      private static CountDownLatch latch;

      public static void main(String[] args) throws URISyntaxException, InterruptedException {
          latch = new CountDownLatch(1);
          WebSocketContainer container = ContainerProvider.getWebSocketContainer();
          URI uri = new URI("wss://api.whitebit.com/ws");
          container.connectToServer(OrderBookClient.class, uri);
          latch.await(1, TimeUnit.MINUTES);
      }

      @OnOpen
      public void onOpen(Session session) throws Exception {
          JsonObject message = Json.createObjectBuilder()
                  .add("id", 1)
                  .add("method", "depth_subscribe")
                  .add("params", Json.createArrayBuilder()
                          .add("ETH_BTC")
                          .add(LIMIT)
                          .add("0")
                          .add(true)
                  )
                  .build();
          session.getBasicRemote().sendText(message.toString());
      }

      @OnMessage
      public void onMessage(String message, Session session) {
          JsonReader reader = Json.createReader(new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8)));
          JsonObject jsonMessage = reader.readObject();

          if ("depth_update".equals(jsonMessage.getString("method"))) {
              JsonArray params = jsonMessage.getJsonArray("params");
              JsonObject updateData = params.getJsonObject(0);
              boolean isFirstMessage = !updateData.containsKey("past_update_id") ||
                                       updateData.isNull("past_update_id");

              if (isFirstMessage) {
                  asks = parseOrderBookSide(updateData.getJsonArray("asks"));
                  bids = parseOrderBookSide(updateData.getJsonArray("bids"));
              } else {
                  applyUpdates(asks, updateData.getJsonArray("asks"), "ask");
                  applyUpdates(bids, updateData.getJsonArray("bids"), "bid");
                  truncateOrderBook(asks);
                  truncateOrderBook(bids);
              }
          }
      }

      @OnClose
      public void onClose(Session session, CloseReason reason) {
          System.out.println("Connection closed: " + reason);
          latch.countDown();
      }

      @OnError
      public void onError(Session session, Throwable throwable) {
          throwable.printStackTrace();
          latch.countDown();
      }

      private static List<IDepth> parseOrderBookSide(JsonArray jsonArray) {
          List<IDepth> orderBookSide = new ArrayList<>();
          for (JsonValue value : jsonArray) {
              JsonArray level = value.asJsonArray();
              orderBookSide.add(new IDepth(level.getString(0), level.getString(1)));
          }
          return orderBookSide;
      }

      private static void applyUpdates(List<IDepth> orderBookSide, JsonArray updates, String side) {
          for (JsonValue value : updates) {
              JsonArray level = value.asJsonArray();
              String price = level.getString(0);
              String amount = level.getString(1);

              int priceIndex = -1;
              for (int i = 0; i < orderBookSide.size(); i++) {
                  if (orderBookSide.get(i).getPrice().equals(price)) {
                      priceIndex = i;
                      break;
                  }
              }

              if ("0".equals(amount)) {
                  if (priceIndex != -1) orderBookSide.remove(priceIndex);
              } else {
                  if (priceIndex == -1) {
                      int insertIndex = -1;
                      for (int i = 0; i < orderBookSide.size(); i++) {
                          if ((side.equals("ask") && orderBookSide.get(i).getPrice().compareTo(price) > 0) ||
                              (side.equals("bid") && orderBookSide.get(i).getPrice().compareTo(price) < 0)) {
                              insertIndex = i;
                              break;
                          }
                      }
                      if (insertIndex == -1) orderBookSide.add(new IDepth(price, amount));
                      else orderBookSide.add(insertIndex, new IDepth(price, amount));
                  } else {
                      orderBookSide.get(priceIndex).setAmount(amount);
                  }
              }
          }
      }

      private static void truncateOrderBook(List<IDepth> orderBookSide) {
          if (orderBookSide.size() > LIMIT) orderBookSide.subList(LIMIT, orderBookSide.size()).clear();
      }

      static class IDepth {
          private final String price;
          private String amount;

          public IDepth(String price, String amount) { this.price = price; this.amount = amount; }
          public String getPrice() { return price; }
          public String getAmount() { return amount; }
          public void setAmount(String amount) { this.amount = amount; }
      }
  }
  ```
</CodeGroup>
