Command line (wscat)
Copy
# 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:Copy
# 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)
Copy
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) {
console.log(` ${update.slug} [${asset.outcome}]: ${asset.price}`);
}
}
}
}
};
JavaScript (specific markets)
Copy
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) {
console.log(`${update.slug} [${asset.outcome}]: ${asset.price}`);
}
} 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)
Copy
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"]:
print(f" {update['slug']} [{asset['outcome']}]: {asset['price']}")
asyncio.run(stream_firehose())
Set
max_size to at least 10 MB when connecting to the firehose. Batch messages can be large when many markets update in the same 100ms window.Python (specific markets)
Copy
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['price']}")
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
Copy
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:Copy
await ws.send(json.dumps({
"action": "subscribe",
"markets": [
"what-price-will-bitcoin-hit-in-march-2026", # slug
"0xd1796c09d0d6f876f8580086ae9808ec991784e3a74b25a1830a25de71a78c96", # condition ID
"73624432805780182150964443951045800666977811185963019133914618974858599458273" # token ID
]
}))

