Skip to content
datasportsAPI

DeliverySeason exports

Documentation

/

Type to filter the docs by title or path.

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
JSON · CSV · Parquet20 / dayKept 7 days
01

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.

02

Start, poll, download

Three calls, and only the middle one repeats.

The life of one export
  1. POST /v2/exports

    Requested

    Answers 202 at once with the export and its id.

  2. status: pending

    Building

    From a few seconds for a cached season to a few minutes. Poll every few seconds.

  3. status: ready

    Ready

    files lists each file with its size and a signed download link.

  4. files[].url

    Downloaded

    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. 1

    Start the export

    POST /v2/exports with a JSON body: league (our id), season (a season id from the league's seasons endpoint) and optionally format, which defaults to json. The answer is 202 with the export in pending.

    Request
    curl -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. 2

    Poll until it settles

    Read GET /v2/exports/{id} every few seconds until status is ready or failed. A cached season comes back within seconds; one built from scratch can take a few minutes.

    Request
    curl "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. 3

    Download the files

    Each entry in files has a signed url that works without your API key until linksExpireAt, 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://…"
03

Formats and columns

Pick the format that matches where the data is going.

jsonseason.json

One 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 tables

Two 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 tables

The 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
04

The export object

Create, status and list all return the same object.

FieldTypeMeaning
idintegerThe export's id, for polling.
leagueintegerOur league id, as you sent it.
seasonintegerThe 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.
errorstring | nullWhy a failed export failed, or, on a ready one, which parts could not be read, e.g. "not included: topPlayers".
fixtureCountinteger | nullHow many fixtures the export holds, once it is ready.
createdAtstring (ISO-8601, UTC)When you started it.
finishedAtstring | nullWhen it became ready or failed.
files{ name, bytes, contentType, url }[]One entry per file. Empty until the export is ready.
linksExpireAtstring (ISO-8601, UTC)When the url values in files stop working: an hour after you read them.
05

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.

06

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 error names what is missing, for example not 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.

07

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.