# WS wss://api.sportsapi.app/?ticket={ticket}

Group: WebSocket

Connect to the url from the ticket. The server greets you with a welcome frame (protocol 4 since 2026-10-23; 3 earlier that day, 2 from 2026-10-19); then subscribe to channels. A subscription starts with a snapshot (or, after a lastSeq replay or a repeated subscribe, a subscribed frame), followed by frames carrying a per-channel seq that increases by one per frame — an opaque number (it starts from the server clock, not 0). On livescores, sport:{slug} and league:{id} a fixture that enters the channel’s set (it kicks off, or the server first sees it) arrives as a fixture frame with the whole Fixture; deltas then patch it, and only fixtures you were given are patched — a fixture that leaves (finished, canceled) gets one last delta saying so.

Plans: Business only (other plans get 403 ws_not_available on the ticket).

Note: Protocol 4: handle the fixture frame — a client that skips it misses every match that kicks off while it is connected. A delta’s changes are a partial Fixture: merge them key by key into what you hold (nested objects such as homeScore, clock and cricket innings merge too — they never replace the whole object). Every frame carries ts (server time, Unix ms). Close codes: 1008 invalid or expired ticket (mint a new one) — or, on an open connection, its API key was revoked, the account suspended or the plan changed (the reason says which; checked at once on those events and every minute), 1013 your plan’s concurrent-connection limit is reached. The server sends a protocol ping every heartbeatMs — connections that stop answering are dropped. A score that goes DOWN (a disallowed goal, a corrected basket) arrives up to 1.5 s after the source sends it (football: 5 s), together with its lowered period scores: the source sometimes re-sends an older score for a moment, and such flaps are dropped (since 2026-10-24).

## Example response

```json
← { "type": "welcome", "protocol": 4, "heartbeatMs": 30000, "maxChannels": 50, "replay": true }
→ { "type": "subscribe", "channel": "livescores" }
← { "type": "snapshot", "channel": "livescores", "seq": 1760800000000, "ts": 1760800000000, "data": [ Fixture, … ] }
← { "type": "fixture", "channel": "livescores", "seq": 1760800000001, "ts": 1760800002480,
    "id": 201330, "sport": "basketball", "data": { "id": 201330, "sport": "basketball",
    "status": { "type": "inprogress", "phase": "inprogress", "code": 13, "description": "1st quarter", "period": "period1", "finish": null }, … } }
← { "type": "delta", "channel": "livescores", "seq": 1760800000002, "ts": 1760800004120,
    "id": 201324, "sport": "football",
    "changes": { "awayScore": { "current": 1, "periods": { "period2": 1 } } } }
← { "type": "delta", "channel": "livescores", "seq": 1760800000003, "ts": 1760800009310,
    "id": 201324, "sport": "football",
    "changes": { "status": { "code": 100, "description": "Ended", "type": "finished", "phase": "finished",
                             "finish": "regular", "period": null },
                 "winner": "away", "clock": null } }

// Reconnect with the last seq you hold (Business): the missed frames, then subscribed
→ { "type": "subscribe", "channel": "livescores", "lastSeq": 1760800000003 }
← { "type": "subscribed", "channel": "livescores", "seq": 1760800000003, "ts": 1760800030000, "replayed": 0 }

// fixture:{id} — play by play appended from index start (since 2026-10-24)
← { "type": "playByPlay", "channel": "fixture:201330", "seq": 1760800040000, "ts": 1760800041900, "id": 201330, "sport": "basketball",
    "start": 117, "total": 118, "entries": [ { "type": "scoreChange", "side": "home", "period": "period3", "periodSecond": 401,
      "text": "Jump shot made", "homeScore": 69, "awayScore": 74, "scoreType": "twoPointer", … } ] }
← { "type": "shotmap", "channel": "fixture:201324", "seq": 1760800050000, "ts": 1760800050500, "id": 201324, "sport": "football",
    "shots": [ { "side": "away", "outcome": "goal", "minute": 37, "period": "period1", … }, … ] }

→ { "type": "subscribe", "channel": "fixture:999999999999" }
← { "type": "error", "code": "unknown_fixture", "channel": "fixture:999999999999", "message": "no fixture with that id", "ts": 1760800031000 }
→ { "type": "unsubscribe", "channel": "sport:tennis" }
← { "type": "error", "code": "not_subscribed", "channel": "sport:tennis", "message": "not subscribed to this channel", "ts": 1760800032000 }
```

---

Base URL: https://api.sportsapi.app. Send your API key as `Authorization: Bearer <key>` on every /v2 request; keys are created in the dashboard (https://sportsapi.app/dashboard).
HTML: https://sportsapi.app/docs/websocket/connect · All docs: https://sportsapi.app/markdown/en/docs.md
