How to Build an FPL Dashboard with Free Tools: A Step-by-Step Tutorial
Hands-on student tutorial: build an interactive FPL dashboard with Google Sheets, free FPL APIs, and weekly visualizations for coursework and portfolios.
Build a weekly Fantasy Premier League dashboard using only free tools — a practical student tutorial
Feel overwhelmed by scattered FPL data, tight schedules, and the need for an accessible project for coursework or your portfolio? This step-by-step hands-on guide shows students and lifelong learners how to build an interactive FPL dashboard with Google Sheets, free FPL APIs, and simple visualizations that update weekly. By the end you will have a reusable dashboard you can extend for class projects, club analytics, or your portfolio.
Why this tutorial matters in 2026
Two clear trends highlight why an FPL dashboard is a valuable learning project in 2026:
- Data literacy and low-code tooling are primary skills students need for analytics roles. Google Sheets remains a universal entry point for data projects.
- Open sports data and community APIs matured through 2024–2025. The Fantasy Premier League endpoints remain freely accessible and have become the de facto source for real-time FPL stats and event data.
What you’ll build (quick overview)
- A Google Sheet that pulls FPL JSON from the official endpoints
- Parsed tables for players, teams, fixtures, and live gameweek data
- Interactive charts: top scorers, form trend, ownership heatmap, captaincy split
- Automation to refresh data weekly (trigger before each gameweek)
Tools and data sources (all free)
- Google Sheets — front end, charts, pivot tables, conditional formatting
- Google Apps Script — fetch JSON, parse nested objects, schedule updates
- FPL public API — endpoints like bootstrap-static and event live (example base: https://fantasy.premierleague.com/api/)
- Optional: BBC Sport RSS or club pages for manual team news; you can paste short notes into a Team News sheet for weekly context
High-level architecture
- Apps Script calls FPL API and writes normalized tables to separate sheets
- Sheets use QUERY, SORT, and FILTER formulas to create summary views
- Charts and conditional formatting visualize priorities for each gameweek
- Time-driven trigger runs the fetch script weekly (or more often) to refresh
Before you start — create a new Google Sheet and name tabs
Create a Sheets file and add these sheets (tabs):
- Players
- Teams
- Fixtures
- LiveGW
- Summary
- TeamNews
Step 1 — Add Apps Script to fetch the FPL JSON
The official FPL JSON endpoints are free and highly useful. The two endpoints you will use first are:
- /api/bootstrap-static/ — contains players, teams, element_types, and basic metadata
- /api/event/EV/live/ — returns live event data for a given gameweek (replace EV with event id)
Open Extensions → Apps Script and paste this script. It fetches bootstrap-static and writes the players and teams tables to your sheet. This script intentionally keeps the code simple so you can study and extend it.
function fetchFPLBootstrap() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const url = 'https://fantasy.premierleague.com/api/bootstrap-static/';
const res = UrlFetchApp.fetch(url, {muteHttpExceptions: true});
const data = JSON.parse(res.getContentText());
// Players
const players = data.elements; // array of player objects
const playerSheet = ss.getSheetByName('Players') || ss.insertSheet('Players');
playerSheet.clear();
const headers = [
'id','web_name','first_name','second_name','team','team_code','element_type',
'now_cost','total_points','points_per_game','minutes','goals_scored','assists',
'clean_sheets','form','transfers_in','selected_by_percent'
];
playerSheet.appendRow(headers);
players.forEach(p => {
playerSheet.appendRow([
p.id,p.web_name,p.first_name,p.second_name,p.team,p.team_code,p.element_type,
p.now_cost/10,p.total_points,p.points_per_game,p.minutes,p.goals_scored,p.assists,
p.clean_sheets,p.form,p.transfers_in,p.selected_by_percent
]);
});
// Teams
const teams = data.teams;
const teamSheet = ss.getSheetByName('Teams') || ss.insertSheet('Teams');
teamSheet.clear();
teamSheet.appendRow(['id','name','short_name','strength']);
teams.forEach(t => teamSheet.appendRow([t.id,t.name,t.short_name,t.strength]));
}
Save and click the play icon (authorize when prompted). After the script runs you will have populated Players and Teams.
Step 2 — Pull the current gameweek live data
To grab live event stats for a specific gameweek, add this function. It writes flattened player live stats to the LiveGW sheet.
function fetchFPLLiveGW(eventId) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const url = `https://fantasy.premierleague.com/api/event/${eventId}/live/`;
const res = UrlFetchApp.fetch(url, {muteHttpExceptions: true});
const data = JSON.parse(res.getContentText());
const liveSheet = ss.getSheetByName('LiveGW') || ss.insertSheet('LiveGW');
liveSheet.clear();
const headers = ['id','web_name','team','position','stats','minutes','total_points','event_points'];
liveSheet.appendRow(headers);
// We need player lookup to map id to name and team. Use Players sheet.
const playerMap = {};
const pSheet = ss.getSheetByName('Players');
if (pSheet) {
const rows = pSheet.getDataRange().getValues();
const colIndex = rows[0].reduce((m,h,i)=>{m[h]=i;return m;},{});
for (let i=1;i<rows.length;i++) {
const r = rows[i];
playerMap[r[colIndex['id']]] = {name: r[colIndex['web_name']], team: r[colIndex['team']]};
}
}
const elements = data.elements; // live player entries
elements.forEach(e => {
const name = playerMap[e.id] ? playerMap[e.id].name : '';
const team = playerMap[e.id] ? playerMap[e.id].team : '';
liveSheet.appendRow([e.id,name,team,e.element_type,JSON.stringify(e.stats),e.minutes,e.total_points,e.event_points]);
});
}
Run fetchFPLLiveGW and pass the current gameweek id. You can detect the current event by examining bootstrap-static events or entering it manually.
Tip — caching and rate limits
FPL endpoints are free but not infinite. Use script properties to store the last-fetch timestamp and avoid polling more than once every few minutes. For weekly automation, a single daily or pre-GW run is sufficient.
Step 3 — create summary formulas and queries
Now build your Summary sheet using native Sheets functions. Here are practical examples to paste into cells.
Top 10 scorers (by total_points)
In Summary!A1 paste:
=QUERY(Players!A2:P, "select B, H, I order by I desc limit 10", 0)
This returns web_name, now_cost, total_points for the top 10 scorers.
Players in form (filter by form > 3)
=QUERY(Players!A2:P, "select B, M, P where cast(M as number) > 3 order by cast(M as number) desc", 0)
Sparkline for a single player's points trend
Assuming you keep a historic points-per-GW row for each player in a separate sheet, use:
=SPARKLINE(FILTER(History!C2:Z, History!A2:A = "Player Name"))
If you don’t want to store full history, you can snapshot total_points weekly using an Apps Script function and append new rows to a History sheet.
Step 4 — build visualizations
Google Sheets charts are powerful for student projects. Use these visualizations for a compelling dashboard.
- Bar chart — Top 10 by total_points (use the QUERY output)
- Pie chart — Captaincy distribution (if you keep a column for chosen captain across a sample of managers)
- Line chart — Average points per gameweek across your tracked players
- Heatmap — Use conditional formatting to highlight selected_by_percent across players; set color scale so high ownership is red
- Sparklines — For each player row show last 5 GWs inline
Create charts from the Insert → Chart menu and point them to your summary ranges. Combine several charts in the Summary sheet to make an at-a-glance dashboard.
Step 5 — bring in team news and context
Numbers are necessary but team news changes decisions. For free and reliable news:
- Manually paste short notes into the TeamNews sheet for each team before the GW
- Optionally, pull BBC Sport or trusted RSS feeds using IMPORTXML for headlines, but be mindful of site terms
Practical class project approach: assign students to be the weekly news editor and paste 1–3 sentences per team. This mirrors real-world workflows and avoids scraping risks.
Step 6 — automate weekly updates with time-driven triggers
Set a trigger so fetchFPLBootstrap runs once per day and fetchFPLLiveGW runs shortly before the GW deadline.
- Open Apps Script editor → Triggers
- Create a time-driven trigger for fetchFPLBootstrap (daily)
- Create a time-driven trigger for fetchFPLLiveGW with eventId passed via a wrapper function or update a cell with current eventId
Example wrapper to call the live fetch for current event automatically:
function updateLiveForCurrentEvent() {
const url = 'https://fantasy.premierleague.com/api/bootstrap-static/';
const res = UrlFetchApp.fetch(url);
const data = JSON.parse(res.getContentText());
const events = data.events; // find next or current event
const current = events.find(e => e.is_current) || events.find(e => e.is_next);
if (current) fetchFPLLiveGW(current.id);
}
Advanced strategies and student project ideas (2026-focused)
These extensions reflect the latest trends through late 2025 and early 2026 and are great for coursework or portfolios:
- Integrate expected goals (xG) — combine free expected-goals datasets (Understat scraping or open sources) with FPL points to highlight underpriced players
- AI-assisted insights — use a local LLM or cloud LLM to generate captain suggestions based on form and fixtures. Keep this optional and demonstrate transparent reasoning
- Interactive filters — build on-sheet dropdowns for positions, teams, or price bands using Data Validation and dynamic QUERY() calls
- Classroom leaderboard — let students submit mini-teams into the sheet and calculate weekly and season-long ranks
Practical visualization recipes
Ownership vs points scatter
- Create two columns: selected_by_percent and total_points
- Insert → Chart → Scatter and select those two columns
- Add a trendline: it shows whether highly owned players also produce points
Differential checklist
Use a filter that shows players with selected_by_percent < 5 and form > 3. This highlights low-ownership players worth considering.
Troubleshooting and common pitfalls
- UrlFetchApp errors — ensure you handle muteHttpExceptions and check response codes
- JSON schema changes — when FPL changes fields, inspect bootstrap-static in your browser and update the script mappings
- Large sheets slow down — limit history retention (archive older seasons to separate files)
- Authorization scopes — the first Apps Script run requires permission to connect to external services
Grade-ready deliverables
For a class assignment or portfolio, submit these items:
- A link to the Google Sheet with sample data and charts
- Apps Script code with comments explaining each function
- A short README (in the sheet or repository) explaining data sources, refresh schedule, and how to extend
- A one-page analysis of 2–3 insights your dashboard revealed for a recent gameweek
Ethics, attribution, and reliability
Always cite your data sources. For the FPL API, include a reference note in the sheet: Data sourced from Fantasy Premier League public API (fantasy.premierleague.com). When you use external news headlines, attribute the publishers and avoid republishing full articles.
Recap — actionable checklist
- Create a Google Sheet and named tabs
- Add Apps Script and run fetchFPLBootstrap to populate Players and Teams
- Run fetchFPLLiveGW (or wrapper) to update LiveGW each GW
- Build summary queries and charts on Summary sheet
- Add team news manually or via curated RSS and schedule a student editor role
- Set time-driven triggers to automate weekly refresh
Why students should build this in 2026
This project teaches essential skills that employers seek in 2026: API integration, data cleaning, visualization, and automation. It’s low cost, high signal for portfolios, and directly applicable to sports analytics or more general data roles.
Further reading and resources
- FPL endpoints: visit the public bootstrap-static endpoint in your browser to explore the JSON
- Google Apps Script documentation for UrlFetchApp and time-driven triggers
- Understat / xG community guides (for advanced expected-goals integration)
Final notes — keep it simple, iterate
Start with the core: players, points, and a couple of charts. Iterate by adding heatmaps, captaincy data, or class leaderboards. The value of the dashboard comes from repeated weekly use and the disciplined capture of context — the TeamNews sheet makes your numbers actionable.
Call to action
Ready to build? Open a new Google Sheet and paste the Apps Script code now. If you want a starter template, export this sheet as a template for your class and share your best dashboard on GitHub or your portfolio. Share your project link with classmates or instructors and ask for feedback — then iterate for the next gameweek.
Related Reading
- SEO Audit Checklist for Domain Investors: How to Spot Hidden Traffic Potential Before You Buy
- Seasonal Car Rentals vs. Buying for Snow Sports Families: A Cost Comparison
- From Lightwood to Darkwood: Crafting Progression and Best Farming Routes in Hytale
- Spotify vs. The World: How Streaming Price Hikes Affect Fans, Artists and Independent Labels
- The Therapist’s Guide to Pocketable Tech Deals: When to Buy and What’s Worth It
Related Topics
Unknown
Contributor
Senior editor and content strategist. Writing about technology, design, and the future of digital media. Follow along for deep dives into the industry's moving parts.
Up Next
More stories handpicked for you
Fantasy Premier League (FPL) Content Kit: Templates for Weekly Team News and Stats Posts
How to Produce a Music Video Analysis Blog Post That Gets Shared
Worksheet: Close Listening and Thematic Analysis Using 'Where's My Phone?'
How to Write a Music Album Review: Use Mitski’s New Record to Teach Tone and Context
Photography Cheat Sheet: Capturing Low-Light Cocktail Shots Like a Pro
From Our Network
Trending stories across our publication group