Add Python examples for FPL teams, league standings and transfers

This commit is contained in:
PinkEyedOrphan committed 2026-10-24 11:05:49 +01:00
commit a9521479c0
4 files changed
+304

No files matched your search

+74
View File
@@ -0,0 +1,74 @@
# Fantasy Premier League API: three Python examples
These standalone examples use Python 3's standard library. No packages, API key,
or login are needed for these public GET requests.
## Run the examples
Open a terminal in this folder, then run:
```sh
python3 team.py
python3 league.py
python3 transfers.py
```
`team.py` defaults to fantasy team **2134**. It prints the manager, overall points
and rank, then the latest published gameweek's squad, including captain and bench.
It joins player IDs from the picks response with names, clubs and positions from
the bootstrap response.
`league.py` defaults to classic league **37803**, **Special Teams, Special Players**.
It prints the first page of standings with team names, managers, gameweek points,
total points and entry IDs. You can pass an entry ID to the team script.
`transfers.py` defaults to fantasy team **2134**. It prints public transfer history,
newest first, with UTC timestamps, gameweeks, players sold and bought, and their
transfer prices in millions of pounds. Player IDs are joined to bootstrap data
to display readable names.
```sh
python3 team.py --team-id 2134 --gameweek 7
python3 league.py --league-id 37803 --page 2
python3 league.py --league-id 37803 --all-pages
python3 transfers.py --team-id 2134 --gameweek 7
```
`--all-pages` starts at `--page` (page 1 by default). Run any script with
`--help` for its options.
## What the API calls demonstrate
Each script builds a URL, sends an HTTP GET with `urllib.request.urlopen`, parses
the JSON using `json.load`, and reads fields from Python dictionaries and lists.
The small request helper is repeated to keep each example self-contained.
| Endpoint, relative to `https://fantasy.premierleague.com/api/` | Purpose |
| --- | --- |
| `entry/2134/` | Fantasy team and manager summary |
| `entry/2134/event/7/picks/` | Squad selection for gameweek 7 |
| `entry/2134/transfers/` | Public transfer history for the current season |
| `bootstrap-static/` | Player, club, position and gameweek reference data |
| `leagues-classic/37803/standings/?page_standings=1` | One page of classic league standings |
Try opening the [team JSON](https://fantasy.premierleague.com/api/entry/2134/)
or [league JSON](https://fantasy.premierleague.com/api/leagues-classic/37803/standings/)
in your browser to compare the raw response with the printed output.
## Useful details
- A fantasy team ID is an **entry ID**, not a Premier League club ID or player ID.
- Gameweek selections are public after the deadline. This does not show a
manager's unpublished changes for the next deadline. The role column describes
the selected lineup; it does not apply automatic substitutions or chip scoring.
- The league example is for **classic** leagues, not head-to-head leagues.
- The transfers example shows only the history published by the public API.
Transfers for an upcoming deadline may not be public yet. Prices are the sale
and purchase prices recorded for each transfer, not current player prices;
they do not indicate transfer points deductions.
- Standings are paginated; `has_next` tells the script whether another page exists.
Newly joined entries may not appear in the ranked standings yet.
- Data and IDs are season-specific. These public website endpoints can change;
use the response's league name to confirm the intended league after a reset.
- Requests have a 20-second timeout. HTTP/network errors produce a message and a
nonzero exit status. Avoid repeatedly polling the service in a tight loop.
+71
View File
@@ -0,0 +1,71 @@
"""Print classic mini-league standings, including optional pagination."""
import argparse
import json
import sys
from urllib.error import URLError
from urllib.request import Request, urlopen
BASE_URL = "https://fantasy.premierleague.com/api/"
def get_json(path):
"""Send an HTTP GET request and turn the JSON response into a Python dict."""
request = Request(
BASE_URL + path,
headers={"User-Agent": "FPL-API-Demo/1.0", "Accept": "application/json"},
)
with urlopen(request, timeout=20) as response:
return json.load(response)
def positive_int(value):
number = int(value)
if number < 1:
raise argparse.ArgumentTypeError("must be a positive integer")
return number
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--league-id", type=positive_int, default=37803)
parser.add_argument("--page", type=positive_int, default=1)
parser.add_argument(
"--all-pages", action="store_true", help="fetch all pages from --page onwards"
)
args = parser.parse_args()
page = args.page
while True:
# This example uses classic scoring; head-to-head leagues use other endpoints.
data = get_json(
f"leagues-classic/{args.league_id}/standings/?page_standings={page}"
)
if page == args.page:
print(f"League: {data['league']['name']} (ID {args.league_id})")
print(f"\nPage {page}")
print(f"{'Rank':<6} {'Team':<30} {'Manager':<26} {'GW':>5} {'Total':>7} {'Entry ID':>10}")
standings = data["standings"]
for entry in standings["results"]:
print(
f"{entry['rank']:<6} {entry['entry_name']:<30} "
f"{entry['player_name']:<26} {entry['event_total']:>5} "
f"{entry['total']:>7} {entry['entry']:>10}"
)
if not standings["results"]:
print("No ranked entries on this page.")
if not standings["has_next"]:
break
if not args.all_pages:
print(f"\nMore results available: use --page {page + 1} or --all-pages.")
break
page += 1
if __name__ == "__main__":
try:
main()
except (URLError, TimeoutError, OSError, ValueError, KeyError) as exc:
print(f"Unable to load league data: {exc}", file=sys.stderr)
print("Check the classic league ID and connection. The API may also be unavailable.", file=sys.stderr)
sys.exit(1)
+84
View File
@@ -0,0 +1,84 @@
"""Show an FPL team's summary and published gameweek squad (no login needed)."""
import argparse
import json
import sys
from urllib.error import URLError
from urllib.request import Request, urlopen
BASE_URL = "https://fantasy.premierleague.com/api/"
def get_json(path):
"""Send an HTTP GET request and turn the JSON response into a Python dict."""
request = Request(
BASE_URL + path,
headers={"User-Agent": "FPL-API-Demo/1.0", "Accept": "application/json"},
)
with urlopen(request, timeout=20) as response:
return json.load(response)
def positive_int(value):
number = int(value)
if number < 1:
raise argparse.ArgumentTypeError("must be a positive integer")
return number
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--team-id", type=positive_int, default=2134)
parser.add_argument("--gameweek", type=positive_int, choices=range(1, 39))
args = parser.parse_args()
# An entry is a manager's fantasy team, rather than a Premier League club.
team = get_json(f"entry/{args.team_id}/")
print(f"Team: {team['name']} (entry {args.team_id})")
print(f"Manager: {team['player_first_name']} {team['player_last_name']}")
print(f"Overall points: {team['summary_overall_points']}")
print(f"Overall rank: {team['summary_overall_rank']}")
gameweek = args.gameweek or team.get("current_event")
if not gameweek:
print("No gameweek squad is available yet.")
return
# Picks contain player IDs. Join them to bootstrap data to get readable names.
picks = get_json(f"entry/{args.team_id}/event/{gameweek}/picks/")
bootstrap = get_json("bootstrap-static/")
players = {player["id"]: player for player in bootstrap["elements"]}
clubs = {club["id"]: club["short_name"] for club in bootstrap["teams"]}
positions = {
position["id"]: position["singular_name_short"]
for position in bootstrap["element_types"]
}
print(f"\nGameweek {gameweek} squad (published at the deadline):")
print(f"{'Slot':<6} {'Player':<24} {'Club':<6} {'Pos':<5} Role")
for pick in sorted(picks["picks"], key=lambda item: item["position"]):
player = players[pick["element"]]
role = "Starter" if pick["position"] <= 11 else "Bench"
if pick["is_captain"]:
role += " (captain)"
elif pick["is_vice_captain"]:
role += " (vice captain)"
print(
f"{pick['position']:<6} {player['web_name']:<24} "
f"{clubs[player['team']]:<6} {positions[player['element_type']]:<5} {role}"
)
if picks.get("active_chip"):
print(f"Active chip: {picks['active_chip']}")
if __name__ == "__main__":
try:
main()
except (URLError, TimeoutError, OSError, ValueError, KeyError) as exc:
print(f"Unable to load team data: {exc}", file=sys.stderr)
print(
"Check the team ID and connection. Gameweek picks are available only "
"after that gameweek's deadline. The API may also be unavailable.",
file=sys.stderr,
)
sys.exit(1)
+75
View File
@@ -0,0 +1,75 @@
"""Show an FPL team's public transfer history, newest first (no login needed)."""
import argparse
import json
import sys
from datetime import datetime
from urllib.error import URLError
from urllib.request import Request, urlopen
BASE_URL = "https://fantasy.premierleague.com/api/"
def get_json(path):
"""Send an HTTP GET request and parse the JSON into Python objects."""
request = Request(
BASE_URL + path,
headers={"User-Agent": "FPL-API-Demo/1.0", "Accept": "application/json"},
)
with urlopen(request, timeout=20) as response:
return json.load(response)
def positive_int(value):
number = int(value)
if number < 1:
raise argparse.ArgumentTypeError("must be a positive integer")
return number
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--team-id", type=positive_int, default=2134)
parser.add_argument("--gameweek", type=positive_int, choices=range(1, 39))
args = parser.parse_args()
# Public history contains completed transfers released by the API.
transfers = get_json(f"entry/{args.team_id}/transfers/")
if args.gameweek is not None:
transfers = [item for item in transfers if item["event"] == args.gameweek]
transfers.sort(key=lambda item: item["time"], reverse=True)
print(f"Transfer history for team {args.team_id}")
if args.gameweek is not None:
print(f"Gameweek: {args.gameweek}")
print(f"Transfers: {len(transfers)}")
if not transfers:
print("No public transfers found for this selection.")
return
# Transfer records contain IDs; bootstrap data supplies readable player names.
bootstrap = get_json("bootstrap-static/")
players = {player["id"]: player["web_name"] for player in bootstrap["elements"]}
print(f"\n{'Date/time (UTC)':<20} {'GW':>3} {'Player out':<24} {'Sold':>8} {'Player in':<24} {'Bought':>8}")
for transfer in transfers:
timestamp = datetime.fromisoformat(transfer["time"].replace("Z", "+00:00"))
date = timestamp.strftime("%Y-%m-%d %H:%M:%S")
player_out = players.get(transfer["element_out"], f"ID {transfer['element_out']}")
player_in = players.get(transfer["element_in"], f"ID {transfer['element_in']}")
# API prices are in tenths of a million pounds: 61 means £6.1m.
sold = f"£{transfer['element_out_cost'] / 10:.1f}m"
bought = f"£{transfer['element_in_cost'] / 10:.1f}m"
print(
f"{date:<20} {transfer['event']:>3} {player_out:<24} {sold:>8} "
f"{player_in:<24} {bought:>8}"
)
if __name__ == "__main__":
try:
main()
except (URLError, TimeoutError, OSError, ValueError, KeyError) as exc:
print(f"Unable to load transfer data: {exc}", file=sys.stderr)
print("Check the team ID and connection. The API may also be unavailable.", file=sys.stderr)
sys.exit(1)