Subscribe with filters
Send {"type":"subscribe","sports":["soccer"],"live":true,"markets":["moneyline","total"]} and the server filters before sending. Change the subscription at any time without reconnecting.
Stop polling. One authenticated WebSocket delivers a snapshot of everything you subscribed to, then only the markets that changed — with optional permessage-deflate compression.
Send {"type":"subscribe","sports":["soccer"],"live":true,"markets":["moneyline","total"]} and the server filters before sending. Change the subscription at any time without reconnecting.
After a chunked snapshot, every message is a delta batch: market upserts with full outcome prices and explicit removals when a market is suspended, settled or the event ends.
Heartbeats every 20 seconds, documented close codes, a sequence number per batch, per-key connection limits and back-pressure protection. Reconnect and resubscribe to resync.
It is a plain WebSocket with JSON frames. Our docs include working clients in Python and Node.js; anything that speaks WebSocket works.
# pip install websockets
import asyncio, json, os, websockets
async def main():
url = "wss://pinnfeed.io/v1/stream"
async with websockets.connect(url, additional_headers={"x-api-key": os.environ["PINNSOCKET_KEY"]}) as ws:
await ws.send(json.dumps({"type": "subscribe", "sports": ["soccer"], "live": True}))
async for raw in ws:
msg = json.loads(raw)
if msg["type"] == "ping": await ws.send('{"type":"pong"}')
elif msg["type"] == "delta":
for c in msg["changes"]:
if c["kind"] == "market" and c["op"] == "upsert":
m = c["market"]; print(m["key"], [(o["name"], o["decimal"], o["fair"]) for o in m["outcomes"]])
asyncio.run(main())// npm install ws
import WebSocket from 'ws';
const ws = new WebSocket('wss://pinnfeed.io/v1/stream', {
headers: { 'x-api-key': process.env.PINNSOCKET_KEY }, perMessageDeflate: true,
});
ws.on('open', () => ws.send(JSON.stringify({ type: 'subscribe', sports: ['tennis', 'basketball'] })));
ws.on('message', (buf) => {
const msg = JSON.parse(buf);
if (msg.type === 'ping') return ws.send('{"type":"pong"}');
if (msg.type === 'delta') for (const c of msg.changes) console.log(c.kind, c.op, c.market?.key ?? c.event?.home);
});# REST snapshot: every live soccer event with main markets
curl -H "x-api-key: $PINNSOCKET_KEY" \
"https://pinnfeed.io/v1/odds?sport=soccer&live=true&main=true"
# Closing lines for yesterday (Builder and above)
curl -H "x-api-key: $PINNSOCKET_KEY" \
"https://pinnfeed.io/v1/closing?date=2026-11-08&sport=tennis"{
"type": "delta", "seq": 48213, "ts": 1762679412093,
"changes": [{
"kind": "market", "op": "upsert",
"market": {
"id": "1640278226:s;0;m", "eventId": 1640278226,
"type": "moneyline", "period": 0, "main": true, "live": true,
"margin": 0.0357, "maxStake": 1500,
"outcomes": [
{ "name": "home", "american": -128, "decimal": 1.781, "fair": 1.845, "fairProb": 0.5421 },
{ "name": "draw", "american": 262, "decimal": 3.62, "fair": 3.750, "fairProb": 0.2667 },
{ "name": "away", "american": 405, "decimal": 5.05, "fair": 5.230, "fairProb": 0.1912 }
]
}
}]
}Yes. Clients that negotiate permessage-deflate receive compressed frames, typically 5–10× smaller.
Hobby 1, Builder 3, Pro 10, Enterprise custom — per account, across all keys.
Yes. Every plan includes REST snapshot endpoints with the same schema.
Delayed REST data free forever. Real-time WebSocket from $29/mo.