polynode-sdk
v0.4.8
Published
TypeScript SDK for the PolyNode real-time Polymarket API
Maintainers
Readme
polynode-sdk
TypeScript SDK for the PolyNode real-time Polymarket API.
Stream settlements, trades, positions, deposits, oracle events, orderbook updates, and more through a single WebSocket connection. All events enriched with full market metadata.
New in v0.4: Local Cache — SQLite-backed local storage. Backfill wallet history in seconds, query trades and positions instantly with zero API calls.
Install
npm install polynode-sdk ws
# For local cache (optional):
npm install better-sqlite3Requires Node.js 18+.
Quick Start
import { PolyNode } from 'polynode-sdk';
const pn = new PolyNode({ apiKey: 'pn_live_...' });
// Fetch top markets
const markets = await pn.markets({ count: 10 });
console.log(`${markets.count} markets, ${markets.total} total`);
// Search
const results = await pn.search('bitcoin');
console.log(results.results[0].question);REST API
// System
await pn.healthz();
await pn.status();
await pn.createKey('my-bot');
// Markets
await pn.markets({ count: 10 });
await pn.market(tokenId);
await pn.marketBySlug('bitcoin-100k');
await pn.marketByCondition(conditionId);
await pn.marketsList({ count: 20, sort: 'volume' });
await pn.search('ethereum', { limit: 5 });
// Pricing
await pn.candles(tokenId, { resolution: '1h', limit: 100 });
await pn.stats(tokenId);
// Settlements
await pn.recentSettlements({ count: 20 });
await pn.tokenSettlements(tokenId, { count: 10 });
await pn.walletSettlements(address, { count: 10 });
// Wallets
await pn.wallet(address);
// RPC (rpc.polynode.dev)
await pn.rpc('eth_blockNumber');
await pn.rpc('eth_getBlockByNumber', ['latest', false]);WebSocket Streaming
const sub = await pn.ws.subscribe('settlements')
.minSize(100)
.status('pending')
.snapshotCount(20)
.send();
sub.on('settlement', (event) => {
console.log(`${event.taker_side} $${event.taker_size} on ${event.market_title}`);
});
sub.on('status_update', (event) => {
console.log(`Confirmed in ${event.latency_ms}ms`);
});
// Or use async iterator
for await (const event of sub) {
if (event.event_type === 'settlement') {
console.log(event.taker_wallet, event.taker_size);
}
}Subscription Types
pn.ws.subscribe('settlements'); // pending + confirmed settlements
pn.ws.subscribe('trades'); // all trade activity
pn.ws.subscribe('prices'); // price-moving events
pn.ws.subscribe('blocks'); // new Polygon blocks
pn.ws.subscribe('wallets'); // all wallet activity
pn.ws.subscribe('markets'); // all market activity
pn.ws.subscribe('large_trades'); // $1K+ trades
pn.ws.subscribe('oracle'); // UMA resolution events
pn.ws.subscribe('chainlink'); // real-time price feedsSubscription Filters
pn.ws.subscribe('settlements')
.wallets(['0xabc...'])
.tokens(['21742633...'])
.slugs(['bitcoin-100k'])
.conditionIds(['0xabc...'])
.side('BUY')
.status('pending')
.minSize(100)
.maxSize(10000)
.eventTypes(['settlement'])
.snapshotCount(50)
.feeds(['BTC/USD'])
.send();Orderbook Streaming
await pn.orderbook.subscribe(['token_id_1', 'token_id_2']);
pn.orderbook.on('snapshot', (snap) => {
console.log(snap.asset_id, snap.bids.length, 'bids', snap.asks.length, 'asks');
});
pn.orderbook.on('update', (delta) => {
console.log(delta.asset_id, delta.bids.length, 'bid changes');
});
pn.orderbook.on('price', (change) => {
for (const asset of change.assets) {
console.log(asset.outcome, asset.price);
}
});LocalOrderbook
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);
const bestBid = book.getBestBid(tokenId);
const bestAsk = book.getBestAsk(tokenId);
const spread = book.getSpread(tokenId);OrderbookEngine
Higher-level orderbook client. One connection, shared state, filtered views for different parts of your app.
import { OrderbookEngine } from 'polynode-sdk';
const engine = new OrderbookEngine({ apiKey: 'pn_live_...' });
// Subscribe with token IDs, slugs, or condition IDs
await engine.subscribe([tokenA, tokenB, tokenC]);
engine.on('ready', () => {
// Query computed values from local state
engine.midpoint(tokenA); // 0.465
engine.spread(tokenA); // 0.01
engine.bestBid(tokenA); // { price: '0.46', size: '226.29' }
engine.book(tokenA); // { bids: [...], asks: [...] }
// Create filtered views for different components
const view = engine.view([tokenA]);
view.on('update', (u) => console.log(u.asset_id, 'updated'));
view.midpoint(tokenA); // reads from shared state
// Swap tokens or destroy views at any time
view.setTokens([tokenD, tokenE]);
view.destroy();
});
engine.close();Local Cache
Store trades and positions in a local SQLite database. Backfills recent history on startup, streams live updates, and serves all queries locally with zero API calls.
import { PolyNode, PolyNodeCache } from 'polynode-sdk';
const pn = new PolyNode({ apiKey: 'pn_live_...' });
const cache = new PolyNodeCache(pn, {
dbPath: './cache.db',
watchlistPath: './polynode.watch.json',
onBackfillProgress: (p) => console.log(`${p.label}: ${p.fetched} trades`),
});
await cache.start();
// Query locally — instant, no API calls
const trades = cache.walletTrades('0xabc...', { limit: 50, side: 'BUY' });
const positions = cache.walletPositions('0xabc...');
const multiPos = cache.multiWalletPositions(['0xabc...', '0xdef...']);
const marketTrades = cache.marketTrades('0xcondition...');
// Add wallets at runtime
cache.addToWatchlist([{ type: 'wallet', id: '0xnew...', label: 'whale' }]);
// Stats
const stats = cache.stats();
console.log(`${stats.trade_count} trades, ${(stats.db_size_bytes / 1024 / 1024).toFixed(1)} MB`);
await cache.stop();Watchlist (polynode.watch.json):
{
"version": 1,
"wallets": [
{ "address": "0xabc...", "label": "trader-1", "backfill": true }
],
"settings": { "ttl_days": 30 }
}Backfill timing: 1 request per wallet at 1 req/s. 10 wallets = 10 seconds. Up to 500 trades per wallet (configurable with backfillPages).
See full documentation for all query methods, configuration options, and examples.
Compression & Reconnection
Zlib compression is enabled by default (~50% bandwidth savings). All connections auto-reconnect with exponential backoff.
// Compression is automatic — no config needed
// To disable (not recommended):
const ws = pn.configureWs({ compress: false });
ws.onConnect(() => console.log('connected'));
ws.onDisconnect((reason) => console.log('disconnected:', reason));
ws.onReconnect((attempt) => console.log('reconnected, attempt', attempt));
ws.onError((err) => console.error(err));Configuration
const pn = new PolyNode({
apiKey: 'pn_live_...',
baseUrl: 'https://api.polynode.dev',
wsUrl: 'wss://ws.polynode.dev/ws',
obUrl: 'wss://ob.polynode.dev/ws',
rpcUrl: 'https://rpc.polynode.dev',
timeout: 10000,
});Error Handling
import { PolyNode, ApiError, WsError } from 'polynode-sdk';
try {
await pn.market('invalid-id');
} catch (err) {
if (err instanceof ApiError) {
console.log(err.status);
console.log(err.message);
}
}Cleanup
sub.unsubscribe(); // remove one subscription
pn.ws.unsubscribeAll(); // remove all
pn.ws.disconnect(); // close event stream
pn.orderbook.unsubscribe(); // unsubscribe orderbook
pn.orderbook.disconnect(); // close orderbook streamTesting Utilities
The SDK includes helpers that return known-active Polymarket wallets for testing. Useful in examples, integration tests, and local development.
import { getActiveTestWallet, getActiveTestWallets } from 'polynode-sdk';
// Get a single active wallet (instant, uses cached fallback)
const wallet = await getActiveTestWallet();
// Get multiple active wallets
const wallets = await getActiveTestWallets(5);
// Fetch a fresh wallet from live data
const fresh = await getActiveTestWallet({ fresh: true });Combine with the cache for a zero-config quickstart:
const wallet = await getActiveTestWallet();
cache.addToWatchlist([{ type: 'wallet', id: wallet, label: 'test' }]);Links
License
MIT
