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_filter
argument 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.