Documentation Index
Fetch the complete documentation index at: https://polynode.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Command line (wscat)
# Subscribe to the full firehose (all active markets)
wscat -c "wss://ob.polynode.dev/ws?key=pn_live_YOUR_KEY" \
-x '{"action":"subscribe","markets":["*"]}'
# Subscribe by slug (easiest for specific markets)
wscat -c "wss://ob.polynode.dev/ws?key=pn_live_YOUR_KEY" \
-x '{"action":"subscribe","markets":["what-price-will-bitcoin-hit-in-march-2026"]}'
# Subscribe by condition ID
wscat -c "wss://ob.polynode.dev/ws?key=pn_live_YOUR_KEY" \
-x '{"action":"subscribe","markets":["0x561ffbf7de21ef3781c441f30536b026d2b301d7a4a0145a8f526f98db049ba2"]}'
# Subscribe by token ID
wscat -c "wss://ob.polynode.dev/ws?key=pn_live_YOUR_KEY" \
-x '{"action":"subscribe","markets":["73624432805780182150964443951045800666977811185963019133914618974858599458273"]}'
Finding markets
Use the PolyNode REST API to search for markets:# Search by keyword
curl -s "https://api.polynode.dev/v1/search?q=bitcoin" | python3 -m json.tool
# Look up by slug (from any Polymarket URL: polymarket.com/event/{slug})
curl -s "https://api.polynode.dev/v1/markets/slug/what-price-will-bitcoin-hit-in-march-2026"
# Look up by condition ID
curl -s "https://api.polynode.dev/v1/markets/condition/0x561ffbf7de21ef3781c441f30536b026d2b301d7a4a0145a8f526f98db049ba2"
JavaScript (firehose)
const ws = new WebSocket("wss://ob.polynode.dev/ws?key=pn_live_YOUR_KEY");
ws.onopen = () => {
ws.send(JSON.stringify({ action: "subscribe", markets: ["*"] }));
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === "subscribed") {
console.log(`Firehose active: ${msg.markets} tokens`);
} else if (msg.type === "batch") {
console.log(`${msg.count} updates at ${msg.ts}`);
for (const update of msg.updates) {
if (update.type === "price_change") {
for (const asset of update.assets) {
// size="0" removes the level; otherwise it's the new absolute size at that level.
console.log(` ${update.slug} [${asset.outcome}] ${asset.side} ${asset.price} -> ${asset.size} (bbo ${asset.best_bid}/${asset.best_ask})`);
}
}
}
}
};
JavaScript (specific markets)
const ws = new WebSocket("wss://ob.polynode.dev/ws?key=pn_live_YOUR_KEY");
ws.onopen = () => {
// Subscribe by slug — both sides (Yes + No) are included automatically
ws.send(JSON.stringify({
action: "subscribe",
markets: [
"what-price-will-bitcoin-hit-in-march-2026",
"netanyahu-out-before-2027"
]
}));
};
// Maintain local orderbook state
const books = {};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
switch (msg.type) {
case "subscribed":
console.log(`Subscribed to ${msg.markets} tokens`);
console.log(`Resolved from: ${JSON.stringify(msg.resolved_from)}`);
break;
case "snapshot_batch":
for (const snap of msg.snapshots) {
books[snap.asset_id] = { bids: snap.bids, asks: snap.asks };
console.log(`Snapshot: ${snap.event_title} [${snap.outcome}] - ${snap.bids.length}b/${snap.asks.length}a`);
}
break;
case "batch":
for (const update of msg.updates) {
if (update.type === "price_change") {
for (const asset of update.assets) {
// Apply directly to local books — price_change carries size + side so you
// can maintain a tick-accurate book without polling REST.
const book = books[asset.asset_id];
if (book) {
const levels = asset.side === "BUY" ? book.bids : book.asks;
applyDelta(levels, [{ price: asset.price, size: asset.size }]);
}
}
} else if (update.type === "book_update") {
const book = books[update.asset_id];
if (book) {
applyDelta(book.bids, update.bids);
applyDelta(book.asks, update.asks);
}
}
}
break;
}
};
// Apply incremental orderbook updates
function applyDelta(existing, deltas) {
for (const delta of deltas) {
const idx = existing.findIndex(l => l.price === delta.price);
if (delta.size === "0") {
if (idx >= 0) existing.splice(idx, 1);
} else if (idx >= 0) {
existing[idx].size = delta.size;
} else {
existing.push(delta);
}
}
}
ws.onclose = () => setTimeout(() => location.reload(), 3000);
Python (firehose)
import asyncio
import json
import websockets
async def stream_firehose():
url = "wss://ob.polynode.dev/ws?key=pn_live_YOUR_KEY"
async with websockets.connect(url, max_size=10_000_000) as ws:
await ws.send(json.dumps({
"action": "subscribe",
"markets": ["*"]
}))
async for message in ws:
data = json.loads(message)
if data["type"] == "subscribed":
print(f"Firehose active: {data['markets']} tokens")
elif data["type"] == "batch":
for update in data["updates"]:
if update["type"] == "price_change":
for asset in update["assets"]:
# size="0" removes the level; otherwise it's the new absolute size.
print(f" {update['slug']} [{asset['outcome']}] {asset['side']} {asset['price']} -> {asset['size']} (bbo {asset['best_bid']}/{asset['best_ask']})")
asyncio.run(stream_firehose())
Set
max_size to at least 10 MB when connecting to the firehose (full data stream). Batch messages can be large when many markets update in the same 100ms window.Python (specific markets)
import asyncio
import json
import websockets
async def stream_orderbook():
url = "wss://ob.polynode.dev/ws?key=pn_live_YOUR_KEY"
async with websockets.connect(url) as ws:
# Subscribe by slug — resolves to all token IDs for those events
await ws.send(json.dumps({
"action": "subscribe",
"markets": [
"what-price-will-bitcoin-hit-in-march-2026",
"netanyahu-out-before-2027",
]
}))
async for message in ws:
data = json.loads(message)
if data["type"] == "subscribed":
print(f"Subscribed to {data['markets']} tokens")
print(f"Resolved from: {data['resolved_from']}")
elif data["type"] == "snapshot_batch":
for snap in data["snapshots"]:
print(f"Snapshot: {snap['event_title']} [{snap['outcome']}]")
print(f" Bids: {len(snap['bids'])} levels")
print(f" Asks: {len(snap['asks'])} levels")
elif data["type"] == "batch":
for update in data["updates"]:
if update["type"] == "price_change":
for asset in update["assets"]:
print(f" {update['slug']} [{asset['outcome']}] {asset['side']} {asset['price']} -> {asset['size']} (bbo {asset['best_bid']}/{asset['best_ask']})")
elif update["type"] == "book_update":
bids = len(update.get("bids", []))
asks = len(update.get("asks", []))
print(f" Book delta: {update['slug']} [{update['outcome']}] +{bids}b/+{asks}a")
asyncio.run(stream_orderbook())
Python with compression
import asyncio
import json
import zlib
import websockets
async def stream_compressed():
url = "wss://ob.polynode.dev/ws?key=pn_live_YOUR_KEY&compress=zlib"
async with websockets.connect(url) as ws:
await ws.send(json.dumps({
"action": "subscribe",
"markets": ["what-price-will-bitcoin-hit-in-march-2026"]
}))
async for message in ws:
if isinstance(message, bytes):
text = zlib.decompress(message, -zlib.MAX_WBITS).decode("utf-8")
data = json.loads(text)
else:
data = json.loads(message)
print(f"[{data.get('type')}] {json.dumps(data)[:200]}")
asyncio.run(stream_compressed())
Mixed identifier subscribe
You can mix slugs, condition IDs, and token IDs in a single subscribe:await ws.send(json.dumps({
"action": "subscribe",
"markets": [
"what-price-will-bitcoin-hit-in-march-2026", # slug
"0xd1796c09d0d6f876f8580086ae9808ec991784e3a74b25a1830a25de71a78c96", # condition ID
"73624432805780182150964443951045800666977811185963019133914618974858599458273" # token ID
]
}))

