Every odds change, the moment it lands. One socket.
The Stream API sends each board once, then once a second only the markets and selections that moved. Your copy stays exact, your bandwidth drops to a few kilobytes a second, and one connection carries every bookmaker and sport your plan allows, live and prematch.
53 bookmakers and 47 sports are streamable right now.
- 218 B{"t":"connected","plan":"max","protocol":2,"streams":["bet365/live/soccer","onexbet/live/soccer"]}
- 1.1 MB{"t":"snapshot","stream":"bet365/live/soccer","part":1,"of":1,"events":[ …22 events, every market… ]}
- 84 B{"t":"snapshot_end","stream":"bet365/live/soccer","count":22}
- 196 B{"t":"delta","stream":"bet365/live/soccer","changes":[{"eventId":"181920144","op":"patch","sel":[{"m":"1x2","k":"home","odds":2.10}]}]}
- 171 B{"t":"delta","stream":"onexbet/live/soccer","changes":[{"eventId":"57120388","op":"patch","set":{"score":{"home":1,"away":0}}}]}
- 188 B{"t":"delta","stream":"bet365/live/soccer","changes":[{"eventId":"181920144","op":"patch","mSet":[{"k":"totals_2.5","isActive":false}]}]}
- 112 B{"t":"delta","stream":"onexbet/live/soccer","changes":[{"eventId":"57119902","op":"remove","reason":"expired"}]}
- 6.4 KB{"t":"delta","stream":"bet365/live/soccer","changes":[{"eventId":"181920901","op":"upsert","event":{ …new match… }}]}
- one snapshot, then a few hundred bytes per second, and the board stays exact
How a stream works, start to finish
Declare once
Open the socket with your key and the bookmakers, sports and kinds you want, or send one JSON message with per-stream league, date and market filters. The server checks it against your plan before the upgrade.
wss://api.pulsescore.net/api/stream/ws?key=…&bookmakers=bet365,onexbet&sports=soccer&kinds=live,prematchGet the board
Each stream sends its full board in ~1 MB pages, exactly the documents the REST API serves. Big boards stream in the background while other streams already deliver deltas.
{"t":"snapshot","stream":"bet365/prematch/soccer","part":1,"of":224,"events":[…]}Apply the deltas
Once a second per stream: only the events, markets and selections that changed, keyed by id so array order never matters. Every value is absolute, so applying a frame twice is harmless.
{"t":"delta","stream":"bet365/live/soccer","seq":…,"changes":[{"eventId":"…","op":"patch","sel":[…]}]}Resume, never restart
Drop the socket, reconnect with the last seq per stream, and you get resumed plus the frames you missed — including across our own deploys, measured at under a second.
{"streams":[{"bookmaker":"bet365","kind":"live","sport":"soccer","since":7685713542330188070}]}
What changes when you stream instead of poll
Bandwidth
Polling refetches the whole board to notice one moved price. A stream sends that board once and afterwards only the selections that changed, compressed once for everyone on the wire — six to nine times smaller than the same frames plain. Soccer and tennis on every bookmaker ran at about 0.7 MB per second on the wire in our tests; a single live board is a few kilobytes.
Exactness
Changes are keyed by market and selection identity, not by position, and every value is absolute. Your copy of a board is byte-for-byte what the REST API would return at the same moment; we verify it that way before every release. Changes that happen while your snapshot pages are still arriving are folded into one catch-up delta, so a slow reader misses nothing.
Continuity
Every frame carries a sequence number that grows across our servers. Reconnect with it and the stream resumes where it stopped for up to two minutes. When we deploy, the new process takes the live state over before the old one lets go: you see one close, a sub-second reconnect and a resumed frame, never a fresh snapshot.
Included in PRO, MAX and ULTRA
Limits are counted per account across all your stream connections; live and prematch of one sport count once, and every bookmaker is included on every plan. The table reads the live values from the service.
Sockets are budgeted per bookmaker across both WebSocket products: PRO 1, MAX 3, ULTRA 6 per bookmaker, shared between the legacy live feed and streams. A stream connection uses one socket on each bookmaker it declares.
Compare plans and prices| Plan | Stream connections | Distinct sports | Bookmakers |
|---|---|---|---|
| PRO | 1 | 1 | all |
| MAX | 2 | 3 | all |
| ULTRA | 3 | all | all |
A declaration over your limits is refused before the upgrade with a message that names the total it would reach.
Ten lines to a live board
Two dependency-free files apply the deltas, route frames to one board per stream and keep the socket alive — they reconnect with the last sequence number, honour the server's retry hints and stop on the codes a retry cannot fix. Download apply.js or apply.py, or write your own from the protocol document.
import { StreamConnection } from "./apply.js"; // pulsescore.net/stream-clients/apply.js
const conn = new StreamConnection({
url: "wss://api.pulsescore.net/api/stream/ws",
key: "YOUR_API_KEY",
streams: [
{ bookmaker: "bet365", kind: "live", sport: "soccer" },
{ bookmaker: "bet365", kind: "prematch", sport: "soccer", markets: ["MATCH_RESULT"] },
],
onFrame: ({ stream, type }, client) => {
if (type === "delta" || type === "snapshot_end") render(stream, client.board(stream).toArray());
},
onState: (state, info) => console.log(state, info), // connecting | open | reconnecting | closed
});
conn.connect();import asyncio
from apply import StreamConnection # pulsescore.net/stream-clients/apply.py, pip install websockets
conn = StreamConnection(
"wss://api.pulsescore.net/api/stream/ws", key="YOUR_API_KEY",
streams=[{"bookmaker": "bet365", "kind": "live", "sport": "soccer"}],
on_frame=lambda stream, kind, client, raw: kind == "delta" and render(stream, client.board(stream).to_list()),
on_state=lambda state, info: print(state, info),
)
asyncio.run(conn.run())What beta means here
The protocol is at version 2 and we treat its frame shapes, keys and close codes as a contract: a change that would break your applier means a new version, not a silent edit. The service has been carrying real customers since mid-September and passed its correctness, load and deploy gates.
What may still move during the beta: the plan limits above, the size and timing limits, and which sports a bookmaker exposes. We may also add frames and fields — your applier should ignore what it does not know. Anything that changes is announced in the docs first.
Found a board that differs from the REST API, a close you did not expect, or something the docs do not answer? Tell us from your dashboard; that feedback is what the beta is for.
Open a socket tonight
Any PRO, MAX or ULTRA key streams today. Get one from the dashboard, declare the bookmakers and sports you care about, and watch the deltas arrive.