---
title: "Download full contest lineups (CSV or JSON)"
method: GET
path: "/api/v1/dfs/contests/{contest_id}/download"
tags: ["DFS"]
---

# Download full contest lineups (CSV or JSON)

`GET /api/v1/dfs/contests/{contest_id}/download`

## Request

- Base URL: `https://api.stat-api.com`
- URL: `https://api.stat-api.com/api/v1/dfs/contests/{contest_id}/download`
- Auth: HTTP bearer

Streams or renders full contest lineups including complete player pool,
ownership metrics, exact seat selections, and fantasy points.
Supports on-the-fly CSV generation (?format=csv) or streamable gzipped JSON (?format=json).

**CSV Lineup Slot Format**:
Each lineup roster cell is formatted with player metadata and slot-specific ownership:
`"<Player Name> (id: <export_id>, salary: <salary>, fpts: <fpts>, own: <slot_own>%)"`
- **Showdown / Single Game**: Captain/MVP slot displays Captain ownership (`cpt_field_pct`); FLEX slots display Flex ownership (`flex_field_pct`).
- **Several multiplied slots** (FanDuel NBA MVP/STAR/PRO, MLB MVP/STAR): each multiplied slot displays the share of lineups that hold the player in that slot, and the UTIL slots display the share that hold him in a UTIL seat. A cell shows the points of the player's row for its slot.
- **Classic Multi-Game**: All slots display tournament field ownership (`field_pct`).
- Each cell shows the id, salary and points of the player's `player_pool` row for that slot (the CPT/MVP row on the first seat of a single-game lineup).
- A value the data does not hold is left out: an unresolved player's cell reads `"(id: <export_id>, own: <slot_own>%)"`.
- A removed-player seat (the operator took the player off the slate after users drafted him) reads `"<operator's name> (id: <negative capsule-local id>, fpts: <operator's table points>, own: <slot_own>%, canonical_player_id: <our player id>, is_removed: true)"`: no salary and no upload id.
- A shared-name seat (the operator's own player pool gives the name to two players of one position, and its contest file names no id) reads `"<operator's name> (id: <negative capsule-local id>, fpts: <operator's table points>, own: <slot_own>%, is_shared_name: true)"`: no player id of any kind, no salary and no upload id.

**CSV Stat Enrichment (`enrich=dfs.player_stats`)**:
When enriched with `dfs.player_stats`, non-zero boxscore counting stats are nested directly inside each slot cell:
`"<Player Name> (id: <export_id>, salary: <salary>, fpts: <fpts>, own: <slot_own>%, dfs.player_stats: [<stat_1>: <val_1>, ...])"`

*Example (Showdown Enriched Lineup)*:
`"Patrick Mahomes (id: 112411-57439, salary: 15000, fpts: 45.15, own: 1.97%, dfs.player_stats: [passing_yds: 245, passing_tds: 1, rushing_yds: 43, rushing_tds: 1])","Josh Allen (id: 112411-62239, salary: 17000, fpts: 21.38, own: 2.12%, dfs.player_stats: [passing_yds: 237, passing_tds: 2, rushing_yds: 39, fumbles_recovered: 3])"`

**202 Accepted (the lineup file is not loaded yet)**: A caller with a paid plan (not a trial key) who asks for a DraftKings contest that has started and has no lineup file gets `202` and no lineups, and the API queues a load of the file. A 202 costs no record. It carries `Retry-After` (seconds) and `Cache-Control: no-store`, and its body is JSON whatever `format` asks for:

`{"status": "queued", "message": "The lineups of contest 196420231 are not loaded yet. A request to load them is queued. Expect them by the time in estimated_ready_at. Call again in 120 seconds.", "contest_id": 29647259, "external_id": 196420231, "requested_at": "2026-10-12T02:26:11Z", "estimated_ready_at": "2026-10-12T02:42:00Z", "retry_after_seconds": 120, "entry_count": 340}`

`status` is `queued` (the loader will load the file; `estimated_ready_at` is the time to expect it) or `waiting_for_contest` (the contest has not finished; `estimated_ready_at` is then null). Call again after `retry_after_seconds`.

A caller on a free plan, on a trial key or with no key never gets a 202: it keeps the answer it had before (a 200 with no lineups, or the preview).

A contest that will never have a file answers `404` (`lineups_not_collected`: the loader does not collect lineups for this contest), `410` (`lineups_file_gone`: DraftKings no longer serves the file) or `503` (`lineups_not_loaded`: the loader gave up and support is informed), each in the usual error body.

## Path parameters

- `contest_id` integer, required

## Query parameters

- `format` string
- `enrich` string
- `sampled_only` boolean

## Response `200`

Download full contest lineups (CSV or JSON)

- DFSContestsDownloadDfsContestDownloadResponse
  - `contest` object, required: Contest metadata.
    - `date` string, required: Slate date (YYYY-MM-DD).
    - `entry_fee_cents` integer, required: Entry fee in cents.
    - `game_type` string, required: Contest format (classic, showdown).
    - `id` integer, required: Contest ID.
    - `name` string, required: Contest name.
    - `operator` string, required: DFS operator name (DraftKings, FanDuel).
    - `operator_slate_id` integer, required: Operator slate ID (DraftKings contestGroupId, FanDuel fixtureListId).
    - `paid_places` integer, required: Total paying positions.
    - `prize_pool_cents` integer, required: Prize pool in cents.
    - `season` integer, required: Season year.
    - `slate_id` integer, required: DFS slate ID.
    - `slate_name` string, required: Name of the slate (e.g. Main, Sunday Main).
    - `slots` string[], required: Ordered roster position slots.
    - `sport` string, required: League or sport code.
    - `total_entries` integer, required: Total entrants.
  - `lineups` object[], required: Entrant lineups with rank, user, points, payout, and seat selections.
    - `payout_cents` number, double, required: Payout in cents.
    - `points` number, double, required: Total lineup fantasy points.
    - `rank` integer, required: Final standing rank.
    - `slots` integer[], required: Selected player IDs matching contest slots.
    - `username` string, required: Contest entrant username.
  - `player_pool` object[], required: The contest's player pool, one row per player per roster slot (hub decision D1). On a single-game contest (DraftKings showdown, FanDuel single game) each player has two rows with the same id: the multiplied slot (CPT on DraftKings, MVP on FanDuel) and FLEX (UTIL on NBA formats). On a contest with several multiplied slots (FanDuel NBA MVP 2x, STAR 1.5x, PRO 1.2x; FanDuel MLB MVP 2x, STAR 1.5x, shown as All-Star in the operator's file) each player has one row per multiplied slot and one for the plain slot (UTIL): the points of a row are the player's points times the multiplier of its slot. On a classic contest each player has one row, and its slot is his roster position. A lineup seat reads the row with its player id and its slot: the first seat of a single-game lineup is the CPT/MVP seat. Unique on (id, slot). Box-score stats come as an object per row with enrich=dfs.player_stats.
    - `canonical_player_id` integer, nullable: Our player ID in the league player tables. Null while the player is parked (DraftKings lists him, his identity is not resolved yet) or matched only by name; the player is still served with the operator's own name, team, position, salary and points. On a removed-player seat (is_removed) it is the player our player finder names for the operator's name. On a shared-name seat (is_shared_name) it is always null: two players of the operator's pool carry the name, and no rule picks one.
    - `export_id` string, nullable: The id the operator's lineup upload file uses for the player in this slot: the DraftKings draftable id, which differs between a player's CPT row and his FLEX row; the FanDuel "<fixture list id>-<player id>" id, the same on both rows. Null when unknown.
    - `fantasy_points` number, double, nullable: The fantasy points this slot scores: the CPT/MVP row carries the multiplied points (1.5x). Null when nothing measured them: a contest with no lineup file before our stats hold the game (the points appear once they do), or a player the operator's contest data gave no points for and our stats do not hold. Never a 0 placeholder.
    - `id` integer, required: The operator's player id (dfs.slate_players.external_id). lineups[].slots hold these ids. A single-game player's CPT/MVP row and FLEX row share it. A removed-player seat (is_removed) has a negative id local to the contest instead: the operator's file names him without an id. The same holds for a shared-name seat (is_shared_name).
    - `is_removed` boolean, nullable: Present, and true, only on a removed-player seat: the operator took the player off the slate after users drafted him (a trade, a cut), and its contest file still lists his seat with the points its own player table holds for him (0 when the table says 0). The row carries the operator's name and position, our player id (canonical_player_id), those fantasy points, and no salary, team or upload id. The key is absent on every other row.
    - `is_shared_name` boolean, nullable: Present, and true, only on a shared-name seat: the operator's own player pool gives the name to two players of one position, and its contest file names a seat by name and position only, so the seat belongs to neither. The row carries the operator's name and position and the points of its player table (the same whichever player is meant), and no player id of any kind (canonical_player_id is null), salary, team or upload id. The key is absent on every other row.
    - `is_unresolved` boolean, required: True when the player matches no row of the slate's player pool and the operator's contest data holds no name for him. The values the data does not hold are then null.
    - `name` string, nullable: The player's name, from the operator's contest data or the slate's player pool. Null when neither holds it (is_unresolved is then true); never a placeholder such as "Player 123".
    - `ownership` object, required: Contest field ownership of the player (not of the row's slot): his CPT/MVP row and his FLEX row carry the same values. Every share is null when the contest has no stored lineup (no lineup file yet, or a file with standings only): nothing measured it, and it is never shown as 0.
      - `cash_pct` number, double, nullable: Share of the cashing lineups that hold the player, in percent. Null unless the contest's lineup file stored it (older version-1 files, when a stored lineup cashed); current files do not store it, so it is null on them.
      - `cpt_field_pct` number, double, nullable: Share of the field's lineups that hold the player at CPT/MVP, in percent. On a contest with several multiplied slots, the share that hold him in the slot of the row (null on the UTIL row). Null on a classic contest and when the contest has no stored lineup.
      - `field_pct` number, double, nullable: Share of the field's lineups that hold the player in any slot, in percent. Null when the contest has no stored lineup.
      - `flex_field_pct` number, double, nullable: Share of the field's lineups that hold the player at FLEX, in percent. On a contest with several multiplied slots, the share that hold him in a UTIL seat. Null on a classic contest and when the contest has no stored lineup.
      - `top1_pct` number, double, nullable: Share of the top-1% lineups that hold the player, in percent. Null unless the contest's lineup file stored it (older version-1 files, when a stored lineup ranked in the top 1%); current files do not store it, so it is null on them.
    - `position` string, nullable: The player's real position (QB, RB, WR, TE, K, DST on DraftKings, D on FanDuel ...), the same on his CPT/MVP row and his FLEX row; never a slot such as CPT or MVP. Null only for an unresolved player.
    - `salary` integer, nullable: The salary of this slot in dollars, as the operator prices it: the CPT/MVP row carries the multiplied price (1.5x on DraftKings). Null only when the price is unknown (an unresolved player).
    - `slot` string, nullable: The roster slot this row prices and scores. On a single-game contest: CPT (DraftKings) or MVP (FanDuel) for the multiplied slot, and FLEX for the other slot (UTIL on NBA formats, as the contest names it). On a contest with several multiplied slots: MVP, STAR and PRO (MLB has MVP and STAR) for the multiplied slots, and UTIL for the plain slot. On a classic contest: the player's roster position. Null only for an unresolved player on a classic contest, whose position is unknown.
    - `team` string, nullable: Team abbreviation. Null for a player without a team (a golfer) and for an unresolved player.

## Other responses

- `400`: Invalid contest ID format
- `404`: Contest or full contest lineup not found, or the loader does not collect lineups for this contest (lineups_not_collected: a caller with a paid plan on a DraftKings contest that has started, see the description)
- `410`: DraftKings no longer serves the lineup file of this contest (lineups_file_gone)
- `503`: The loader gave up on the lineup file and support is informed (lineups_not_loaded, Retry-After 3600), or the request to load it could not be queued (lineups_request_failed, Retry-After 60)

---

[API](https://skmtc.dev/stat-api/apis/sports-db-public-api.md) · [All operations](https://skmtc.dev/stat-api/apis/sports-db-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/stat-api/apis/sports-db-public-api/revisions/601ff9eaca3f?raw)
