diff --git a/CHANGELOG.md b/CHANGELOG.md index 925d8aa..db78618 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to the specification are recorded here. The API surface is versioned as `v1`. Changes within `v1` are **additive only**; removing a field or changing its type would require `v2`. +## [1.13.77] - 2026-10-29 + +### Fixed +- **`/fixtures` and `/matches` tell one story.** A fixture bound to a match (`match_id` set) is served with the match's `status`, in the fixture vocabulary, and the match's `start_time`, read at request time. The match row moves within minutes of a schedule change; the fixture row followed the 6-hourly refresher, so the two endpoints could disagree on the same match for hours. A customer found a Vienna match `live` at 09:00Z on one and `upcoming` at 12:30Z on the other. A vendor-stated `cancelled` or `postponed` on the fixture is kept. A null `is_qualifying` on the fixture takes the match's value when the match states one. +- **A bare digit in the schedule's status field is no longer a match in play.** The day-ahead listing carries `1` on matches that have not started. It was read as a set in play, so 298 fixtures for 29 October were published as `live`. The digit is `live` only when the schedule's own in-play flag says so. The stored rows were restated the same day. + ## [1.13.76] - 2026-10-28 ### Added diff --git a/docs/auth-quota-and-health.html b/docs/auth-quota-and-health.html index cbebf30..8731ef1 100644 --- a/docs/auth-quota-and-health.html +++ b/docs/auth-quota-and-health.html @@ -202,7 +202,7 @@
Live Tennis API DocsGet a free key
Explore the documentation
-

Live Tennis API · docs · version 1.13.76

+

Live Tennis API · docs · version 1.13.77

Tennis API authentication, quota and status

How do you authenticate, and how do you see what quota is left? Send your key as X-API-Key on every request. GET /usage returns your own consumption against your plan's quota and works on every tier including FREE, so a client can check its own headroom before a burst. GET /health is the unauthenticated liveness probe.

2 endpoints on this page. Base URL diff --git a/docs/broadcast-graphics.html b/docs/broadcast-graphics.html index 5d861ca..178c4df 100644 --- a/docs/broadcast-graphics.html +++ b/docs/broadcast-graphics.html @@ -202,7 +202,7 @@

Live Tennis API DocsGet a free key
Explore the documentation
-

Live Tennis API · docs · version 1.13.76

+

Live Tennis API · docs · version 1.13.77

Tennis scores for on-air graphics

How do you put live tennis scores into a broadcast graphics template? GET /broadcast/match/{matchId} returns ONE flat object for one match and GET /broadcast/live the same object for every live match in a single call. Both are ULTRA. A graphics template binds each field to a fixed path, so three things are contractual: the object is flat, every key is present on every read (null, never missing), and the score is already in display form — p1_points reads "40" or "AD" and set_line is one string. Built for 1 Hz: every response carries a strong ETag, so If-None-Match makes an unchanged state a 304 with no body. Detect a new state on sequence, never on served_at, which changes on every read.

2 endpoints on this page. Base URL diff --git a/docs/changelog.html b/docs/changelog.html index cb5bf38..16cfd75 100644 --- a/docs/changelog.html +++ b/docs/changelog.html @@ -4,7 +4,7 @@ Live Tennis API — Changelog - + @@ -14,7 +14,7 @@ @@ -204,8 +204,14 @@

Live Tennis API · docs

Changelog

-

Every change to the specification, dated and versioned. The API surface is v1; changes within it are additive only. Current version 1.13.76, last change . Also as the full reference, OpenAPI YAML / JSON, and the dated facts file.

+

Every change to the specification, dated and versioned. The API surface is v1; changes within it are additive only. Current version 1.13.77, last change . Also as the full reference, OpenAPI YAML / JSON, and the dated facts file.

+

1.13.77

+

Fixed

+
    +
  • /fixtures and /matches tell one story. A fixture bound to a match (match_id set) is served with the match's status, in the fixture vocabulary, and the match's start_time, read at request time. The match row moves within minutes of a schedule change; the fixture row followed the 6-hourly refresher, so the two endpoints could disagree on the same match for hours. A customer found a Vienna match live at 09:00Z on one and upcoming at 12:30Z on the other. A vendor-stated cancelled or postponed on the fixture is kept. A null is_qualifying on the fixture takes the match's value when the match states one.
  • +
  • A bare digit in the schedule's status field is no longer a match in play. The day-ahead listing carries 1 on matches that have not started. It was read as a set in play, so 298 fixtures for 29 October were published as live. The digit is live only when the schedule's own in-play flag says so. The stored rows were restated the same day.
  • +

1.13.76

