Delivery · Season exports
A whole season, as files
When you need everything a league did in a season (every fixture, the final tables, the leading players) hundreds of paged calls are the slow way. An export packs one league season into JSON, CSV or Parquet in the background and hands you download links when it is done.
- Free: not included
- Starter: not included
- Pro: not included
- Business: included
What an export is
One league, one season, one request to start it. The files are built from the same data the endpoints serve, so nothing is renamed or reshaped behind your back.
- Scope
- one league season
A league id plus one of that league's season ids. For several seasons, start several exports.
- Built
- in the background
You get an id at once and poll it. Your code never waits on a long request.
- Good for
- warehouses, models, archives
Back-filling a data warehouse, training sets for a model, an end-of-season archive.
- Plan
- Business
On any other plan, every exports endpoint answers 403 endpoint_not_in_plan.
Start, poll, download
Three calls, and only the middle one repeats.
POST /v2/exportsRequested
Answers 202 at once with the export and its id.
status: pendingBuilding
From a few seconds for a cached season to a few minutes. Poll every few seconds.
status: readyReady
files lists each file with its size and a signed download link.
files[].urlDownloaded
Links last one hour. Ask for the export again for fresh ones.
status: failed: nothing to download; error says why.
Every export is kept for 7 days, then deleted. Download what you want to keep.
- 1
Start the export
POST /v2/exportswith a JSON body:league(our id),season(a season id from the league's seasons endpoint) and optionallyformat, which defaults tojson. The answer is 202 with the export inpending.Requestcurl -X POST "https://api.datasportsapi.com/v2/exports" \ -H "Authorization: Bearer $DATASPORTSAPI_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "league": 17, "season": 61627, "format": "parquet" }'202 · response{ "data": { "id": 42, "league": 17, "season": 61627, "format": "parquet", "status": "pending", "error": null, "fixtureCount": null, "createdAt": "2026-11-01T18:00:00.000Z", "finishedAt": null, "files": [] } } - 2
Poll until it settles
Read
GET /v2/exports/{id}every few seconds untilstatusisreadyorfailed. A cached season comes back within seconds; one built from scratch can take a few minutes.Requestcurl "https://api.datasportsapi.com/v2/exports/42" \ -H "Authorization: Bearer $DATASPORTSAPI_API_KEY"Response, once ready{ "data": { "id": 42, "league": 17, "season": 61627, "format": "parquet", "status": "ready", "error": null, "fixtureCount": 380, "createdAt": "2026-11-01T18:00:00.000Z", "finishedAt": "2026-11-01T18:00:41.000Z", "files": [ { "name": "fixtures.parquet", "bytes": 21944, "contentType": "application/vnd.apache.parquet", "url": "https://…" }, { "name": "standings.parquet", "bytes": 3120, "contentType": "application/vnd.apache.parquet", "url": "https://…" } ], "linksExpireAt": "2026-11-01T19:01:02.000Z" } } - 3
Download the files
Each entry in
fileshas a signedurlthat works without your API key untillinksExpireAt, an hour after you read it. Missed the window? Read the export again, or list your exports, and you get fresh links.Download# each files[].url is a signed link: no API key needed, valid until linksExpireAt curl -o fixtures.parquet "https://…" curl -o standings.parquet "https://…"
Formats and columns
Pick the format that matches where the data is going.
jsonseason.jsonOne document holding the league, the season, its rounds, every fixture, the standings, top players and top teams, each shaped exactly as the matching endpoint returns it.
Default. Reuse the parsers you already wrote for the REST API.
csvtwo tablesTwo flat tables, fixtures and standings, with one row per fixture and one per table position.
Spreadsheets, quick imports, anything that speaks rows and columns.
parquettwo tablesThe same two tables as CSV, typed and compressed in the columnar Parquet format.
pandas, Polars, DuckDB, Spark and data warehouses.
Columns of the flat tables
CSV and Parquet use the same column names, in snake_case. Ids are our ids, so they join straight onto anything you read from the REST API.
fixtures
- fixture_id
- sport
- start_time
- round
- round_name
- status
- status_description
- home_team_id
- home_team
- away_team_id
- away_team
- home_score
- away_score
- winner
- venue
standings
- table
- primary
- position
- team_id
- team
- played
- wins
- draws
- losses
- scores_for
- scores_against
- points
- zone
The export object
Create, status and list all return the same object.
| Field | Type | Meaning |
|---|---|---|
id | integer | The export's id, for polling. |
league | integer | Our league id, as you sent it. |
season | integer | The season id, as you sent it. |
format | "json" | "csv" | "parquet" | The format you asked for. |
status | "pending" | "ready" | "failed" | Where the build stands. Poll until it is ready or failed. |
error | string | null | Why a failed export failed, or, on a ready one, which parts could not be read, e.g. "not included: topPlayers". |
fixtureCount | integer | null | How many fixtures the export holds, once it is ready. |
createdAt | string (ISO-8601, UTC) | When you started it. |
finishedAt | string | null | When it became ready or failed. |
files | { name, bytes, contentType, url }[] | One entry per file. Empty until the export is ready. |
linksExpireAt | string (ISO-8601, UTC) | When the url values in files stop working: an hour after you read them. |
Limits and lifetimes
- Exports per day
- 20 per account, per UTC day
The 21st start of the day answers 429 export_limit. The count starts again at 00:00 UTC.
- Kept for
- 7 days
After that the export and its files are gone. Store what you need.
- Download links
- 1 hour
Read the export again whenever you need fresh ones.
- History
- GET /v2/exports
The 20 most recent exports, newest at the top; ready ones come with new links.
Reading the files
An export mirrors the source faithfully, quirks included. Three to know about.
Fixtures follow rounds
Fixtures are collected round by round. Competitions the source does not organise in rounds, such as the NBA and NHL regular seasons, export no fixtures; page through the league's fixtures endpoint for those.
A ready export can still be partial
If a part could not be read, the export still completes and
errornames what is missing, for examplenot included: topPlayers. Check it even when the status is ready.Replayed matches appear twice
A match postponed and played later can show up as two fixtures with different ids, the postponed one and the played one, exactly as the source lists them. Filter on status if you need one row per pairing.
One script, end to end
Start an export, wait for it, save the files. Swap the league and season ids for your own.
// export-season.ts: one league season to Parquet files on disk (Node 22+)
import { writeFile } from "node:fs/promises";
const API = "https://api.datasportsapi.com";
const headers = {
Authorization: `Bearer ${process.env.DATASPORTSAPI_API_KEY}`,
"Content-Type": "application/json",
};
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
async function call(path: string, init: RequestInit = {}) {
const res = await fetch(API + path, { ...init, headers });
const body = await res.json();
if (!res.ok) throw new Error(`${res.status} ${body.error.code}: ${body.error.message}`);
return body.data;
}
// 1. Start: answers 202 with status "pending"
let exp = await call("/v2/exports", {
method: "POST",
body: JSON.stringify({ league: 17, season: 61627, format: "parquet" }),
});
// 2. Poll until it settles: seconds when cached, a few minutes otherwise
while (exp.status !== "ready" && exp.status !== "failed") {
await sleep(5_000);
exp = await call(`/v2/exports/${exp.id}`);
}
if (exp.status === "failed") throw new Error(exp.error);
if (exp.error) console.warn(exp.error); // e.g. "not included: topPlayers"
// 3. Download every file while the links are valid (one hour)
for (const file of exp.files) {
const res = await fetch(file.url); // signed link: no Authorization header
await writeFile(file.name, Buffer.from(await res.arrayBuffer()));
console.log(`${file.name} ${file.bytes} bytes`);
}Not sure which season id to use? The league's seasons endpoint lists them. Need a league id? Search, or browse the endpoint catalogue.