Which client library should I use?
Pick the row for your language. Every package calls the same REST API with the same key. The version is the newest release on the registry named under each install command. These client libraries are the official SDKs for the API.
| Library | Latest version | Minimum runtime | Install and package | Language | Licence | Source |
|---|---|---|---|---|---|---|
| Python client | 1.8.0 | Python 3.9 | pip install livetennisapilivetennisapi on PyPI | Python | MIT | GitHub |
| JavaScript and TypeScript client | 1.10.2 | Node 18 | npm install livetennisapilivetennisapi on npm | JavaScript, TypeScript | MIT | GitHub |
| Go client | 1.2.1 | Go 1.25 | go get github.com/livetennisapi/livetennisapi-gogithub.com/livetennisapi/livetennisapi-go on Go modules | Go | MIT | GitHub |
| PHP client | 1.1.1 | PHP 8.1 | composer require livetennisapi/livetennisapilivetennisapi/livetennisapi on Packagist | PHP | MIT | GitHub |
| .NET client | 1.2.1 | .NET Standard 2.0 or .NET 8 | dotnet add package LiveTennisApiLiveTennisApi on NuGet | C# | MIT | GitHub |
| Dart and Flutter client | 1.2.1 | Dart 3.5 | dart pub add livetennisapilivetennisapi on pub.dev | Dart | MIT | GitHub |
| Swift client | 1.1.1 | Swift 5.9 | .package(url: "https://github.com/livetennisapi/livetennisapi-swift.git", from: "1.1.1")livetennisapi-swift on Git tag | Swift | MIT | GitHub |
| R client | 0.2.0 | R 4.1 | install.packages("livetennisapi", repos = c("https://livetennisapi.r-universe.dev", "https://cloud.r-project.org"))livetennisapi on R-universe | R | MIT | GitHub |
| MCP server | 1.6.5 | Node 20 | npx -y livetennisapi-mcplivetennisapi-mcp on npm | TypeScript | MIT | GitHub |
| Vercel AI SDK tools | 1.3.0 | Node 22 | npm install livetennisapi-ailivetennisapi-ai on npm | TypeScript | MIT | GitHub |
| Export command | 0.1.0 | Python 3.9 | pip install livetennisapi-exportlivetennisapi-export on PyPI | Python | MIT | GitHub |
| Laravel package | 1.1.0 | PHP 8.2 | composer require livetennisapi/livetennisapi-laravellivetennisapi/livetennisapi-laravel on Packagist | PHP | MIT | GitHub |
| Haystack integration | 0.2.0 | Python 3.10 | pip install livetennisapi-haystacklivetennisapi-haystack on PyPI | Python | MIT | GitHub |
| LangChain integration | 0.2.0 | Python 3.10 | pip install -U langchain-livetennislangchain-livetennis on PyPI | Python | MIT | GitHub |
| LlamaIndex tools | 0.1.0 | Python 3.10 | pip install llama-index-tools-livetennisapillama-index-tools-livetennisapi on PyPI | Python | MIT | GitHub |
Every package is MIT licensed. Plugins for apps and automation platforms are not in this table. They live in the GitHub organisation.
Which libraries support REST, the live stream, file export and MCP?
Every library reads the REST API. Two open the live stream, one writes files and one is an MCP server.
| Library | REST | Live stream | File export | MCP |
|---|---|---|---|---|
| Python client | Yes | Yes | No | No |
| JavaScript and TypeScript client | Yes | Yes | No | No |
| Go client | Yes | Token only | No | No |
| PHP client | Yes | Token only | No | No |
| .NET client | Yes | Token only | No | No |
| Dart and Flutter client | Yes | Token only | No | No |
| Swift client | Yes | Token only | No | No |
| R client | Yes | No | No | No |
| MCP server | Yes | No | No | Yes |
| Vercel AI SDK tools | Yes | No | No | No |
| Export command | Yes | No | CSV, JSON Lines, Parquet | No |
| Laravel package | Yes | Token only | No | No |
| Haystack integration | Yes | No | No | No |
| LangChain integration | Yes | No | No | No |
| LlamaIndex tools | Yes | No | No | No |
Live stream says whether the package opens the WebSocket feed for you. The Python and
JavaScript clients do. In Python, pip install "livetennisapi[all]" adds the WebSocket feed.
Token only means the client calls GET /ws-token for a push feed token.
You open the socket with your own WebSocket library. The
push feed and webhooks page describes the frames.
The MCP server also runs hosted at https://mcp.livetennisapi.com/mcp, with nothing to install.
The AI SDK guide and the export guide
cover those two packages in full.
Which endpoints does each client cover?
The eight language clients do not all reach the same endpoints. Each cell was read from the source of the published version in the first table.
| Endpoints | Python | JS | Go | PHP | .NET | Dart | Swift | R | Paths |
|---|---|---|---|---|---|---|---|---|---|
| Matches and live scores | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | /matches/matches/{matchId}/matches/{matchId}/score |
| Players, fixtures and tournaments | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | /players/players/{playerId}/fixtures/tournaments/tournaments/{tournamentId} |
| Player record | No | Yes | No | No | No | No | No | Yes | /players/{playerId}/stats |
| Rankings | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | /rankings |
| Results archive and head-to-head | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Part | /history/archive/matches/history/archive/matches/{archiveId}/history/archive/players/history/archive/career/h2h |
| Completed matches and the point-by-point tape | Yes | Yes | Yes | Yes | Yes | Yes | Yes | No | /history/matches/history/matches/{matchId} |
| History coverage report | Yes | Yes | No | No | No | No | No | No | /history/coverage |
| Match events, statistics and analysis | Yes | Yes | Yes | Yes | Yes | Yes | Yes | No | /matches/{matchId}/events/matches/{matchId}/statistics/matches/{matchId}/analysis |
| Per-point events over REST | Yes | Yes | No | No | No | No | No | No | /matches/{matchId}/points |
| Markets and prices | Yes | Yes | Yes | Yes | Yes | Yes | Yes | No | /markets/markets/{matchId}/prices |
| Rally and charting data | Yes | Yes | Yes | Yes | Yes | Yes | Yes | No | /rally/matches/rally/matches/{rallyMatchId}/history/matches/{matchId}/rally/charting/players/charting/matches/{chartingMatchId} |
| History packages | Yes | Yes | Yes | Yes | Yes | Yes | Yes | No | /history/packages/history/packages/{period} |
| Webhooks | No | No | Yes | Yes | Yes | Yes | Yes | No | POST /webhooks/webhooksDELETE /webhooks/{webhookId} |
| Push feed token | Yes | Yes | Yes | Yes | Yes | Yes | Yes | No | /ws-token |
| Usage and quota | Yes | No | Yes | Yes | Yes | Yes | Yes | Yes | /usage |
Part for R means it lists archive results, players, careers and head-to-head records. It has no call for a single archive result.
An endpoint with no method in your client is still one HTTP request away. The reference lists every path. The errors page says when to retry.
What is the smallest working call in each language?
Each example lists live matches. Every call needs an API key, and a free key is enough for these. The free plan has 100 requests a day.
Python
pip install livetennisapi
from livetennisapi import LiveTennisAPI
with LiveTennisAPI(api_key="twjp_…") as client: # or set LIVETENNISAPI_KEY
for match in client.list_matches(status="live"):
print(match.tournament, match.p1.name, "vs", match.p2.name, match.score.sets)
The client reads LIVETENNISAPI_KEY when you pass no key. The spec lets score be null, so check it before you read sets. The example is from the README of version 1.8.0.
JavaScript and TypeScript
npm install livetennisapi
import { LiveTennisAPI } from 'livetennisapi';
const client = new LiveTennisAPI({ apiKey: 'twjp_…' }); // or $LIVETENNISAPI_KEY
const { data } = await client.listMatches({ status: 'live' });
for (const match of data) {
console.log(match.tournament, match.players?.p1?.name, 'vs', match.players?.p2?.name);
}
The package has no runtime dependencies and ships its own types. The example is from the README of version 1.10.2.
Go
go get github.com/livetennisapi/livetennisapi-go
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/livetennisapi/livetennisapi-go"
)
func main() {
client := livetennisapi.New(os.Getenv("LIVETENNISAPI_KEY"))
page, err := client.ListMatches(context.Background(), livetennisapi.ListMatchesParams{
Status: livetennisapi.StatusLive,
ListParams: livetennisapi.ListParams{Limit: 10},
})
if err != nil {
log.Fatal(err)
}
for _, match := range page.Data {
fmt.Printf("%-28s %s vs %s — %s\n",
match.Tournament,
match.Players.P1.Name,
match.Players.P2.Name,
match.Score, // nil-safe: prints "-" before a match starts
)
}
}
The module uses the standard library only. The example is from the README of version 1.2.1.
PHP
composer require livetennisapi/livetennisapi
composer require guzzlehttp/guzzle
use LiveTennisApi\LiveTennisApi;
$client = new LiveTennisApi('twjp_…'); // or set LIVETENNISAPI_KEY
foreach ($client->listMatches('live') as $match) {
echo $match->tournament, ' [', $match->tour, ']: ',
$match->p1()?->name, ' vs ', $match->p2()?->name, "\n";
// score is nullable — an upcoming match has none yet
if ($match->score !== null) {
// points are STRINGS ("40", "AD"); games are player-major per-set lists
echo ' ', implode('-', $match->score->points), "\n";
}
}
The client needs a PSR-18 HTTP client and PSR-17 factories. The second line installs Guzzle, which provides both. The example is from the README of version 1.1.1.
.NET
dotnet add package LiveTennisApi
using LiveTennisApi;
using LiveTennisApi.Models;
using var client = new LiveTennisApiClient("twjp_your_key");
var live = await client.ListMatchesAsync(MatchStatus.Live);
foreach (var m in live.Data)
{
Console.WriteLine($"{m.Tournament}: {m.Players?.P1?.Name} vs {m.Players?.P2?.Name}");
}
The package targets .NET Standard 2.0 and .NET 8. The example is from the README of version 1.2.1.
Dart and Flutter
dart pub add livetennisapi
import 'package:livetennisapi/livetennisapi.dart';
Future<void> main() async {
final client = LiveTennisApi(apiKey: 'twjp_your_key');
try {
final page = await client.listMatches(status: MatchStatus.live);
for (final match in page.data) {
final s = match.score;
final (g1, g2) = s?.gamesForSet(0) ?? (null, null);
print('${match.tournament} [${match.tour}]: '
'${match.p1?.name} vs ${match.p2?.name} '
'(${g1 ?? '-'}-${g2 ?? '-'}, serving: ${s?.server ?? '—'})');
}
} on UpgradeRequiredException catch (e) {
print('Needs the ${e.requiredTier} tier');
} on RateLimitedException catch (e) {
print('Slow down; retry after ${e.retryAfter}s');
} finally {
client.close();
}
}
The package is pure Dart and depends only on package:http. The example is from the README of version 1.2.1.
Swift
.package(url: "https://github.com/livetennisapi/livetennisapi-swift.git", from: "1.1.1")
import LiveTennisApi
let client = LiveTennisApiClient(apiKey: "twjp_your_key_here")
let live = try await client.listMatches(status: .live, tour: .atp)
for match in live.data {
let p1 = match.players?.p1?.name ?? "?"
let p2 = match.players?.p2?.name ?? "?"
print("\(match.tournament): \(p1) vs \(p2) — \(match.score?.description ?? "-")")
}
Add the first line to the dependencies in Package.swift. The package supports macOS 12, iOS 15, tvOS 15, watchOS 8 and Linux. The example is from the README of version 1.1.1.
R
install.packages("livetennisapi", repos = c("https://livetennisapi.r-universe.dev", "https://cloud.r-project.org"))
library(livetennisapi)
live <- live_matches(tour = "atp")
if (nrow(live) > 0) {
print(live[, c("id", "tournament", "players.p1.name", "players.p2.name")])
match_score(live$id[1])
}
search_players("sinner")
list_fixtures(tour = "wta", limit = 20)
List functions return a tibble. Put the key in ~/.Renviron as LIVETENNISAPI_KEY. The example is from the README of version 0.2.0.
How do I check that a version here is current?
The versions were read from the registries on . The registry link under each install command always shows the newest release.
The docs repository has a script, scripts/check_client_versions.py. It reads every
registry again and names each row that is behind.
Links
- Every repository in the GitHub organisation
- The OpenAPI spec, for generating a client in another language
- Free API key
- Authentication, quota and status