Added

    diff --git a/docs/historical-results-archive.html b/docs/historical-results-archive.html index 89ac62c..1cecdcb 100644 --- a/docs/historical-results-archive.html +++ b/docs/historical-results-archive.html @@ -202,7 +202,7 @@
    Live Tennis API DocsGet a free key
    Explore the documentation
    -

    Live Tennis API · docs · version 1.13.76

    +

    Live Tennis API · docs · version 1.13.77

    Historical tennis results API — 1968 onward

    How far back does the historical tennis data go, and what is in it? The results archive covers 1968 to 2022: GET /history/archive/matches for results, /archive/players for bios, /archive/career for career aggregates and GET /h2h for head-to-head across both the archive and our own completed matches. All BASIC. Matches from 2013 onward additionally carry a reconstructed point-by-point tape at /archive/matches/{archiveId}/tape (ULTRA).

    6 endpoints on this page. Base URL diff --git a/docs/live-scores.html b/docs/live-scores.html index 3ddcecd..b6eb6fa 100644 --- a/docs/live-scores.html +++ b/docs/live-scores.html @@ -202,7 +202,7 @@

    Live Tennis API DocsGet a free key
    Explore the documentation
    -

    Live Tennis API · docs · version 1.13.76

    +

    Live Tennis API · docs · version 1.13.77

    Live tennis scores API

    How do you read live tennis scores from the API? Poll GET /matches?status=live for the slate, then GET /matches/{matchId}/score for the lowest-latency read of one match. Both are on the FREE plan. Scores carry sets, games, points and the server; /events and /points give the same match as a stream of changes rather than a snapshot, so a client can catch up without re-reading state.

    11 endpoints on this page. Base URL @@ -414,6 +414,7 @@

    Example

    curl https://api.livetennisapi.com/api/public/v1/matc
     

    GET /fixtures

    Upcoming scheduled fixtures, earliest first (FREE)

    Plan required: FREE · operationId: listFixtures

    +

    One row per schedule entry, keyed on the schedule's own event key. A fixture bound to a match (match_id set) is served with the MATCH's status (mapped onto the fixture vocabulary: upcoming is scheduled, completed is finished) and start_time, read at request time (2026-10-29). The match row is the fresher record: the ingest moves it within minutes of the schedule changing, while the fixture row follows the 6-hourly refresher. A fixture whose own status is cancelled or postponed keeps it, since its reason carries the stated word. A fixture whose is_qualifying is null takes the match's value when the match states one; null is never turned into false without a source. A fixture with no match_id is served as stored. Before this, /fixtures and /matches could disagree on the same match for up to six hours.

    Parameters

    GET /fixtures — parameters
    NameInTypeRequiredNotes
    tourquerystring (atp, wta, challenger, itf, juniors)noRestrict results to one tour. Each value covers its singles and doubles draws, so atp includes ATP doubles and juniors covers the boys' and girls' Grand Slam draws. Omit for all tours. An unrecognised value is a 400 rather than a silent pass-through, so a caller never receives a tour it did not ask for. Applied before pagination, so meta.count reflects the filtered set.
    drawquerystring (singles, doubles)noDraw filter (added 2026-08-18) — the axis the tour filter deliberately collapses; the two compose (?tour=itf&draw=doubles is the ITF doubles slice). Same vocabulary as the draw field on Match, decided by the same shared definition, so filter and field cannot disagree. A row whose draw is null — a team tie, or no stated event type and no doubles-team participant — matches NEITHER value: null is an answer, not a wildcard. Two honesty notes: on /tournaments the answer comes from the event type alone (a tournament row has no participants to supply the doubles-team evidence matches have), and draw=doubles alone also returns mixed and exhibition doubles that no tour value reaches. An unknown value is a 400 bad_draw with the allowed values.
    is_qualifyingquerybooleannoReturn only matches in the QUALIFYING draw (true) or only those the source states are not (false), matching the is_qualifying flag each row carries (field added 2026-08-12, filter added 2026-10-02). Omit for both. Anything other than true/false is a 400 bad_is_qualifying, never a silently ignored filter. THIS IS THE ONLY WAY TO SPLIT THE TWO DRAWS, and the reason is in the is_qualifying field description: the two are routinely carried under ONE tournament name with ONE round vocabulary, so tournament, tournament_id, round and round_code all read main-draw for a qualifying match. Do not filter on the round text. THREE-VALUED, so this filter is too: a row whose flag is null (no source has ever stated it) matches NEITHER value and is returned only when the filter is omitted. null is an answer, not a false — returning it under is_qualifying=false would assert "main draw" on the strength of nothing. Measured 2026-10-02 over completed matches: the flag is stated on 98.9% of the last 30 days and 98.3% of 2026 outside the UTR circuit, where it is mostly unstated (1,695 of the 1,831 unstated rows in the last 30 days are UTR).
    genderquerystring (men, women)noReturn only men's or only women's matches, matching the gender field each row carries (added 2026-09-09, documented here 2026-10-02). Omit for both. Anything other than men/women is a 400 bad_gender, never a silently ignored filter. diff --git a/docs/openapi.json b/docs/openapi.json index e301981..eb1af03 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -2,7 +2,7 @@ "openapi": "3.1.0", "info": { "title": "Live Tennis API", - "version": "1.13.76", + "version": "1.13.77", "contact": { "name": "Live Tennis API", "url": "https://livetennisapi.com" @@ -2450,6 +2450,7 @@ "/fixtures": { "get": { "summary": "Upcoming scheduled fixtures, earliest first (FREE)", + "description": "One row per schedule entry, keyed on the schedule's own event key. A fixture bound to a match (`match_id` set) is served with the MATCH's `status` (mapped onto the fixture vocabulary: `upcoming` is `scheduled`, `completed` is `finished`) and `start_time`, read at request time (2026-10-29). The match row is the fresher record: the ingest moves it within minutes of the schedule changing, while the fixture row follows the 6-hourly refresher. A fixture whose own `status` is `cancelled` or `postponed` keeps it, since its `reason` carries the stated word. A fixture whose `is_qualifying` is null takes the match's value when the match states one; null is never turned into false without a source. A fixture with no `match_id` is served as stored. Before this, `/fixtures` and `/matches` could disagree on the same match for up to six hours.", "operationId": "listFixtures", "parameters": [ { @@ -6502,7 +6503,7 @@ "null" ], "format": "date-time", - "description": "Scheduled start (UTC). Null until the order of play assigns a time — a date-only fixture is a real state, and rows written before 2026-08-03 stay null." + "description": "Scheduled start (UTC). Null until the order of play assigns a time — a date-only fixture is a real state, and rows written before 2026-08-03 stay null. On a fixture bound to a match this is the match's `scheduled_time` (2026-10-29)." }, "player1_id": { "type": [ @@ -6584,7 +6585,16 @@ "type": [ "string", "null" - ] + ], + "enum": [ + "scheduled", + "live", + "finished", + "cancelled", + "postponed", + null + ], + "description": "A closed set. On a fixture bound to a match it is the match's status in this vocabulary (2026-10-29); a vendor-stated `cancelled` or `postponed` on the fixture itself is kept. A bare digit in the schedule's status field is `live` only when the schedule's own in-play flag says so; before 2026-10-29 it read as a set in play on matches that had not started." } } }, diff --git a/docs/players-and-tournaments.html b/docs/players-and-tournaments.html index 3be4a3b..d9c4329 100644 --- a/docs/players-and-tournaments.html +++ b/docs/players-and-tournaments.html @@ -202,7 +202,7 @@
    Live Tennis API DocsGet a free key
    Explore the documentation
    -

    Live Tennis API · docs · version 1.13.76

    +

    Live Tennis API · docs · version 1.13.77

    Tennis players, tournaments and rankings API

    How do you look up a player, a tournament or a ranking? GET /players searches by name and GET /players/{playerId} returns one player's bio, current ranking and cached statistics — both FREE. GET /players/{playerId}/stats (PRO) is the player's win/loss record over our completed matches, split by surface, level and season, with ULTRA adding summed serve and return statistics. GET /tournaments is the catalogue that Match.tournament_id joins against, so a match can be resolved to its event without a second lookup. GET /rankings serves rank-ordered listings on PRO. The per-player as-of records, including the Elo tape, are ULTRA.

    8 endpoints on this page. Base URL diff --git a/docs/point-by-point-history.html b/docs/point-by-point-history.html index fd0cf8d..4947905 100644 --- a/docs/point-by-point-history.html +++ b/docs/point-by-point-history.html @@ -202,7 +202,7 @@

    Live Tennis API DocsGet a free key
    Explore the documentation
    -

    Live Tennis API · docs · version 1.13.76

    +

    Live Tennis API · docs · version 1.13.77

    Point-by-point tennis data API

    Which endpoints return point-by-point tennis data, and how complete is it? GET /history/matches/{matchId} returns the tape for one completed match: the ordered score states we hold for it. The model's win probability sits on the rows that were observed live, and coverage is declared per match. GET /history/matches lists completed matches with their tape coverage, and GET /history/coverage is the measured completeness rollup per tour and draw bucket — a number we publish rather than estimate, with its own as_of. GET /history/packages serves the same data as pre-built monthly bulk files.

    8 endpoints on this page. Base URL diff --git a/docs/push-feed-and-webhooks.html b/docs/push-feed-and-webhooks.html index efbdc72..d4ef447 100644 --- a/docs/push-feed-and-webhooks.html +++ b/docs/push-feed-and-webhooks.html @@ -202,7 +202,7 @@

    Live Tennis API DocsGet a free key
    Explore the documentation
    -

    Live Tennis API · docs · version 1.13.76

    +

    Live Tennis API · docs · version 1.13.77

    Tennis WebSocket feed and webhooks

    How do you receive tennis data as it happens instead of polling? Two ways, both ULTRA. GET /ws-token mints a short-lived token for the high-fan-out push feed, which streams score and point changes as they are written. POST /webhooks registers an outbound HTTP callback for the same events; GET /webhooks lists your registrations and never returns the signing secret. Webhooks require a direct key, not a marketplace one.

    4 endpoints on this page. Base URL diff --git a/docs/reference.html b/docs/reference.html index d4abd7d..829dd04 100644 --- a/docs/reference.html +++ b/docs/reference.html @@ -207,7 +207,7 @@

    Live Tennis API — Full Reference

    -

    Version 1.13.76 · OpenAPI 3.1.0

    +

    Version 1.13.77 · OpenAPI 3.1.0