Pinnacle odds WebSocket Python example
Answer: install websockets, set PINNFEED_KEY, connect to wss://pinnfeed.io/v1/stream with header x-api-key, send a subscribe frame, apply every snapshot/delta, reply to ping with {"type":"pong"}, and reconnect with exponential backoff. You need the Builder plan (€99/mo) — Free and Hobby keys get 403 plan_lacks_ws on /v1/stream.
This is the same client the Python and Node guide ships; the snippet below is the minimal runnable shape.
Minimal client#
pip install websockets
export PINNFEED_KEY="ps_live_..."
# Example — truncated for the page. Full file: see the Python/Node guide.
import asyncio, json, os, random
import websockets
WS = os.environ.get("PINNFEED_WS", "wss://pinnfeed.io/v1/stream")
KEY = os.environ["PINNFEED_KEY"]
async def main():
delay = 1
while True:
try:
async with websockets.connect(WS, additional_headers={"x-api-key": KEY}, max_size=None) as ws:
await ws.send(json.dumps({
"type": "subscribe",
"sports": ["soccer", "tennis"],
"markets": ["moneyline"],
"main": True,
"specials": False,
}))
async for raw in ws:
msg = json.loads(raw)
if msg["type"] == "ping":
await ws.send('{"type":"pong"}')
elif msg["type"] == "snapshot" and msg.get("final"):
print("book ready", len(msg.get("markets", [])), "markets in last part")
delay = 1
elif msg["type"] == "delta":
for c in msg["changes"]:
if c["kind"] == "market" and c["op"] == "upsert":
m = c["market"]
print(m["id"], [(o["name"], o["decimal"], o["fair"]) for o in m["outcomes"]])
except (OSError, websockets.ConnectionClosed) as e:
print("disconnected", e, "retry in", delay, "s")
await asyncio.sleep(delay + random.random())
delay = min(delay * 2, 30)
asyncio.run(main())
Fields on each outcome: decimal, american, fair (no-vig), fairProb. Market id is eventId:key. Full frame list: WebSocket docs. Schema: schema.
Trial CTA#
Start the 3-day Builder trial from pricing (card required; charged after day 3 unless you cancel), or grab a free REST key first. Related: Node reconnect answer, steam detection.
Questions people ask#
Why does my Free key get 403 on the WebSocket?#
Free and Hobby are REST-only. Opening /v1/stream returns 403 plan_lacks_ws. Upgrade to Builder for the push feed, or poll /v1/odds on Free (30 s delay) / Hobby (real time).
Do I have to answer pings?#
Yes. The server sends ping about every 20 seconds. Reply with {"type":"pong"} or the socket closes after ~60 s of silence.
Is this educational content for adults?#
Yes (18+). It is tooling documentation, not tips or a promise of profit.