Live orderbooks

Real-Time Orderbook

Stream a live aggregated orderbook with the SDK, hooks, or UI components

The sufwan-local-test 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.

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

For bounded REST snapshots and the shared single/batch outcome-unit quota, see Market-data API. That guide also explains snapshotChecksum versus the full engine checksum and optional native Worker ownership.

Orderbook socket: wss://ob.agg.market/ws. The orderbook channel can run on its own socket with the same snapshot and delta objects. It keeps the 100 ms default and adds an opt-in realtime tier ("latency": "realtime" on subscribe). Backends can also stream every book on a venue from it once Snag enables the firehose for their app.

Full control over the WebSocket connection and local orderbook state.

1. Connect and subscribe

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

const client = createAggClient({
  baseUrl: "https://api.t.sufwankhalid.com",
  appId: "your-app-id",
  wsUrl: "wss://ws.t.sufwankhalid.com/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);
  },
});

const venueMarketOutcomeId = "vmo_1";
ws.subscribe(venueMarketOutcomeId, "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. It also runs a 5-minute book watchdog; see Integrity.

2. Read the orderbook

Orderbook timestamp is Unix milliseconds on the wire, and the SDK reports it as seconds.

The OrderbookState passed to callbacks uses object-shaped levels:

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 (SDK converts wire milliseconds)
}

3. Per-venue orderbooks

Per-venue depth is available on the same object:

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:

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

Integrity

The SDK keeps each book correct without any code from you:

  • Seq and checksum: it applies deltas only in order, verifies every checksum, and requests a fresh snapshot when either check fails.
  • Server-sent snapshots: if sufwan-local-test rebuilds a book, its seq restarts and the gateway sends a new snapshot with the first update from it. The SDK replaces the book with it.
  • Connection liveness: it reconnects with backoff after 45 seconds with no frame, and re-subscribes everything. Reconnects authenticate with the token from your latest authenticate() call.
  • Resolution: on market_resolved it drops the outcome's book and calls onMarketResolved.
  • Book watchdog: heartbeats only prove the connection is alive. Once a minute, the SDK checks any book with no update for 5 minutes against GET /orderbooks/outcomes (see Market-data API). A matching seq means the market is just quiet. A different seq triggers a resnapshot, then a resubscribe if the next check still differs or the snapshot never arrives.

The watchdog is on for sockets from client.createWebSocket(), which the hooks and UI components use too (always with the default timings). Each check costs one market-data quota unit per silent outcome, and it pauses for Retry-After on a 429. With the SDK directly, tune or disable it:

const ws = client.createWebSocket(callbacks, {
  bookWatchdog: { silenceMs: 10 * 60_000, checkIntervalMs: 2 * 60_000 },
  // or: bookWatchdog: false
});

Watch it through onDiagnostics:

const ws = client.createWebSocket({
  onDiagnostics: (event) => {
    if (event.type === "book_watchdog_resync") {
      // { outcomeId, localSeq, serverSeq, action: "resnapshot" | "resubscribe" }
      console.warn("orderbook healed", event);
    }
    if (event.type === "book_watchdog_error") {
      // { status } — the REST check failed; the next tick retries
    }
  },
});

If you are implementing the wire protocol yourself, follow Staleness checks and Best practices on the WebSocket Protocol page.

On this page