Skip to content

Releases: livetennisapi/livetennisapi-python

v1.3.1 — fix: WS score frames nest the score object

Choose a tag to compare

@bensynapse bensynapse released this 07 Aug 01:40

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 — but ScoreUpdate.from_dict built its Score from the whole frame. On real frames every field of update.score came back None. The nested object is now parsed when present; the inline read remains only as a defensive fallback for flat emitters. break_point / break_point_result frames 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

Choose a tag to compare

@bensynapse bensynapse released this 07 Aug 00:54

Full typed coverage of the 2026-08 API surface, on both clients.

Added

  • Point-by-point tapeget_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 in tiebreaks; typed TapeMeta coverage.
  • In-play statisticsget_match_statistics() (ULTRA): derived vs measured families, per-family freshness; measured fields omitted when absent, never zero-filled.
  • Point-in-time rankingslist_rankings(): full published table for one system (PRO) or per-player as-of records via player= (ULTRA). RankingRecord carries previous_rank, rank_movement, rating.
  • Rally construction & charting (ULTRA) — list_rally_matches / get_rally_match / get_match_rally (404 not_charted distinguishable), get_charting_player / get_charting_match.
  • Bulk packageslist_history_packages(kind=, year=) / get_history_package() (PRO; non-tape kinds and year= are ULTRA).
  • Push feedget_ws_token() (ULTRA): ws_url, match_channel(id), slate_channel (slate:all).
  • Usageget_usage(), quota-exempt.
  • ErrorsAbuseThrottled (a RateLimited; .retry_at_epoch, never auto-retried) and daily-cap 429s surfacing .scope / .limit_per_day / .resets_at (parsed; not auto-retried). Parameter-aware required_tier on 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

Choose a tag to compare

@bensynapse bensynapse released this 03 Aug 11:41

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

Choose a tag to compare

@bensynapse bensynapse released this 02 Aug 02:14

Added

  • Break-point signals over the WebSocket feed. LiveScoreStream takes a new signals= argument; pass signals=["break_point"] and the stream also yields a BreakPoint the instant a break point arises and a BreakPointResult when it resolves, alongside the usual ScoreUpdate.
  • BreakPoint, BreakPointResult and the StreamFrame union are exported from the package (lazily, so websockets stays 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

Choose a tag to compare

@bensynapse bensynapse released this 21 Jul 12:36

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

Choose a tag to compare

@bensynapse bensynapse released this 19 Jul 00:31

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_attempts was 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 --json worked on only 3 of 8 subcommands, and livetennis live --json was an argparse error. Fixed both.
  • format_score used zip, silently dropping the in-progress set. Now zip_longest, matching the JS client.
  • AsyncLiveScoreStream was advertised but never existed. LiveScoreStream and ScoreUpdate are exported instead.
  • mypy could not run: python_version = "3.9" is rejected by mypy ≥1.18.
pip install --upgrade livetennisapi

livetennisapi 1.0.0

Choose a tag to compare

@bensynapse bensynapse released this 18 Jul 23:39

First release of the official Python client for the Live Tennis API.

Install

pip install livetennisapi

What's in it

  • LiveTennisAPI and AsyncLiveTennisAPI — all 12 REST endpoints, sync and async from one core.
  • LiveScoreStream — reconnecting WebSocket live-score feed (ULTRA).
  • livetennis CLIhealth, live, match, score, players, fixtures, history, watch.
  • Typed errorsUpgradeRequired carries .required_tier; RateLimited carries .retry_after.
  • Full type hints and a py.typed marker.

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.

Docs: https://docs.livetennisapi.com