Skip to contents

The {fantasypl} package is an R package that provides a convenient way to interact with the Fantasy Premier League API endpoints. It leverages the {httr2} package for making HTTP requests and integrates seamlessly with tidyverse packages for data manipulation and analysis.

Installation

You can install the development version of the {fantasypl} package using the {pak} package:

# install.packages("pak")
pak::pak("danieloc1989/fantasypl")

As of this time, there is no intention of releasing the package on CRAN.

Overview

The package offers functions to retrieve various types of information from the Fantasy Premier League API endpoints, such as team data, player statistics, fixtures, and more.

Data Formatting

The data retrieved from the API endpoints are often not in a convenient tabular format. For instance, the ‘bootstrap-static’ endpoint (general information in FPL) returns a list with a mix of dataframes, lists, and other objects:

# API URL
fpl_url <- "https://fantasy.premierleague.com/api"
 
# Retrieving bootstrap-static endpoint data
general_info <-
  httr2::request(fpl_url) |> 
  httr2::req_url_path_append("bootstrap-static") |> 
  httr2::req_perform() |> 
  httr2::resp_body_string() |> 
  jsonlite::fromJSON()

# First level structure of the bootstrap-static endpoint output
str(general_info, max.level = 1)
#> List of 8
#>  $ events       :'data.frame':   38 obs. of  23 variables:
#>  $ game_settings:List of 28
#>  $ phases       :'data.frame':   11 obs. of  4 variables:
#>  $ teams        :'data.frame':   20 obs. of  21 variables:
#>  $ total_players: int 9830943
#>  $ elements     :'data.frame':   714 obs. of  88 variables:
#>  $ element_stats:'data.frame':   22 obs. of  2 variables:
#>  $ element_types:'data.frame':   4 obs. of  11 variables:

To improve the usability of the data, the {fantasypl} package reformats and organises the data into logical tabular structures.

Example: Team Data

One example is the team data obtained from the ‘teams’ dataframe in the ‘bootstrap-static’ endpoint. By default, the ordering of the columns in the raw data does not make sense:

# Original ordering of columns in teams dataframe
general_info$teams |> 
  str()
#> 'data.frame':    20 obs. of  21 variables:
#>  $ code                 : int  3 7 91 94 36 90 8 31 11 54 ...
#>  $ draw                 : int  0 0 0 0 0 0 0 0 0 0 ...
#>  $ form                 : logi  NA NA NA NA NA NA ...
#>  $ id                   : int  1 2 3 4 5 6 7 8 9 10 ...
#>  $ loss                 : int  0 0 0 0 0 0 0 0 0 0 ...
#>  $ name                 : chr  "Arsenal" "Aston Villa" "Bournemouth" "Brentford" ...
#>  $ played               : int  0 0 0 0 0 0 0 0 0 0 ...
#>  $ points               : int  0 0 0 0 0 0 0 0 0 0 ...
#>  $ position             : int  0 0 0 0 0 0 0 0 0 0 ...
#>  $ short_name           : chr  "ARS" "AVL" "BOU" "BRE" ...
#>  $ strength             : int  4 3 3 3 3 2 3 3 3 3 ...
#>  $ team_division        : logi  NA NA NA NA NA NA ...
#>  $ unavailable          : logi  FALSE FALSE FALSE FALSE FALSE FALSE ...
#>  $ win                  : int  0 0 0 0 0 0 0 0 0 0 ...
#>  $ strength_overall_home: int  1230 1115 1060 1125 1165 1060 1115 1100 1075 1095 ...
#>  $ strength_overall_away: int  1285 1175 1095 1205 1210 1080 1160 1100 1100 1100 ...
#>  $ strength_attack_home : int  1250 1130 1050 1120 1120 1060 1130 1140 1070 1090 ...
#>  $ strength_attack_away : int  1250 1190 1100 1220 1200 1080 1210 1170 1120 1090 ...
#>  $ strength_defence_home: int  1210 1100 1060 1130 1210 1060 1100 1080 1080 1100 ...
#>  $ strength_defence_away: int  1320 1160 1090 1190 1240 1080 1110 1085 1080 1140 ...
#>  $ pulse_id             : int  1 2 127 130 131 43 4 6 7 34 ...

The {fantasypl} package renames some of the columns and reorganises the data for better readability:

# Improved naming and ordering of columns in teams dataframe
fpl_team_info() |> 
  str()
#> tibble [20 × 18] (S3: tbl_df/tbl/data.frame)
#>  $ team_fpl_code        : int [1:20] 3 7 91 94 36 90 8 31 11 54 ...
#>  $ team_name            : chr [1:20] "Arsenal" "Aston Villa" "Bournemouth" "Brentford" ...
#>  $ team_abb             : chr [1:20] "ARS" "AVL" "BOU" "BRE" ...
#>  $ season_id            : int [1:20] 1 2 3 4 5 6 7 8 9 10 ...
#>  $ form                 : logi [1:20] NA NA NA NA NA NA ...
#>  $ played               : int [1:20] 0 0 0 0 0 0 0 0 0 0 ...
#>  $ win                  : int [1:20] 0 0 0 0 0 0 0 0 0 0 ...
#>  $ draw                 : int [1:20] 0 0 0 0 0 0 0 0 0 0 ...
#>  $ loss                 : int [1:20] 0 0 0 0 0 0 0 0 0 0 ...
#>  $ points               : int [1:20] 0 0 0 0 0 0 0 0 0 0 ...
#>  $ league_position      : int [1:20] 0 0 0 0 0 0 0 0 0 0 ...
#>  $ strength             : int [1:20] 4 3 3 3 3 2 3 3 3 3 ...
#>  $ strength_overall_home: int [1:20] 1230 1115 1060 1125 1165 1060 1115 1100 1075 1095 ...
#>  $ strength_defence_home: int [1:20] 1210 1100 1060 1130 1210 1060 1100 1080 1080 1100 ...
#>  $ strength_attack_home : int [1:20] 1250 1130 1050 1120 1120 1060 1130 1140 1070 1090 ...
#>  $ strength_overall_away: int [1:20] 1285 1175 1095 1205 1210 1080 1160 1100 1100 1100 ...
#>  $ strength_defence_away: int [1:20] 1320 1160 1090 1190 1240 1080 1110 1085 1080 1140 ...
#>  $ strength_attack_away : int [1:20] 1250 1190 1100 1220 1200 1080 1210 1170 1120 1090 ...