Get your league's ESPN Fantasy Football scores using Typescript

Typescript function to call the ESPN Fantasy Football API and returns your league's current matchups.

Setup

First you'll need to get your League ID, which can be found by going to fantasy.espn.com, navigating to your league and copying the path parameter from the url, which will look something like https://fantasy.espn.com/football/league?leagueId=12345. That will be the leagueId variable in the .ts file below, which you can hardcode in the file or use a .env file like the snippet uses. Your .env file would look like:

FF_LEAGUE_ID=12345
text

The only dependency is node-fetch which is a lightweight Fetch wrapper for Node.js. I suppose you could also use the fetch library built into Node.js if your project is on a diet. That's it! Here's the main function for returning the Boxscore.

import { BoxscoreResponse, Matchup, MatchupObj, Scoreboard } from './types'; const fetch = require('node-fetch'); const leagueId = process.env.FF_LEAGUE_ID; const getBoxscores = async (seasonId: number, scoringPeriodId: number) => { const BASE_URL = `https://fantasy.espn.com/apis/v3/games/ffl/seasons/${seasonId}/`; const scoresUrl = BASE_URL + `segments/0/leagues/${leagueId}?view=mMatchupScore&view=mTeam&view=mScoreboard&view=mRoster`; try { const response = await fetch(scoresUrl); const data: BoxscoreResponse = await response.json(); const boxscores: Scoreboard[] = data.schedule .filter((x) => x.matchupPeriodId === scoringPeriodId) .map((score: Matchup) => { let home = data.teams.find((x) => x.id === score.home.teamId); let away = data.teams.find((x) => x.id === score.away.teamId); let homeMember = data.members.find( (member) => home?.owners.findIndex((owner) => owner === member.id) != -1 )!; let awayMember = data.members.find( (member) => away?.owners.findIndex((owner) => owner === member.id) != -1 )!; return { homeScore: score.home.totalProjectedPointsLive || score.home.totalPoints, awayScore: score.away.totalProjectedPointsLive || score.away.totalPoints, homeName: home!.location + ' ' + home!.nickname, awayName: away!.location + ' ' + away!.nickname, homeFirstName: homeMember.firstName, awayFirstName: awayMember.firstName, homeLastName: homeMember.lastName, awayLastName: awayMember.lastName, homeRoster: rosterForMatchup(score.home), awayRoster: rosterForMatchup(score.away), }; }); return boxscores; } catch (err) { throw Error('(in getBoxscores()):' + err); } }; const rosterForMatchup = (matchup: MatchupObj) => { if ( !matchup.rosterForCurrentScoringPeriod || !matchup.rosterForCurrentScoringPeriod.entries ) { return []; } else { return matchup.rosterForCurrentScoringPeriod.entries.map((x) => { const slot = x.lineupSlotId; return { name: x.playerPoolEntry.player.fullName, projectedPoints: x.playerPoolEntry.player.stats[0].appliedTotal || 0, points: x.playerPoolEntry.appliedStatTotal, starting: slot < 9 || slot == 17 || slot == 16, // Other slots are bench positions - this could change based on league settings }; }); } }; export default getBoxscores;
ts

Note that the "starting" field for individual players is based on a roster size of 13 with 4 bench slots and 1 IR slot. If your league settings are different, the "starting" slots could be different.

To get the seasonId (year) and scoringPeriodId, either hardcode the values or put this function in a separate file to get it for you:

import { Matchup, Status, WEEK_STATUS } from './types'; const fetch = require('node-fetch'); const leagueId = process.env.FF_LEAGUE_ID; // Returns the current year (seasonId) and week (scoring period) const getCurrentWeek = async () => { const year = new Date().getFullYear().toString(); const URL = `https://fantasy.espn.com/apis/v3/games/ffl/seasons/${year}/segments/0/leagues/${leagueId}?view=mMatchupScore&view=mScoreboard`; try { const response = await fetch(URL); const data: CurrentWeekResponse = await response.json(); const status = getStatusForData(data); const currentWeek = status === 'JUST FINISHED' ? data.scoringPeriodId - 1 : data.scoringPeriodId; return { seasonId: data.seasonId, currentWeek, status }; } catch (err) { throw Error('(in getCurrentWeek()):' + err); } }; function getStatusForData(data: CurrentWeekResponse): WEEK_STATUS { var today = new Date(); // on Tuesday => "JUST FINISHED" if (today.getDay() === 2) { return 'JUST FINISHED'; } const { scoringPeriodId, schedule, status } = data; const matchup = schedule.filter((x) => x.matchupPeriodId === scoringPeriodId)[0]; if (new Date()) if (scoringPeriodId > status.finalScoringPeriod) { return 'SEASON OVER'; } else if (!!matchup.home.totalPointsLive && matchup.home.totalPointsLive > 0) { return 'LIVE'; } return 'UPCOMING'; } interface CurrentWeekResponse { seasonId: number; scoringPeriodId: number; schedule: Matchup[]; status: Status; }
ts

The types.ts file:

// The generated object after filtering/mapping export interface Scoreboard { homeScore: number; awayScore: number; homeName: string; awayName: string; homeFirstName: string; awayFirstName: string; homeLastName: string; awayLastName: string; homeRoster: Player[]; awayRoster: Player[]; } export type WEEK_STATUS = 'LIVE' | 'UPCOMING' | 'JUST FINISHED' | 'SEASON OVER'; // ----- Below is what's returned from the API --- // export interface BoxscoreResponse { gameId: number; id: number; schedule: Matchup[]; scoringPeriodId: number; seasonId: number; segmentId: number; status: Status; members: Member[]; teams: Team[]; } export interface Matchup { away: MatchupObj; home: MatchupObj; id: number; matchupPeriodId: number; winner: Winner; } export interface MatchupObj { teamId: number; totalPoints: number; totalPointsLive?: number; totalProjectedPointsLive?: number; rosterForCurrentScoringPeriod: { lineupSlotId: number; entries: PlayerEntry[] }; } export interface Status { currentMatchupPeriod: number; finalScoringPeriod: number; latestScoringPeriod: number; // not using these rn standingsUpdateDate: number; transactionScoringPeriod: number; waiverLastExecutionDate: number; waiverProcessStatus: { [key: string]: number }; // { [ISO_DATE]: teamId } }
ts

Calling the function:

Here's a basic example of getting the boxscores:

try { const { seasonId, currentWeek, status } = await getCurrentWeek(); if (status === 'SEASON OVER') { return successResponse('The season is over :('); } const boxscore = await getBoxscores(seasonId, currentWeek); // Do something with the boxscore (check out the repo below for examples) // ... } catch (err) { console.log('ERROR:', err); }
ts

This code is taken from my fantasy football GroupMe bot. View it on Github