Skip to main content
The SDK includes a dedicated orderbook client for real-time book data from ob.polynode.dev. This is a separate WebSocket connection from the event stream, with its own protocol optimized for orderbook updates across 108k+ Polymarket markets.

Subscribe

import { PolyNode, LocalOrderbook } from 'polynode-sdk';

const pn = new PolyNode({ apiKey: 'pn_live_...' });
await pn.orderbook.subscribe(['token_id_1', 'token_id_2']);

Events

pn.orderbook.on('snapshot', (snap) => {
  console.log(snap.asset_id, snap.bids.length, 'bids');
});

pn.orderbook.on('update', (delta) => {
  // Incremental delta. size "0" = removal.
  console.log(delta.asset_id, delta.bids.length, 'changes');
});

pn.orderbook.on('price', (change) => {
  for (const asset of change.assets) {
    console.log(asset.outcome, asset.price);
  }
});

LocalOrderbook

Maintain a sorted local copy of the book:
import { LocalOrderbook } from 'polynode-sdk';

const book = new LocalOrderbook();

pn.orderbook.on('snapshot', (snap) => book.applySnapshot(snap));
pn.orderbook.on('update', (delta) => book.applyUpdate(delta));

const fullBook = book.getBook(tokenId);   // { bids, asks }
const bestBid = book.getBestBid(tokenId);  // { price, size }
const spread = book.getSpread(tokenId);    // number

OrderbookEngine

Higher-level wrapper that manages one connection, maintains local state, and supports filtered views for different UI components.
import { OrderbookEngine } from 'polynode-sdk';

const engine = new OrderbookEngine({ apiKey: 'pn_live_...', compress: true });
await engine.subscribe([tokenId1, tokenId2]);

engine.on('ready', () => {
  console.log(`${engine.size} books loaded`);
});

// Query state
engine.midpoint(tokenId);  // (bestBid + bestAsk) / 2
engine.spread(tokenId);    // bestAsk - bestBid
engine.bestBid(tokenId);   // { price, size }

// Filtered views — no extra connections
const view = engine.view([tokenA, tokenB]);
view.on('update', (u) => console.log(u.asset_id));
view.midpoint(tokenA);

// Swap tokens or destroy
view.setTokens([newTokenX]);
view.destroy();

engine.close();