---
title: "Real-Time Orderbook"
description: "Stream a live aggregated orderbook with the SDK, hooks, or UI components"
---

> **For AI agents:** the complete documentation index is at [llms.txt](/llms.txt). Append `.md` to any page URL for its markdown version.

The AGG WebSocket delivers a full aggregated orderbook snapshot on subscribe, then incremental
deltas as the book changes. Each outcome has its own orderbook — there is no complement derivation.
Each level includes per-venue attribution so you can render both the aggregated depth and the venue
breakdown from the same stream.

<Warning>
  **Orderbook subscriptions are keyed by `venueMarketOutcomeId`** — the `outcomes[].id` on REST
  market responses — not by `venueMarketId`. Subscribing with a `venueMarketId` never resolves a
  book and the gateway replies with a `Snapshot unavailable` error. (Only the REST
  `GET /orderbooks` endpoint and the arb streams use market-level ids.)
</Warning>

<Tabs>
  <Tab title="SDK (vanilla JS/TS)">
    <div key="sdk-vanilla-js-ts" className="[&>p]:mb-4 [&>p:last-child]:mb-0">
      Full control over the WebSocket connection and local orderbook state.

      ## 1. Connect and subscribe

      ```typescript
      import { createAggClient } from "@agg-build/sdk";

      const client = createAggClient({
        baseUrl: "https://api.agg.market",
        appId: "your-app-id",
        wsUrl: "wss://ws.agg.market/ws",
      });

      let book = null;

      const ws = client.createWebSocket({
        onSnapshot: (_outcomeId, nextBook) => {
          book = nextBook;
          renderOrderbook(book);
        },
        onDelta: (_outcomeId, nextBook) => {
          book = nextBook;
          renderOrderbook(book);
        },
        onError: (msg) => {
          console.error(msg.message);
        },
      });

      ws.subscribe("your-outcome-id", "orderbook");
      ```

      The SDK applies snapshots and deltas for you, tracks `seq`, validates `checksum` (XOR of
      per-level CRC32), drops stale deltas where `seq <= current.seq`, and requests a fresh snapshot
      when integrity checks fail. The gateway is a stateless fan-out — all orderbook recovery is
      handled client-side by the SDK.

      ## 2. Read the orderbook

      The `OrderbookState` passed to callbacks uses object-shaped levels:

      ```typescript
      interface OrderbookState {
        outcomeId: string;
        bids: Array<{ price: number; size: number; venues: Record<string, number> }>;
        asks: Array<{ price: number; size: number; venues: Record<string, number> }>;
        venueOrderbooks: Record<
          string,
          {
            bids: Array<{ price: number; size: number }>;
            asks: Array<{ price: number; size: number }>;
          }
        >;
        venues: Record<string, { bestBid: number | null; bestAsk: number | null }>;
        midpoint: number | null;
        spread: number | null;
        seq: number;
        checksum: number;
        timestamp: number; // seconds
      }
      ```

      ## 3. Per-venue orderbooks

      Per-venue depth is available on the same object:

      ```typescript
      const ws = client.createWebSocket({
        onSnapshot: (_outcomeId, book) => {
          console.log(book.venueOrderbooks.predict?.bids);
          console.log(book.venueOrderbooks.polymarket?.asks);
        },
      });
      ```

      ## 4. REST fallback

      For one-time fetches without a live socket:

      ```typescript
      const response = await client.getOrderbooks({
        venueMarketIds: ["your-market-id"],
        depth: 20,
      });

      const book = response.data[0];
      if (book?.status !== "ok") {
        throw new Error(book?.error?.message ?? "No live orderbook available");
      }

      // book.venueOrderbooks — per-venue depth keyed by venue
      // book.matchedMarkets  — venue markets considered for the requested market
      // book.requestedMarket — lifecycle metadata for the requested market
      ```
    </div>
  </Tab>

  <Tab title="Hooks (React)">
    <div key="hooks-react" className="[&>p]:mb-4 [&>p:last-child]:mb-0">
      <Note>
        See the [Setup Guide](/api/setup) for the one-time `AggProvider` and `QueryClientProvider`
        wiring. This recipe starts at the hook usage layer.
      </Note>

      ## 1. Live orderbook

      ```tsx
      import { useLiveMarket } from "@agg-build/hooks";

      function Orderbook({ marketId }) {
        const { orderbook, isConnected, integrity } = useLiveMarket(marketId);

        if (!orderbook) return <div>Connecting...</div>;

        return (
          <div>
            <p>Connected: {String(isConnected)}</p>

            <h3>Bids</h3>
            {orderbook.bids.map((level) => (
              <div key={level.price}>
                {(level.price * 100).toFixed(0)}% — {level.size} contracts
                {" ("}
                {Object.entries(level.venues)
                  .map(([venue, size]) => `${venue}: ${size}`)
                  .join(", ")}
                {")"}
              </div>
            ))}

            <p>
              Midpoint: {orderbook.midpoint} | Spread: {orderbook.spread}
            </p>

            <h3>Asks</h3>
            {orderbook.asks.map((level) => (
              <div key={level.price}>
                {(level.price * 100).toFixed(0)}% — {level.size} contracts
              </div>
            ))}

            {integrity === "resyncing" && <p>Resyncing…</p>}
          </div>
        );
      }
      ```

      ## 2. Per-venue orderbooks

      ```tsx
      import { useMarketOrderbook } from "@agg-build/hooks";

      function VenueOrderbooks({ venueMarketOutcomeId, venueOutcomes }) {
        const { data, isLoading } = useMarketOrderbook({
          venueMarketOutcomeId, // outcomes[].id — NOT a venueMarketId
          venueOutcomes, // [{ venue: "predict", venueMarketOutcomeId: "..." }, ...]
        });

        if (isLoading || !data) return <div>Loading...</div>;

        return (
          <div>
            {Object.entries(data.venueOrderbooks).map(([venue, book]) => (
              <div key={venue}>
                <h4>{venue}</h4>
                <p>Bids: {book.bids.length} levels</p>
                <p>Asks: {book.asks.length} levels</p>
              </div>
            ))}
          </div>
        );
      }
      ```

      ## 3. REST-only (no WebSocket)

      ```tsx
      import { useAggClient } from "@agg-build/hooks";
      import { useQuery } from "@tanstack/react-query";

      function StaticOrderbook({ marketId }) {
        const client = useAggClient();
        const { data } = useQuery({
          queryKey: ["orderbooks", marketId],
          queryFn: () =>
            client.getOrderbooks({
              venueMarketIds: [marketId],
            }),
        });

        const book = data?.data[0];
        // book?.venueOrderbooks, book?.matchedMarkets, book?.requestedMarket
      }
      ```
    </div>
  </Tab>

  <Tab title="UI Components">
    <div key="ui-components" className="[&>p]:mb-4 [&>p:last-child]:mb-0">
      Drop-in React components for live orderbook depth, venue attribution, and chart/orderbook tabs.

      ## [Market Details](/components/events/market-details) card with orderbook tab

      ```tsx
      import { MarketDetails } from "@agg-build/ui/events";

      function OrderbookCard({ event, marketId }) {
        return <MarketDetails event={event} marketId={marketId} defaultTab="order-book" />;
      }
      ```

      The card renders bid/ask rows, venue logos, sizes, totals, and live updates without manual
      subscription code.

      For a full event page with both chart and orderbook tabs, use
      [`EventMarketPage`](/components/pages/event-market-page) as shown in
      [Real-Time Charts](/recipes/websocket-charts).

      Browse the live [Market Details reference](/components/events/market-details) and
      [Event Market Page reference](/components/pages/event-market-page).
    </div>
  </Tab>
</Tabs>

## Integrity

The SDK verifies `seq` and `checksum` automatically and requests a fresh snapshot when the local
book drifts. If you are implementing the wire protocol yourself, use the
[WebSocket Protocol](/api/websocket) page for the snapshot/delta formats and resnapshot flow.

## Related

<Columns cols={2}>
  <Card title="WebSocket Protocol" icon="gear" href="/api/websocket">
    Wire format, sequencing, resnapshot requests, and heartbeat behavior.
  </Card>
  <Card title="Real-Time Charts" icon="code" href="/recipes/websocket-charts">
    Build live candles from the same orderbook and trade streams.
  </Card>
  <Card title="User Notifications" icon="key" href="/recipes/websocket-notifications">
    Authenticated order and balance events on the same WebSocket connection.
  </Card>
</Columns>
