Tennis. Point-by-point.
Subscribe to any live match and receive a score frame on every point — sets, games, point score and serve indicator. Same token as the REST API.
"sport":"tennis" in every subscribe and unsubscribe — without it the server treats the event_id as football.
Connect
Append your API token as a query parameter. After the connection opens, send subscribe messages. You can mix tennis and football subscriptions on the same socket.
wss://sports.bzzoiro.com/ws/live/?token=YOUR_TOKEN
Subscribe / Unsubscribe / Ping
Send these JSON messages over the open socket.
// subscribe — sport:"tennis" is required { "action": "subscribe", "event_id": 36835, "sport": "tennis" } // unsubscribe { "action": "unsubscribe", "event_id": 36835, "sport": "tennis" } // keepalive — send if socket may sit idle >60s { "action": "ping" }
Ping / Pong
The server does not send unsolicited pings. Send a ping when your socket may be idle for more than ~60s. The server replies immediately:
// you send: { "action": "ping" } // server replies: { "type": "pong" }
score frames on every point — ping is only needed when no matches are live.Find live match IDs at /tennis/api/v2/matches/?status=live.
subscribed
Sent immediately after a successful subscribe. Contains the latest event snapshot so you can render a complete scoreboard without waiting for the next tick.
{
"type": "subscribed",
"event_id": 36835,
"sport": "tennis",
"event": { /* latest event frame */ },
"livedata": [ /* most recent score frames */ ],
"history": []
}
event
Full match snapshot. Sent every ~30s and on status changes. Contains players, sets, stats and tournament.
{
"type": "event",
"event_id": 36835,
"sport": "tennis",
"home": { "id": 1, "name": "Djokovic, N." },
"away": { "id": 2, "name": "Alcaraz, C." },
"score": {
"sets": [[6,3],[4,6],[2,1]],
"home_sets": 1, "away_sets": 1,
"home_games": 2, "away_games": 1,
"point": "40-15", "server": "home", "set": 3
},
"status": { "name": "live" },
"stats": {
"home": { "aces": 8, "double_faults": 2, "first_serve_pct": 63.4, "winners": 22, "unforced_errors": 14 },
"away": { "...": "same shape" }
},
"tournament": { "name": "Roland Garros", "surface": "Clay" }
}
score
Pushed on every point — typically every few seconds during active play. Use this to update your scoreboard in real time.
{
"type": "score",
"event_id": 36835,
"uts": 1780516577, // unix timestamp (seconds)
"sets": [[6,3],[4,6],[2,1]],
"set": 3, // current set (1-indexed)
"game": "2-1",
"point": "40-15",
"server": "home" // "home" | "away" | null
}
| Field | Type | Description |
|---|---|---|
| sets | array | [[home, away], ...] — one entry per completed or current set |
| set | int | Current set number (1-indexed) |
| game | string | Games in current set, e.g. "2-1" |
| point | string | 0 / 15 / 30 / 40 / Deuce / Ad |
| server | string|null | "home", "away", or null |
| uts | int | Unix timestamp in seconds |
Error codes
| code | Description |
|---|---|
| auth_failed | Token missing, invalid or expired |
| subscription_required | Token valid but WebSocket addon not active |
| not_tracked | Match exists but no live data available right now |
| limit | 10 concurrent match limit reached; unsubscribe first |
| bad_action | Unknown action value |
| bad_event_id | event_id not found |
| bad_sport | Unrecognised sport value (always pass "tennis") |
Limits
- 10 concurrent matches per socket. Mix tennis and football on the same connection.
scorepushed on every point — typically every few secondseventsnapshot every ~30s when something changes- Latest state replayed on subscribe
Code examples
JavaScript
const ws = new WebSocket("wss://sports.bzzoiro.com/ws/live/?token=YOUR_TOKEN"); ws.onopen = () => { ws.send(JSON.stringify({ action:"subscribe", event_id:36835, sport:"tennis" })); }; ws.onmessage = ({ data }) => { const f = JSON.parse(data); switch (f.type) { case "subscribed": if (f.event) applyEvent(f.event); break; case "score": updateBoard(f.sets, f.game, f.point, f.server); break; case "event": if (f.sport === "tennis") applyEvent(f); break; case "pong": break; // keepalive confirmed case "error": console.error(f.code, f.message); break; } }; // keepalive every 45s when idle setInterval(() => ws.readyState === WebSocket.OPEN && ws.send('{"action":"ping"}'), 45000);
Python (asyncio)
import asyncio, json, websockets async def main(): url = "wss://sports.bzzoiro.com/ws/live/?token=YOUR_TOKEN" async with websockets.connect(url) as ws: await ws.send(json.dumps({"action":"subscribe","event_id":36835,"sport":"tennis"})) async for msg in ws: f = json.loads(msg) if f["type"] == "score": sets = " | ".join(f'{s[0]}-{s[1]}' for s in f["sets"]) print(f'Sets: {sets} Game: {f["game"]} Pt: {f["point"]} Srv: {f["server"]}') asyncio.run(main())
Try it — connect to a live match
Connects to wss://sports.bzzoiro.com/ws/live/ directly from your browser.
Get access — $3.00/month
Football + Tennis included · 10 concurrent matches · auto-renewing