ESPN’s New API (V3)

In previous seasons, we’ve enjoyed relatively easy access to ESPN’s fantasy sports data. Sometime in the last few months, ESPN altered/upgraded their AP to V3.

As I’ve tried to replicate previous analysis, old methods accessing the V2 API no longer work.

Credit Where Credit Is Due

I must also credit this reddit thread where one of the users shared the URL with end points that provide the all the available data requests for the API (as far as I can tell).

Getting the Data

To build the URL, you can use the code below by simply changing out your league ID. If you’ve done everything correctly, you should be able to use this code almost verbatim.

Of note, to make this code work for your league, you’ll need to make sure your league manager has the league set to public. If you have a private league, you’ll have to send cookies with your request. That’s another post for another day.

Final note: Each ‘view’ listed below in the ‘tail’ portion of the URL will add another chunk of information to your API request. If you do not think you need it all (such as pending transactions or another one, feel free to leave that section of the API call out of the URL).

base = ""
year = "2019"
mid = "/segments/0/leagues/"
leagueID = "12345678"
tail = "?view=mDraftDetail&view=mLiveScoring&view=mMatchupScore&view=mPendingTransactions&view=mPositionalRatings&view=mSettings&view=mTeam&view=modular&view=mNav&view=mMatchupScore"
url = paste0(base,year,mid,leagueID,tail)

Using HTTR, you’ll be able to request the API. If the status code is 200, you are good. If its 401, you are likely trying to access a private league and have been blocked. You’ll either need to have your commissioner make the league public or pass your cookies (see earlier remark about this). If you see 404, you’ve likely typed in the URL wrong as it did not even make the connection to get rejected.

ESPNGet <- httr::GET(url = url)
## [1] 200

Lastly, the following code creates a clean-ish list from the JSON returned from the API. The V2 did not return the data this cleanly - so despite the frustrations re-figuring this out, the V3 appears to be an improvement.

ESPNRaw <- rawToChar(ESPNGet$content)
ESPNFromJSON <- jsonlite::fromJSON(ESPNRaw)

Parse The Data

Now that we have the data, through trial and error I am able to extract what I would like. There is a lot more data in here which you can discover by experimenting with the JSON.

Below, I extract a tibble that tracks the wins and losses for each team. As of the writing of this post, our season has not yet started.


TeamRecords =
    location = ESPNFromJSON$teams$location,
    nickname = ESPNFromJSON$teams$nickname,
    teamId = ESPNFromJSON$teams$id,
    losses = ESPNFromJSON$teams$record$overall$losses,
    wins = ESPNFromJSON$teams$record$overall$wins
  ) %>%
  unite(Team, c(location, nickname), sep = " ")
Team teamId losses wins
’R’m Chair Quarterback 1 9 4
Philly Chapmaniacs 2 4 9
The Plainsmen 3 7 6
The OBJective Functions 4 7 6
Analysis Paralysis 5 9 4
Team Ward 6 9 4
Compute This! 7 2 11
The Chief 8 5 8
Dallas The boys 9 8 5
Palindrome Tikkit 10 5 8

The tibble below extracts the schedule, points scored in each game, and joins the team names from the previous tibble.

Schedule =
    winner = ESPNFromJSON$schedule$winner,
    Week = ESPNFromJSON$schedule$matchupPeriodId,
    AwayTeam = ESPNFromJSON$schedule$away$teamId,
    AwayPoints = ESPNFromJSON$schedule$away$totalPoints,
    HomeTeam = ESPNFromJSON$schedule$home$teamId,
    HomePoints = ESPNFromJSON$schedule$away$totalPoints
  ) %>%
  left_join(TeamRecords %>% select(teamId, Team), by = c("AwayTeam" = "teamId")) %>%
  select(-AwayTeam) %>%
  rename(AwayTeam = Team) %>%
  left_join(TeamRecords %>% select(teamId, Team), by = c("HomeTeam" = "teamId")) %>%
  select(-HomeTeam) %>%
  rename(HomeTeam = Team) 

Future Posts

I plan on several future posts from here that include…

  • Analyzing the league
  • A walk through of cookies for private leagues
  • How to use reticulate with Python and purrr to access the API.