Mint a connection token for the high-fan-out push feed (ULTRA)
Returns a short-lived signed token plus the push WebSocket URL and the channel vocabulary: match:{match_id} per-match streams and slate:all for every live score frame. Frames are the same allowlist score objects the polling endpoints return. This is a separate surface from the native /ws feed described above — same ULTRA gate, built for high fan-out (no shared connection ceiling), and the recommended home for continuous/production streaming.
The endpoint speaks the Centrifugo client protocol (v2, JSON). Easiest path: the official Python (livetennisapi ≥ 1.4.0) and JS (≥ 1.5.0) SDKs ship a built-in PushStream client — no extra dependency. Raw protocol, if you prefer your own client: (1) open a WebSocket to ws_url; (2) send {"connect": {"token": "<token>"}, "id": 1} — the token goes INSIDE this JSON frame, never as a raw first message; (3) subscribe per channel with {"subscribe": {"channel": "slate:all"}, "id": 2}; (4) publications arrive as {"push": {"channel": ..., "pub": {"data": <frame>}}}; (5) the server's heartbeat is an empty JSON object {} — reply with {} promptly or you will be disconnected. Messages may batch several newline-delimited JSON objects. Tokens are short-lived and the connection closes around token expiry: mint a fresh token on EVERY reconnect and re-subscribe.
The channels object lists only channels that will actually deliver for your key right now (a channel name in this response is a promise). Where enabled server-side, additional channel families appear: point:match:{match_id} / point:slate (per-point events), listed — as point_match / point_slate in the vocabulary — only for keys whose plan carries the point surface, and signal:match:{match_id} / signal:slate (derived break_point, break_point_result and divergence events, and since 2026-09-12 the stoppage family: medical_timeout_start/end, trainer_called/_end, toilet_break_start/end, stoppage_start/end, pause_start/end). A family absent from the response will not deliver for your key right now. Deliberately separate channels: a slate:all subscriber asked for score states and never starts receiving events unasked. Point and signal frames are events, not states — a missed point does NOT self-correct on the next frame; recover it via GET /matches/{matchId}/points?after_seq= and dedup by seq.
WITHDRAWN STATES, AND HOW TO GET ONE BACK. This feed publishes every state we accept and runs no trust deference, while GET /matches/{matchId}/score runs the legality gate and can serve an older state than the newest row. So a state you receive here can later be one we do not stand behind, and when that happens a score_withdrawn frame follows the state's own score frame on the same channel. Eight keys, and no replacement score:
"superseded_sequence": 118, "reason": "illegal_completed_set",
"safe_to_resume": true, "ts": "2026-09-25T10:00:03.108Z",
"published_at": "2026-09-25T10:00:03.115Z" }
superseded_sequence is the sequence you hold and are to discard. The state to resume from is the one on the next score frame, or on a fresh GET /matches/{matchId}/score. kind is withdrawn (discard), deferred (the refused state may be early and is pending the next accept, so hold it) or withheld (nothing publishable, games is null). ts is when the judgement was made, published_at when this feed was handed the frame. A given (match, sequence) is withdrawn at most once per publishing process and there is no replay.
A consumer that was disconnected recovers the judgement from the durable record, added 2026-09-25: GET /matches?withdrawn_since=<instant> for the matches that had one, GET /matches/{matchId}/withdrawals for the whole record on a match (each row carrying the frame exactly as it went out, and the sequence served in the refused state's place), and GET /matches/{matchId}/score?sequence=N to ask about the one sequence you hold. Records are kept 90 days. The live verdict on the score read is the same judgement on demand, but it is recomputed over the newest state, so it stops answering for N as soon as the next state is accepted. The record does not.
Request
- Base URL: https://api.livetennisapi.com/api/public/v1
- URL: https://api.livetennisapi.com/api/public/v1/ws-token
- Auth: one of:
- HTTP bearer
- API key in header X-API-Key
Response
Connection token, push URL and channel vocabulary
Example response
{
"token": "eyJhbGciOi...",
"expires_in": 3600,
"ws_url": "wss://api.livetennisapi.com/connection/websocket",
"channels": {
"match": "match:{match_id}",
"slate": "slate:all"
}
}Changes
No recorded changes to this operation across all 1 revision of this API.