Skip to contents

ESPN API - An Adventure Into Uncharted Territory

Accessing the ESPN Fantasy API is a bit of an adventure into an undocumented abyss. This vignette will give you a bit of a rundown on how to use espn_getendpoint and the lower-level espn_getendpoint_raw, talk about the x-fantasy-filter, and detail some of the known view parameters that could be helpful.

Known Endpoints

The ESPN Fantasy API is typically accessed from two endpoints:

https://lm-api-reads.fantasy.espn.com/apis/v3/games/ffl/seasons/{season}/segments/0/leagues/{league_id} # for 2018 onward
https://lm-api-reads.fantasy.espn.com/apis/v3/games/ffl/leagueHistory/{league_id}?seasonId={season} # for 2017 or earlier

Here’s a non-exhaustive list of view endpoints that I know of:

  • mTeam
  • mMatchup
  • mRoster
  • mSettings
  • mBoxscore
  • mMatchupScore
  • kona_player_info
  • player_wl
  • mSchedule
  • mScoreboard

Please note that calling various combinations of these views at once can often return different results than calling them separately.

A good way to stumble on these endpoints is to load Developer Tools in your browser, go to the Network tab, and then interact with the fantasy.espn.com site to see what API requests the main page is making.

Alternatively, you can consult the source code of other API packages (including those in other languages) which might give you a bit of a better idea of what’s possible!

Using espn_getendpoint

ESPN’s API is mostly structured as making requests of different views against the main league endpoint.

For example, https://lm-api-reads.fantasy.espn.com/apis/v3/games/ffl/seasons/2020/segments/0/leagues/899513?view=mDraftDetail

will pull up draft details for the 2020 league ID 899513. espn_getendpoint helps facilitate this request by allowing you to instead write:

conn <- espn_connect(season = 2020, league_id = 899513)

draft_details <- espn_getendpoint(conn, view = "mDraftDetail")

draft_details
#> <ESPN - GET - Success: (200) OK>
#> QUERY: <https://lm-api-reads.fantasy.espn.com/apis/v3/games/ffl/seasons/2020/segments/0/leagues/899513?view=mDraftDetail>
#> List of 8
#>  $ draftDetail    :List of 4
#>  $ gameId         : int 1
#>  $ id             : int 899513
#>  $ scoringPeriodId: int 18
#>  $ seasonId       : int 2020
#>  $ segmentId      : int 0
#>  $ settings       :List of 1
#>  $ status         :List of 21

This will automatically pass in the league ID, season, and authentication cookies (if used) from the conn object and place it into the request.

You can also use the lower-level equivalent, espn_getendpoint_raw, which does not build the URL from the conn object but still uses the conn object to pass along any authentication cookies:

draft_details_raw <- espn_getendpoint_raw(
  conn,
  "https://lm-api-reads.fantasy.espn.com/apis/v3/games/ffl/seasons/2020/segments/0/leagues/899513?view=mDraftDetail")

draft_details_raw
#> <ESPN - GET - Success: (200) OK>
#> QUERY: <https://lm-api-reads.fantasy.espn.com/apis/v3/games/ffl/seasons/2020/segments/0/leagues/899513?view=mDraftDetail>
#> List of 8
#>  $ draftDetail    :List of 4
#>  $ gameId         : int 1
#>  $ id             : int 899513
#>  $ scoringPeriodId: int 18
#>  $ seasonId       : int 2020
#>  $ segmentId      : int 0
#>  $ settings       :List of 1
#>  $ status         :List of 21

These are equivalent requests. One reason to use the “raw” version is to be able to pass multiple view parameters such as view=mDraftDetail&view=mSettings as the query, which is not possible with the main endpoint function because it only accepts one option for the view http query.

X-Fantasy-Filter

Many of the endpoints are also affected by a JSON header, X-Fantasy-Filter, which can filter/sort/limit (or remove limits) from the API response. Here is an example of how to build up a valid JSON x-fantasy-filter, sourced from the code for ff_playerscores:


xff <- list(players = list(
  limit = 5,
  sortPercOwned = 
    list(sortAsc = FALSE,
         sortPriority = 1),
  filterStatsForTopScoringPeriodIDs = 
    list(value = 2,
         additionalValue = c(paste0("00", conn$season)))
  )) %>%
  jsonlite::toJSON(auto_unbox = TRUE)

xff
#> {"players":{"limit":5,"sortPercOwned":{"sortAsc":false,"sortPriority":1},"filterStatsForTopScoringPeriodIDs":{"value":2,"additionalValue":"002020"}}}

This JSON limits the total responses to 5, filters the “statIDs” returned to just the ones prefixed by “00”, and sorts the whole thing by percent owned, descending.

I’m not exactly clear on what all of the options for x-fantasy-filter are, but you can use it to emulate what’s happening in the request on fantasy.espn.com.

Both the espn_getendpoint and espn_getendpoint_raw functions can accept x-fantasy-filters. espn_getendpoint has an x_fantasy_filterargument that takes the JSON object created above, while espn_getendpoint_raw requires that the object is converted into an HTTP header first.

Examples here:

player_scores <- espn_getendpoint(conn, view = "kona_player_info", x_fantasy_filter = xff)

player_scores_2 <- espn_getendpoint_raw(
  conn,
  "https://lm-api-reads.fantasy.espn.com/apis/v3/games/ffl/seasons/2020/segments/0/leagues/899513?view=kona_player_info",
  httr::add_headers(`X-Fantasy-Filter` = xff))

Other ESPN API resources

Kiernan Nichols’s fflr R package is an R package available on CRAN that is built specifically for ESPN API access. As of this writing (2021-03-03) it only supports public leagues, and has a few style differences as a light-weight/lower-dependency package.

Many of the API endpoints are being researched in other languages and you might be able to draw inspiration on what’s possible by checking them out:

  • Christian Wendt’s espn-api Python package was incredibly helpful in discovering the known and documented API endpoints.

  • Mike Kreiser’s ESPN-Fantasy-Football-API is a well-documented JS client.

  • Steven Morse also has several great blog posts on using the API, mostly accessed via Python.