Releases: livetennisapi/livetennisapi-python
Release list
v1.3.1 — fix: WS score frames nest the score object
Fixed
- WebSocket score frames parsed as all-None. The wire nests the score object — a frame is
{"type": "score", "match_id": N, "score": {sets, games, points, server, is_tiebreak, timestamp, win_probability_p1?, danger?}}with the ULTRA model fields inside the score object — butScoreUpdate.from_dictbuilt itsScorefrom the whole frame. On real frames every field ofupdate.scorecame backNone. The nested object is now parsed when present; the inline read remains only as a defensive fallback for flat emitters.break_point/break_point_resultframes are flat on the wire and were never affected.
If you consume the live feed on 1.3.0, upgrade.
v1.3.0 — the tape, statistics, rankings, rally & charting, push feed
Full typed coverage of the 2026-08 API surface, on both clients.
Added
- Point-by-point tape —
get_match_tape(match_id, sequence=)(BASIC, or any History plan); works on live matches.TapeRow=Score+point_winner(clean rows only); per-set tiebreak finals intiebreaks; typedTapeMetacoverage. - In-play statistics —
get_match_statistics()(ULTRA): derived vs measured families, per-family freshness; measured fields omitted when absent, never zero-filled. - Point-in-time rankings —
list_rankings(): full published table for one system (PRO) or per-player as-of records viaplayer=(ULTRA).RankingRecordcarriesprevious_rank,rank_movement,rating. - Rally construction & charting (ULTRA) —
list_rally_matches/get_rally_match/get_match_rally(404not_charteddistinguishable),get_charting_player/get_charting_match. - Bulk packages —
list_history_packages(kind=, year=)/get_history_package()(PRO; non-tape kinds andyear=are ULTRA). - Push feed —
get_ws_token()(ULTRA):ws_url,match_channel(id),slate_channel(slate:all). - Usage —
get_usage(), quota-exempt. - Errors —
AbuseThrottled(aRateLimited;.retry_at_epoch, never auto-retried) and daily-cap 429s surfacing.scope/.limit_per_day/.resets_at(parsed; not auto-retried). Parameter-awarerequired_tieron 403s.
Changed
- WebSocket score frames carry the ULTRA model fields (
win_probability_p1,danger) exactly as REST does. - README: current quota grid (FREE 100/day), full endpoint/tier table, five-tour coverage (ATP, WTA, Challenger, ITF and juniors).
Fully backwards compatible.
v1.2.0 — results archive (1968–2022), head-to-head, tournaments
New endpoint coverage: tournaments (FREE), the results archive 1968–2022 — matches, player bios with career-highs, career aggregates — and head-to-head across the full 1968→now span (BASIC, or any History plan). New match filters (tour, player, country, from/to, coverage) and new typed fields (tour, tournament_id, round_code, withdrew; fixture start_time + player ids). See CHANGELOG.md.
v1.1.0 — break-point signals over the WebSocket feed
Added
- Break-point signals over the WebSocket feed.
LiveScoreStreamtakes a newsignals=argument; passsignals=["break_point"]and the stream also yields aBreakPointthe instant a break point arises and aBreakPointResultwhen it resolves, alongside the usualScoreUpdate. BreakPoint,BreakPointResultand theStreamFrameunion are exported from the package (lazily, sowebsocketsstays optional).
Notes
- Fully backwards compatible. With no
signals(the default) the behaviour is byte-for-byte identical to 1.0.2 — score frames only. - The break-point feed is ULTRA-only, like the rest of the WebSocket surface.
Full changelog: CHANGELOG.md
livetennisapi 1.0.2
Fixed
A 403 on the history endpoint could not be attributed to a tier.
/history/matches used to be the entitlement floor, so nothing needed to name a tier for it. With the new FREE tier below it, a free key calling that method got an UpgradeRequired carrying no required-tier, leaving the caller with the API's bare upgrade_required and no idea which plan to buy. /history now maps to BASIC.
Mutation-verified: removing the mapping makes the new test fail.
Full changelog: https://github.com/livetennisapi/livetennisapi-python/blob/main/CHANGELOG.md
livetennisapi 1.0.1
Bug-fix release from an adversarial review of 1.0.0. No API changes.
Fixed
- WebSocket backoff never grew against a flapping server. The retry counter reset on a successful subscribe, so a server that accepted then immediately dropped pinned the delay at step one forever and
max_reconnect_attemptswas never reached. It now resets only after a connection survives 60s. - WebSocket leaked a socket per failed handshake — the subscribe exchange was outside any
try, so a recv timeout escaped with the socket still open, once per reconnect attempt. livetennis --jsonworked on only 3 of 8 subcommands, andlivetennis live --jsonwas an argparse error. Fixed both.format_scoreusedzip, silently dropping the in-progress set. Nowzip_longest, matching the JS client.AsyncLiveScoreStreamwas advertised but never existed.LiveScoreStreamandScoreUpdateare exported instead.- mypy could not run:
python_version = "3.9"is rejected by mypy ≥1.18.
pip install --upgrade livetennisapilivetennisapi 1.0.0
First release of the official Python client for the Live Tennis API.
Install
pip install livetennisapiWhat's in it
LiveTennisAPIandAsyncLiveTennisAPI— all 12 REST endpoints, sync and async from one core.LiveScoreStream— reconnecting WebSocket live-score feed (ULTRA).livetennisCLI —health,live,match,score,players,fixtures,history,watch.- Typed errors —
UpgradeRequiredcarries.required_tier;RateLimitedcarries.retry_after. - Full type hints and a
py.typedmarker.
Two design rules, both taken from the API's own contract
Models never reject unknown fields. The API ships additive changes within v1, so a strict client would break the first time a field is added. Unknown keys stay in .raw and are readable as attributes — a new server-side field works without upgrading this package.
Only 429 and 5xx are retried. A bad key or an unentitled tier cannot start working, so retrying it only burns the caller's rate limit. Retry-After is honoured, with exponential backoff and jitter.
Note on the score shape
games is player-major: [[6,3,2],[4,6,1]] reads 6-4, 3-6, 2-1. Use score.games_for_set(i) rather than indexing by hand.
Tested on Python 3.9–3.13, macOS, Linux and Windows. Published from CI via PyPI Trusted Publishing (OIDC) — no API token exists for this package.