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. Read column-wise from pairs it comes out as something else.
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 includes the endpoint, so you never have 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