Live tennis scores are awkward to get right. Most feeds either poll a scraped web page or hand you a shape that quietly loses information. This walks through reading real live scores in Python in about five minutes.
Install
pip install livetennisapi
One dependency (httpx). Python 3.9+.
Authenticate
Get a key at livetennisapi.com. Keys look like twjp_….
The client reads LIVETENNISAPI_KEY from the environment, so you never have to
hard-code it:
export LIVETENNISAPI_KEY=twjp_your_key_here
Read the live board
from livetennisapi import LiveTennisAPI
with LiveTennisAPI() as client:
for match in client.list_matches(status="live"):
print(match.tournament, "—", match.p1.name, "vs", match.p2.name)
print(" ", match.score.sets, match.score.points)
Real output looks like this:
Lincoln — Bernard Tomic vs Spencer Johnson
[0, 0] ['40', '30']
M15 Rochester, NY — Michael Antonius vs Ilyas Milad Fahim
[0, 0] ['40', '0']
The one thing to get right: games is player-major
This is the detail that catches almost everyone. games is not a list of
sets. It is [games_p1, games_p2], where each side is its own per-set list:
score.games # [[6, 3, 2], [4, 6, 1]]
# ^ p1 per set ^ p2 per set
That reads 6-4, 3-6, 2-1 — not 6-4, 3-6, 2-1 read column-wise from pairs. Indexing it the other way gives you a plausible-looking but wrong scoreline, which is the worst kind of bug. Use the helper:
score.games_for_set(0) # (6, 4)
score.games_for_set(1) # (3, 6)
sets is simpler: [sets_p1, sets_p2]. And server is 1 or 2.
Handle the errors you will actually hit
from livetennisapi import UpgradeRequired, RateLimited
try:
analysis = client.get_match_analysis(match_id)
except UpgradeRequired as exc:
print(f"needs the {exc.required_tier} plan") # 'ULTRA'
except RateLimited as exc:
print(f"slow down, retry in {exc.retry_after}s")
UpgradeRequired is not an auth failure — the key is fine, the plan is too low.
The client tells you which tier unlocks the endpoint rather than leaving you to
guess from a bare 403.
Requests retry automatically on 429 and 5xx only, honouring Retry-After
with exponential backoff. Other 4xx are never retried, because a bad key or an
unentitled tier cannot start working and retrying only burns your rate limit.
Async, if you need it
Identical API, awaited:
from livetennisapi import AsyncLiveTennisAPI
async with AsyncLiveTennisAPI() as client:
page = await client.list_matches(status="live")
Paginate
limit defaults to 50 and the API rejects anything above 200. To walk
everything without managing offsets:
for player in client.paginate("search_players", search="alcaraz"):
print(player.name, player.ranking)
From the command line
The package ships a CLI, so you can check the feed without writing any code:
livetennis live
livetennis match 18953
livetennis players djokovic
Where to go next
- Full endpoint reference: https://docs.livetennisapi.com/reference.html
- Source and issues: github.com/livetennisapi/livetennisapi-python
- Plans and pricing: https://livetennisapi.com/#pricing