STAT-API

Read the lineup that won a contest

Winning lineups are the record of what actually worked. This recipe walks the whole contest chain: pick a contest on a slate, page its lineups, take first place, and read the players it rostered. Each hop uses that endpoint's own required filter, so the chain is the only way through — there is no single call that returns a contest's rosters.

  1. 1. List the contests on the slate

    A main slate carries a few dozen contests, from single-entry qualifiers to mass-entry tournaments. entry_fee and prize_pool are integer CENTS — 3300 is $33.00 and 12500000 is $125,000.

    curl -sS --compressed \
      -H 'Authorization: Bearer YOUR_API_KEY' \
      'https://api.stat-api.com/api/v1/dfs/contests?limit=100&slate_id=129036'
  2. 2. Keep the single-entry contests

    max_entries_per_user of 1 means every entrant submitted one lineup, so first place belongs to one person rather than to the best of somebody's 150 entries.

    const single = contests.filter((row) => row.max_entries_per_user === 1)
  3. 3. Take the smallest one

    Lineups are paged one contest at a time, and a large tournament holds tens of thousands. Starting with the smallest keeps this recipe to a few requests.

    const bysize = [...single].sort((a, b) => a.entry_count - b.entry_count)
  4. 4. One contest

    const contest = bysize.slice(0, 1)
  5. 5. Page every lineup in that contest

    One row per submitted lineup, carrying its rank, its score, and its payout.

    curl -sS --compressed \
      -H 'Authorization: Bearer YOUR_API_KEY' \
      'https://api.stat-api.com/api/v1/dfs/contest_user_lineups?contest_id=1'
  6. 6. Keep the lineups that were scored

    const ranked = lineups.filter((row) => row.rank !== null && row.rank !== undefined)
  7. 7. First place first

    const byrank = [...ranked].sort((a, b) => (a.rank ?? 0) - (b.rank ?? 0))
  8. 8. Take the winner

    const winner = byrank.slice(0, 1)
  9. 9. Show the winning entry

    winnings is in cents, so divide by 100 for dollars.

    console.log("Winning entry")
    console.log(["rank", "points", "winnings"].join('\t'))
    for (const row of winner) {
      console.log([String(row.rank ?? ''), String(row.points ?? ''), String(row.winnings ?? '')].join('\t'))
    }
  10. 10. Read the nine players it rostered

    Entries reference the slate player, not the person — slate_player_id points at the pool row, which carries the salary that lineup paid.

    curl -sS --compressed \
      -H 'Authorization: Bearer YOUR_API_KEY' \
      'https://api.stat-api.com/api/v1/dfs/contest_user_lineup_player_entries?contest_user_lineup_id=1'
  11. 11. Page the player pool to resolve names

    curl -sS --compressed \
      -H 'Authorization: Bearer YOUR_API_KEY' \
      'https://api.stat-api.com/api/v1/dfs/slate_players?slate_id=129036'
  12. 12. Index the pool by its own id

    Entries join on slate_players.id, not on player_id.

    const players = new Map<number, DFSSlatePlayer>()
    for (const row of pool) players.set(row.id, row)
  13. 13. Print the winning roster

    console.log("The winning lineup")
    console.log(["display_name", "roster_position", "position", "salary"].join('\t'))
    for (const row of roster) {
      console.log([String(players.get(row.slate_player_id)?.display_name ?? row.slate_player_id), String(row.roster_position ?? ''), String(players.get(row.slate_player_id)?.position ?? row.slate_player_id), String(players.get(row.slate_player_id)?.salary ?? row.slate_player_id)].join('\t'))
    }