Live Tennis API DocsGet a free key
Explore the documentation

Live Tennis API · docs · guide

Export tennis data to CSV, JSON Lines or Parquet

How do you export tennis data to CSV, JSON Lines or Parquet? Install livetennisapi-export, set LIVETENNISAPI_KEY and name an output file. The file extension picks the format. The command pages through every result and writes matches, fixtures, players, rankings or tournaments.

Package livetennisapi-export 0.1.0, for Python 3.9 or later. Read-back examples run on with pandas 3.1.1, DuckDB 2.0.0 and R 4.6.2.

Install livetennisapi-export

The package is livetennisapi-export on PyPI. It needs Python 3.9 or later and installs the official Python client with it.

pip install livetennisapi-export
pip install "livetennisapi-export[parquet]"

The second line adds Parquet support. The parquet extra brings in pandas and pyarrow.

Set the API key

The command reads your key from the LIVETENNISAPI_KEY environment variable. Get a free key.

export LIVETENNISAPI_KEY=twjp_...

There is no flag for the key, so the key is never part of the export command. Without the variable the command prints where to get a key and exits with status 2. It sends no request.

Which format should I pick?

The file name picks the format. All three hold the same rows.

The three export formats and when each fits
FormatFile nameWhat a row isPick it when
CSV.csvFlat columns, one header line, every value as textYou want to open the file in a spreadsheet or load it into a tool that reads nothing else.
JSON Lines.jsonl or .ndjsonOne JSON object per line, exactly as the API returned it, nested objects includedYou want the record unchanged, for a pipeline, a document store or a later reshape.
Parquet.parquet or .pqFlat columns with a type per columnYou analyse the data in pandas, DuckDB or R and want numbers and booleans to stay typed.

CSV is the one every tool opens. A reader has to work out the types again, because a CSV cell is text. JSON Lines is the only format that keeps nested objects as objects. Parquet keeps a type for each column, so a rank stays a number and a flag stays a boolean.

Use -o - with -f csv or -f jsonl to write to stdout. Parquet is binary and needs a file name.

Which datasets can I export, and which plan does each need?

There are six datasets. Each one reads a single endpoint through the official Python client. The plans are the ones the package states.

The six datasets, the endpoint each reads and the plan it needs
DatasetEndpointPlanRequests
matchesGET /matchesFREE for live and upcoming. completed and cancelled are part of History and need BASIC or a History planOne per page
fixturesGET /fixturesFREEOne per page
playersGET /players?search=FREEOne per page
playerGET /players/{playerId}FREEOne per player id
rankingsGET /rankingsPRO. --system elo needs ULTRAOne per page
tournamentsGET /tournamentsFREEOne per page

If your plan does not cover a request, the API answers 403. The command then prints the refusal and exits with status 1. Plans and prices are on the pricing page. The errors page explains each status.

The spec also gives a FREE key 20 history calls each calendar month, which completed matches can use.

rankings exports one ranking table in rank order, so it needs --system. The choices are atp, wta, itf_jt, itf_mt, itf_wt, elo, atp_doubles and wta_doubles. elo also needs --tour atp or --tour wta. matches takes --status, and it defaults to upcoming.

# Upcoming fixtures on the ATP tour
livetennisapi-export fixtures --tour atp -o fixtures.csv

# Every match completed on one day, as Parquet
livetennisapi-export matches --status completed --date 2026-11-08 -o results.parquet

# The same day as JSON Lines, rows unchanged
livetennisapi-export matches --status completed --date 2026-11-08 -o results.jsonl

# Full profiles for a list of player ids
livetennisapi-export player 101 202 303 -o players.csv

# The current ATP ranking table
livetennisapi-export rankings --system atp -o atp.csv

# Player search, written to stdout
livetennisapi-export players alcaraz -o - -f jsonl

livetennisapi-export DATASET --help lists every option for a dataset.

How does paging work?

