---
title: "The broadcast object for every live match (ULTRA)"
method: GET
path: "/broadcast/live"
---

# The broadcast object for every live match (ULTRA)

`GET /broadcast/live`

## Request

- Base URL: `https://api.livetennisapi.com/api/public/v1`
- URL: `https://api.livetennisapi.com/api/public/v1/broadcast/live`
- Auth: one of:
  - HTTP bearer
  - API key in header `X-API-Key`

The same object as `GET /broadcast/match/{matchId}`, for every actively-live match, in one call. A ticker or a multi-match wall polls this instead of one URL per match, and it stays ONE request however many matches are live. At most 200 objects.

Same 1 Hz contract, same `ETag` and `Cache-Control`, same display-form fields, same every-key-present rule. An empty `data` means nothing is live right now and is the ordinary overnight answer, not a fault.

## Query parameters

- `tour` string

## Response `200`

Every live match as a broadcast object

- object
  - `data` BroadcastObject[]
    - `match_id` integer
    - `status` string: `live`, `completed`, `upcoming`
    - `event_status` string, nullable: The terminal badge where one is stated (`Finished`, `Retired`, `Walk Over`, `Interrupted`); null on a normal live row.
    - `outcome` string, nullable
    - `winner` integer, nullable: 1 or 2, null until a result is established.
    - `tour` string, nullable
    - `tournament` string, nullable
    - `round` string, nullable
    - `surface` string, nullable
    - `best_of` integer, nullable
    - `is_doubles` boolean
    - `p1_id` integer, nullable
    - `p1_name` string, nullable
    - `p1_short` string, nullable: Display short form, `"J. Sinner"`. A doubles team keeps each side's last token.
    - `p1_country` string, nullable: Three-letter code.
    - `p1_ranking` integer, nullable
    - `p2_id` integer, nullable
    - `p2_name` string, nullable
    - `p2_short` string, nullable
    - `p2_country` string, nullable
    - `p2_ranking` integer, nullable
    - `p1_sets` integer, nullable
    - `p2_sets` integer, nullable
    - `p1_games` integer, nullable: Games in the set in play.
    - `p2_games` integer, nullable
    - `p1_points` string, nullable: As printed: `"0"`, `"15"`, `"30"`, `"40"`, `"AD"`, or the tiebreak count.
    - `p2_points` string, nullable
    - `p1_set1` integer, nullable: Per-set columns. A set not yet played is null on both sides.
    - `p2_set1` integer, nullable
    - `p1_set2` integer, nullable
    - `p2_set2` integer, nullable
    - `p1_set3` integer, nullable
    - `p2_set3` integer, nullable
    - `p1_set4` integer, nullable
    - `p2_set4` integer, nullable
    - `p1_set5` integer, nullable
    - `p2_set5` integer, nullable
    - `set_line` string, nullable: The completed and in-play sets as one string, `"5-7 2-0"`.
    - `current_set` integer, nullable
    - `is_tiebreak` boolean
    - `match_tiebreak` boolean: True while a deciding match tiebreak is in play.
    - `server` integer, nullable: 1 or 2.
    - `p1_serving` boolean, nullable
    - `p2_serving` boolean, nullable
    - `break_point_for` integer, nullable: 1, 2 or null. Cue an animation on the transition to non-null.
    - `set_point_for` integer, nullable
    - `match_point_for` integer, nullable
    - `p1_win_pct` integer, nullable: The model probability as a whole percent. Null where no model figure was computed for this state — never guessed.
    - `p2_win_pct` integer, nullable
    - `sequence` integer, nullable: The per-match accept counter. THIS is how a poller detects a new state.
    - `updated_at` string, date-time, nullable
    - `age_seconds` integer, nullable: Seconds since the score CONTENT last changed.
    - `stale` boolean, nullable: A live match whose content has frozen past the threshold.
    - `served_at` string, date-time: When this response was built. Changes on every read — do not use it to detect a new state.
  - `count` integer
  - `served_at` string, date-time

## Other responses

- `304`: Unchanged since the `ETag` you sent
- `401`: Missing, unknown, or disabled credentials
- `403`: Your tier doesn't unlock this endpoint
- `429`: Rate limit exceeded (Retry-After header present). Three body shapes, told apart by `error` and `scope`: the per-MINUTE limit (`rate_limited`, with `upgrade_url`, `tier` and `price` naming the next tier up); the per-DAY quota (`rate_limited` with `scope: "day"`, `limit_per_day`, and `resets_at` — the absolute ISO instant the daily window resets); and `abuse_throttled` with `retry_at_epoch` — a 24-hour block applied to clients that keep hammering far past their cap, which a well-behaved retry loop never sees. Fix the loop rather than retrying through it.

---

[API](https://skmtc.dev/livetennisapi/apis/live-tennis-api.md) · [All operations](https://skmtc.dev/livetennisapi/apis/live-tennis-api/llms.txt) · [OpenAPI document](https://skmtc.dev/livetennisapi/apis/live-tennis-api/revisions/7881947e8e43?raw)
