> For the complete documentation index, see [llms.txt](https://docs.lighter.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.lighter.xyz/developers/integration-guides/market-data.md).

# Market data

Markets, order books, trades, candles and prices over REST and WebSocket.

All market data is **public** – no keys or auth needed.

## Discover markets

Binary options markets are not returned by `orderBookDetails`. Discover them through [events](https://docs.lighter.xyz/api/binary-options); see [Binary options: Discover markets](/developers/binary-options/discover-markets.md) for trading windows and metadata.

```shell
curl "https://mainnet.zklighter.elliot.ai/api/v1/orderBookDetails"
```

[orderBookDetails](https://docs.lighter.xyz/api/orders) returns `order_book_details` (perps) and `spot_order_book_details` (spot). The fields you'll use most:

| Field                                 | Meaning                                                                                                                 |
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `market_id`                           | Index used everywhere else                                                                                              |
| `size_decimals`, `price_decimals`     | Integer scaling for orders (see [Core concepts](/developers/getting-started/core-concepts.md#integer-prices-and-sizes)) |
| `min_base_amount`, `min_quote_amount` | Order minimums (maker orders)                                                                                           |
| `status`                              | Only trade `active` markets                                                                                             |

Cache this at startup and refresh periodically – new markets are listed regularly. Never hard-code market IDs other than for tests.

## Stream over WebSocket

```typescript
const ws = new WebSocket("wss://mainnet.zklighter.elliot.ai/stream");

ws.onopen = () => {
  ws.send(JSON.stringify({ type: "subscribe", channel: "order_book/0" }));
  ws.send(JSON.stringify({ type: "subscribe", channel: "trade/0" }));
  ws.send(JSON.stringify({ type: "subscribe", channel: "market_stats/all" }));
};

// Keepalive: send something at least every 2 minutes
setInterval(() => ws.send(JSON.stringify({ type: "ping" })), 60_000);

ws.onmessage = (e) => {
  const msg = JSON.parse(e.data);
  switch (msg.type) {
    case "subscribed/order_book": book.init(msg.order_book);  break; // full snapshot
    case "update/order_book":     book.apply(msg.order_book); break; // deltas
    case "update/trade":          /* new trades */ break;
    case "update/market_stats":   /* mark, index, funding, 24h stats */ break;
  }
};
```

Every channel and its payload is documented in the [WebSocket reference](https://docs.lighter.xyz/api/websocket#channels). Always reconnect and resubscribe automatically: deployments can drop connections.

## Keeping an order book in sync

The `order_book` channel sends a full snapshot on subscribe, then only changed levels. A level's `size` is its new total, and `"0"` means remove it. Each update's `begin_nonce` must equal the previous message's `nonce`; if it doesn't, you missed an update and must resubscribe. Use `nonce` for continuity, not `offset` (see [Order Book](https://docs.lighter.xyz/api/websocket#order-book)).

A minimal implementation:

```typescript
type Level = { price: string; size: string };
type BookMsg = { asks: Level[]; bids: Level[]; nonce: number; begin_nonce?: number };

const book = {
  asks: new Map<string, string>(), // price -> size
  bids: new Map<string, string>(),
  nonce: -1,

  init(ob: BookMsg) {
    this.asks.clear(); this.bids.clear();
    this.merge(ob);
    this.nonce = ob.nonce;
  },

  apply(ob: BookMsg) {
    if (this.nonce < 0) return;                 // no snapshot yet
    if (ob.begin_nonce !== this.nonce) {        // gap: resync
      this.nonce = -1;
      ws.send(JSON.stringify({ type: "unsubscribe", channel: "order_book/0" }));
      ws.send(JSON.stringify({ type: "subscribe",   channel: "order_book/0" }));
      return;
    }
    this.merge(ob);
    this.nonce = ob.nonce;
  },

  merge(ob: BookMsg) {
    for (const [side, levels] of [[this.asks, ob.asks], [this.bids, ob.bids]] as const) {
      for (const { price, size } of levels) {
        if (Number(size) === 0) side.delete(price); else side.set(price, size);
      }
    }
  },
};
```

Sort the maps by price when you need best bid/ask or a depth view.

## REST alternatives

For polling or backfill, use [orderBookOrders](https://docs.lighter.xyz/api/orders), [recentTrades](https://docs.lighter.xyz/api/orders), [candles](https://docs.lighter.xyz/api/candlesticks), [fundings](https://docs.lighter.xyz/api/funding) and [exchangeStats](https://docs.lighter.xyz/api/info). Mind the [rate limits](/developers/reference/rate-limits.md): Standard accounts get only 60 requests/minute – prefer WebSocket.

For binary options, `recentTrades` returns an empty list after settlement; use [trades](https://docs.lighter.xyz/api/orders) with `market_id` for history. `exchangeStats` includes binary options symbols. See [Binary options: Orders and trades](/developers/binary-options/trading-and-fees.md#orders-and-trades).

## Using lighter-ts

[elliottech/lighter-ts](https://github.com/elliottech/lighter-ts) open-sources Lighter's own client-side state store and WebSocket pipeline (see [SDK](/developers/getting-started/sdks.md)). If you use it, note that markets have a `multiplier`, so its "display" values differ from wire values:

* `display_size = real_size × multiplier`
* `display_price = real_price ÷ multiplier`

Keep conversion in one place – mixing real and display numbers is the most common pricing bug.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.lighter.xyz/developers/integration-guides/market-data.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
