Guide
Track live prices: from REST to WebSocket
Most apps start by polling and move to streaming. Here is how to do both with the same ids.
Poll or stream?
| Poll /prices | Stream on the WebSocket | |
|---|---|---|
| Best for | Updates every minute or slower; up to 100 contracts per call | Second-by-second updates: bots, live boards |
| Key | Any key, including the demo key | Your own key (not the demo key) |
| Cost | One request per poll | No per-message cost; connection and subscription limits apply |
1. Get prices and stream ids from REST
Every v1 outcome carries a quote and stream ids. Get them from event detail for one event, or bulk prices for contracts you already know.
Request
curl -s 'https://api.thepredictionmarkets.com/api/public/v1/prices?ids=kalshi:KXPRESNOMD-28-GN,polymarket:0x0f49db97f71c68b1e42a6d16e3de93d85dbf7d4148e3f018eb79e88554be9f75' \
-H 'X-API-Key: tpm_demo_key_authorized_2026'2. Poll in bulk (simple)
Polling loop
import os
import time
import requests
BASE = "https://api.thepredictionmarkets.com/api/public/v1"
HEADERS = {"X-API-Key": os.environ["TPM_API_KEY"]}
IDS = ["kalshi:KXPRESNOMD-28-GN", "polymarket:0x0f49db97f71c68b1e42a6d16e3de93d85dbf7d4148e3f018eb79e88554be9f75"]
while True:
body = requests.get(f"{BASE}/prices", params={"ids": ",".join(IDS)}, headers=HEADERS, timeout=20).json()
for row in body["data"]:
q = row["quote"]
print(row["id"], q["probability"], q["freshness"])
if body["unresolved"]:
print("not tracked:", body["unresolved"])
time.sleep(60)3. Stream (live)
Pass each row's stream object to subscribe. Save the latest cursor so you can resume after a disconnect without missing a change.
REST then WebSocket
# pip install requests "websockets>=14"
import asyncio
import json
import os
import requests
import websockets
KEY = os.environ["TPM_API_KEY"]
BASE = "https://api.thepredictionmarkets.com/api/public/v1"
IDS = "kalshi:KXPRESNOMD-28-GN,polymarket:0x0f49db97f71c68b1e42a6d16e3de93d85dbf7d4148e3f018eb79e88554be9f75"
# 1. REST: current prices plus stream ids
rows = requests.get(f"{BASE}/prices", params={"ids": IDS}, headers={"X-API-Key": KEY}, timeout=20).json()["data"]
subscriptions = [row["stream"] for row in rows if row["stream"]]
# 2. WebSocket: live changes
async def stream():
async with websockets.connect("wss://api.thepredictionmarkets.com/api/public/v1/stream", additional_headers={"X-API-Key": KEY}) as ws:
async for raw in ws:
msg = json.loads(raw)
if msg["type"] == "welcome":
await ws.send(json.dumps({"op": "subscribe", "id": "s1", "subscriptions": subscriptions, "snapshot": True}))
elif msg["type"] == "quote":
q = msg["quote"]
print(q["provider"], q["market_id"], q["price"], q["feed_status"], "cursor", msg["cursor"])
asyncio.run(stream())For full reconnect logic (sequence gaps, resume tokens, resync snapshots), see the complete WebSocket client.
4. Fall back gracefully
- If the socket closes with
1011or sendsstream_unavailable, poll/pricesuntil it reconnects. - Check
/status: when a venue'slive_feedis notok, expectagingquotes from it. - Show
freshnessnext to every price so users can tell live data from old data.
Related
Build with TPM
Get your free API key
One key gives you Kalshi and Polymarket events, prices with freshness, price history, cross-venue gaps and the live quotes WebSocket.