FPL API: Fantasy Premier League Endpoints, Fields and Code
The JSON feed behind the official fantasy game, no key needed. We called every endpoint below.
Last updated:
What is the FPL API?
The FPL API is the JSON feed behind Fantasy Premier League, the Premier League's official fantasy game at fantasy.premierleague.com. It's free, and the public endpoints need no API key or login. The data is the league's own, but the API is undocumented: the Premier League publishes no reference, no changelog and no rate limit for it. Every path on this page comes from our own calls on 2 November 2026.
Developers build price trackers and mini-league dashboards on it. One call to bootstrap-static returns every player, club and gameweek of the 2026/27 season. For live scores with a match clock, the ESPN API fits better, and the soccer odds API guide covers betting lines for the same fixtures.
Base URL and Trailing Slashes
Every endpoint sits under one host, and every path ends in a slash.
https://fantasy.premierleague.com/api/{endpoint}/
Drop the slash and the server answers 301 with an empty body and a Location header that points at the slash version. Eight of the nine paths we called without a slash did this. The query string survives: fixtures?event=10 went to fixtures/?event=10.
Python requests and JavaScript fetch follow the 301 on their own. Plain curl needs -L. The odd one out, leagues-classic/314/standings, answered 200 both ways.
What It Covers
The feed covers one competition, the Premier League, for the current fantasy season. On 2 November 2026 that was 2026/27: 20 clubs, 670 players and 380 fixtures from 21 August 2026 to 30 May 2027. Gameweek 9 was current. Gameweek 10 was next, with its deadline at 18:30 UTC on 6 November.
- Players: price, points, form, ownership, injury news and expected goals.
- Fixtures: kickoff, score, a difficulty rating from 2 to 5 per side, and goals, assists, cards, saves and bonus.
- Managers: any public team's points, rank, value and weekly picks.
- Past seasons: season totals per player, back to 2022/23 for Haaland.
Two gaps caught us out. The teams array has played, win and points fields, yet all three read 0 for every club, so build the league table from /api/fixtures/. A fixture has a score and minutes but no clock or commentary. ESPN's soccer/eng.1/scoreboard matched FPL fixture 89 (Newcastle 0, Everton 1 on 2 November) and added a clock in status.displayClock, so the ESPN API can be the live-score side of an FPL app. For older seasons, our free sports datasets page lists football-data.co.uk results with closing odds and StatsBomb open data.
Key Endpoints
All paths are GET requests under https://fantasy.premierleague.com/api/, and each one answered 200 on 2 November 2026.
| Endpoint | What it returns | Example |
|---|---|---|
bootstrap-static/ | Players, clubs, gameweeks, chips and game rules | bootstrap-static/ |
fixtures/ | All 380 fixtures with scores and match stats | fixtures/?event=10 |
element-summary/{player_id}/ | One player's match history, remaining fixtures and past seasons | element-summary/411/ |
event/{gw}/live/ | Points and stats for every player in one gameweek | event/9/live/ |
event-status/ | Whether a gameweek's points are provisional and bonus is in | event-status/ |
dream-team/{gw}/ | The top-scoring XI of a gameweek and its best player | dream-team/8/ |
team/set-piece-notes/ | Set-piece notes per club | team/set-piece-notes/ |
entry/{team_id}/ | A manager's team: points, rank, value, leagues | entry/895045/ |
entry/{team_id}/history/ | That team's record per gameweek and the chips it played | entry/895045/history/ |
entry/{team_id}/event/{gw}/picks/ | The 15 picks, captain and bench order | entry/895045/event/9/picks/ |
leagues-classic/{league_id}/standings/ | A classic league table, 50 rows a page | leagues-classic/314/standings/ |
Sizes on the day: bootstrap-static/ 1.8 MB, event/9/live/ 475 KB, fixtures/ 323 KB and element-summary/411/ 17 KB.
The bootstrap-static Endpoint
Most FPL projects start with bootstrap-static. It holds the reference data that the other endpoints point at by id, and our call returned 1,790,928 bytes of it.
https://fantasy.premierleague.com/api/bootstrap-static/
| Field | Type | What it holds |
|---|---|---|
events | array (38) | Gameweeks with deadline_time, is_current and finished |
teams | array (20) | Clubs with id, name and short_name |
elements | array (670) | Every player (table below) |
element_types | array (4) | Positions: 1 Goalkeeper, 2 Defender, 3 Midfielder, 4 Forward |
element_stats | array (26) | Labels for the stat fields, such as "Minutes played" |
chips | array (8) | Two each of wildcard, freehit, bboost and 3xc, with start and stop gameweeks |
phases | array (11) | The overall season plus one phase per month |
game_settings, game_config | object | Squad rules and the scoring table |
total_players | integer | Registered managers: 11,228,020 |
Player fields in elements
Each player has 109 fields. These are the ones most apps need, from Erling Haaland's row:
| Field | Type | What it holds |
|---|---|---|
id | integer | Player id used by every other endpoint: 411 |
web_name | string | Display name: "Haaland" |
team | integer | Club id from teams: 15 (Man City) |
element_type | integer | Position id from element_types: 4 (Forward) |
now_cost | integer | Price in tenths of a million pounds: 158 is £15.8m |
total_points | integer | Season points: 68 |
form | string | Recent points per game: "7.2" |
selected_by_percent | string | Share of managers who own him: "79.0" |
expected_goals | string | Season expected goals: "6.56" |
status | string | One letter: a, d, i, s or u; a for Haaland |
news | string | Injury or transfer note, such as "Suspended until 29 Nov" |
chance_of_playing_next_round | integer or null | 100 for Haaland, null for 307 players |
Five of those numbers arrive as strings: form, selected_by_percent, expected_goals, ep_next and points_per_game. Convert them with float() before you sort. The price unit checks out against game_settings, where squad_total_spend is 1000 and ui_currency_multiplier is 10: the game's £100.0m budget.
On our call, d came with news such as "75% chance of playing", i with "Unknown return date", s with a suspension and u with a transfer to another club.
Parameters and Gameweek Filters
Most filtering happens in the path, through a gameweek, player or team id.
| Parameter | What it does | Example |
|---|---|---|
event | Fixtures of one gameweek: 10 rows for Gameweek 10 | fixtures/?event=10 |
future=1 | Only unplayed fixtures: 290 rows, Gameweeks 10 to 38 | fixtures/?future=1 |
page_standings | A page of a classic league; page 2 held ranks 51 to 100 | leagues-classic/314/standings/?page_standings=2 |
{gw} | Gameweek number, 1 to 38 | event/9/live/ |
{player_id} | The id from elements | element-summary/411/ |
{team_id} | A manager's entry id from a standings row | entry/895045/ |
Bad values don't always fail. fixtures/?event=39 answered 200 with [], and page_standings=0 returned an empty results list labelled page 1. An unknown parameter is ignored: bootstrap-static/?foo=1 gave back the full 1.8 MB file.
Fixture fields and the finished flags
Each fixture has team_h and team_a (club ids), scores that stay null before kickoff, kickoff_time in UTC, three status flags and a stats array. The first Gameweek 10 row was fixture 94, Everton against Coventry City.
On 2 November all ten Gameweek 9 matches had started and finished_provisional set to true, but finished was false on every one. event-status/ agreed: all three match days read "points": "p" with bonus_added: false. We read that as provisional points until finished turns true, though no docs exist to confirm it.
Manager Teams and Leagues
Every FPL team has a public entry id. The overall league, id 314, lists teams 50 to a page, and each row's entry field is that id. We used 895045, rank 1 on 2 November with 743 points.
| Field | Type | What it holds |
|---|---|---|
standings.results[].entry | integer | Team id: 895045 |
standings.results[].total | integer | Season points: 743 |
summary_overall_rank | integer | Overall rank, from entry/{team_id}/: 1 |
last_deadline_value, last_deadline_bank | integer | Squad value 1030 and bank 24, in tenths: £103.0m and £2.4m |
current | array | From history/: one row per gameweek with points, overall_rank and points_on_bench |
chips | array | From history/: chips played, such as bboost in Gameweek 1 |
picks | array | From picks/: 15 rows with element, position, multiplier and is_captain |
In the Gameweek 9 picks, the captain (player 411) had a multiplier of 2, the other starters 1, and the four bench slots 0. The entry/ response also returns the manager's first name, last name and region in player_first_name, player_last_name and player_region_name, and standings rows carry player_name. That is personal data, so store only the ids and points your app needs.
Code Examples
Each sample ran on 2 November 2026 with default headers and no key.
Top five players by points
import requests
url = 'https://fantasy.premierleague.com/api/bootstrap-static/'
data = requests.get(url, timeout=30).json()
teams = {t['id']: t['short_name'] for t in data['teams']}
top = sorted(data['elements'], key=lambda p: p['total_points'], reverse=True)[:5]
for p in top:
print(p['web_name'], teams[p['team']], p['now_cost'] / 10, p['total_points'])
Live points for one gameweek
import requests
gw = 9
base = 'https://fantasy.premierleague.com/api'
players = requests.get(f'{base}/bootstrap-static/', timeout=30).json()['elements']
names = {p['id']: p['web_name'] for p in players}
live = requests.get(f'{base}/event/{gw}/live/', timeout=30).json()['elements']
for row in sorted(live, key=lambda r: r['stats']['total_points'], reverse=True)[:5]:
print(names[row['id']], row['stats']['total_points'], row['stats']['minutes'])
A manager's captain and points
import requests
team_id, gw = 895045, 9
url = f'https://fantasy.premierleague.com/api/entry/{team_id}/event/{gw}/picks/'
data = requests.get(url, timeout=30).json()
print('Gameweek points:', data['entry_history']['points'])
captain = next(p for p in data['picks'] if p['is_captain'])
print('Captain id:', captain['element'], 'multiplier:', captain['multiplier'])
Fixtures for one gameweek
curl -s 'https://fantasy.premierleague.com/api/fixtures/?event=10'
# Without the trailing slash, add -L to follow the 301
curl -sL 'https://fantasy.premierleague.com/api/fixtures?event=10'
A player's last three matches
// Node 18 or later; save as fpl.mjs and run: node fpl.mjs
const url = 'https://fantasy.premierleague.com/api/element-summary/411/';
const data = await fetch(url).then(r => r.json());
for (const game of data.history.slice(-3)) {
console.log(game.round, game.kickoff_time, game.total_points, game.expected_goals);
}
console.log('Next:', data.fixtures[0].event_name, data.fixtures[0].kickoff_time);
Run the JavaScript in Node or on your server. No response we checked carried an Access-Control-Allow-Origin header, so a browser blocks a page on your own domain from reading it.
FPL API Errors and Empty Responses
404 with a JSON detail
An unknown id returns 404 with a short JSON body that names what is missing. element-summary/99999/ and element-summary/0/ both gave {"detail":"No Element matches the given query."}. entry/999999999/ named the missing Entry, and event/39/live/ the missing Event.
For a gameweek not yet played, dream-team/10/ and entry/895045/event/10/picks/ returned 404 with {"detail":"Not found."}, and fixtures/?event=abc gave the same body.
404 with an HTML page
A path the API doesn't know returns a 179-byte HTML page titled "Not Found", so check the status before you call response.json(). We got it for element-summary/abc/ and for /api/set-piece-notes/, since the notes live under team/.
200 with an empty list
event/10/live/ and event/38/live/ answered 200 with {"elements": []} because neither gameweek had been played on 2 November. fixtures/?event=39 also gave 200 and [], so check that a gameweek number is 1 to 38 before you call.
301 Moved Permanently
You left off the trailing slash. Add it, or let your client follow the redirect (details above).
Limitations and Terms
Personal use only
The Premier League's site terms let you "download and print material from the Website or App as is reasonable for your own private and personal use". They forbid use "for commercial purposes" and, without the league's "prior written approval", "creating a database (electronic or otherwise) that includes material downloaded or otherwise obtained" from the site or app. A personal FPL tool fits those terms. A paid product or a stored FPL database needs approval first.
We quote the terms at premierleague.com/en/terms-and-conditions (undated, fetched 2 November 2026). The game's own terms sit at fantasy.premierleague.com/help/terms. That page loads its text with JavaScript and our fetch got only the shell, so read it in a browser before you ship.
- No documentation: nothing commits the league to keeping a path or field next season.
- No published rate limit: no
X-RateLimitheaders, and about 70 calls on 2 November, most a second apart, got no 429. - Cached copies:
bootstrap-staticcame withcache-control: max-age=300and anageof 296 seconds, so polling it faster than every five minutes can return the same copy. Other endpoints sentno-storebut still came from the cache, up to 592 seconds old. - One season:
bootstrap-staticholds 2026/27 only. Past seasons survive as totals inhistory_past, keyed byelement_code(the player'scode, 223094 for Haaland).
When your app needs a commercial licence, an SLA or leagues beyond the Premier League, compare licensed sports data providers.
Community Resources
Both repos answered 200 on 2 November 2026, and the league runs neither.
- vaastav/Fantasy-Premier-League on GitHub: season CSVs of player and gameweek data from 2016-17 to 2026-27. The README says weekly updates stopped after 2024-25, and the repo now gets three updates a season. Its MIT licence covers the code; the data came from FPL.
- amosbastian/fpl on GitHub: an asynchronous Python wrapper, installed with
pip install fpl. The latest PyPI release is 0.6.35 from 14 August 2023, and the last push to the repo was in July 2024. We didn't test it against the 2026/27 feed.
Frequently Asked Questions
fantasy.premierleague.com. The league publishes no documentation or support for the API, so every path on this page comes from our own calls.entry/{team_id}/, answered with the client's default headers. curl, Python requests and Node fetch all got 200 with nothing added. We tested public endpoints only, so this page covers no login.bootstrap-static is cached for 300 seconds, so fetch it every five minutes at most.https://fantasy.premierleague.com/api/bootstrap-static/ and search the elements list by web_name. The id field goes into element-summary/{player_id}/. now_cost is in tenths of a million, so Haaland's 158 means £15.8m.is_current set to true in bootstrap-static, then call https://fantasy.premierleague.com/api/event/{gw}/live/. Each row has a player id and a stats object with total_points and minutes, and Gameweek 9 returned 670 rows.Need Licensed Soccer Data?
The FPL API suits personal fantasy tools. Commercial apps need a licence, an SLA and support. Compare providers in our directory.