The command asks for pages of 200 rows, the largest page the API allows. It moves the offset forward by the rows it received and stops when meta.has_more is false. You never page by hand.

Quotas for each plan are on the authentication, quota and status page.

How do nested fields become columns?

JSON Lines needs no change, so each line is the object the API returned. CSV and Parquet need flat columns. The writer applies the same rules to both.

Here is a match row cut down to a few fields. The values are made up.

{"id": 5001, "tournament": "Example Open",
 "players": {"p1": {"name": "Test Player A", "ranking": 12}},
 "score": {"sets": [2, 0], "games": [[6, 6], [3, 4]]}}

It becomes six columns.

The columns the example row becomes
ColumnValue in the CSV
id5001
tournamentExample Open
players.p1.nameTest Player A
players.p1.ranking12
score.sets[2, 0]
score.games[[6, 6], [3, 4]]

The columns follow the fields the API sent, in the order they first appear. A field added to the API shows up as a new column with no upgrade. These are some of the columns each export has. Every name is a field the reference documents.

Some of the column names in each export
ExportSome of its columns
fixtures, as CSVid, event_date, start_time, player1_id, player1_name, player2_id, player2_name, tournament
matches, completed, as Parquetplayers.p1.itf_ranking.rank_date, score.timestamp
rankings, --system atpeffective_date, observed_at, player_id, player_name, points, previous_rank, rank, rank_movement

A match file is wide, because each player object and the score object spread into their own columns. In Parquet a whole-number column that has an empty value is stored as a decimal number.

When is the file written?

Nothing is written until every page has arrived. A request that fails halfway leaves no half-written file. A file of the same name from an earlier run stays as it was.

How do I read an export in pandas?

A dotted name is an ordinary column label in pandas.

import json
import pandas as pd

results = pd.read_parquet("results.parquet")
print(results[["tournament", "round", "players.p1.name", "players.p2.name", "winner"]])

# Lists are stored as JSON text. Decode a column when you need the values.
results["sets"] = results["score.sets"].map(json.loads, na_action="ignore")

fixtures = pd.read_csv("fixtures.csv", parse_dates=["event_date"])
print(fixtures.groupby("tournament")["id"].count())

How do I query an export in DuckDB?

DuckDB reads all three formats from the file name.

-- A dotted column needs double quotes in CSV and Parquet files.
SELECT tournament, "players.p1.name", "players.p2.name", winner
FROM 'results.parquet'
WHERE surface = 'hard';

-- Lists are JSON text. Cast one to read it as a list.
SELECT id, CAST("score.sets" AS INTEGER[]) AS sets
FROM 'results.parquet';

-- JSON Lines keeps the nesting, so the same field is a struct path.
SELECT tournament, players.p1.name, score.sets
FROM read_json_auto('results.jsonl');

SELECT rank, player_name, points
FROM 'atp.csv'
ORDER BY rank;

How do I read an export in R?

Base R reads the CSV. The arrow package reads the Parquet file.

fixtures <- read.csv("fixtures.csv")
table(fixtures$tournament)

library(arrow)
results <- read_parquet("results.parquet")
results[, c("tournament", "players.p1.name", "players.p2.name", "winner")]

# Lists are stored as JSON text.
library(jsonlite)
sets <- lapply(results$score.sets, function(x) if (is.na(x)) NULL else fromJSON(x))

Can I export from Python code?

The same package is a small Python API. fetch returns the rows, write writes them and export does both.

from livetennisapi_export import export, fetch, to_dataframe, write

rows = fetch("fixtures", tour="atp")               # a list of dicts
write(rows, "fixtures.jsonl")

export("matches", "results.csv", status="completed", from_="2026-11-01", to="2026-11-07")

df = to_dataframe(fetch("rankings", system="atp"))  # the same flat columns as the CSV

from_ is the from parameter, renamed because from is a Python keyword. fetch and export take api_key= or read LIVETENNISAPI_KEY. to_dataframe needs the parquet extra.