Getting Real Data Out of the Premier League
I spent three years maintaining a dataset tracking every Premier League matchday table since 1992, and the first thing I learned is that nobody actually gives you clean structured data. The Premier League website shows current standings. ESPN and BBC show current standings. But if you want Premier League Standings History going back decades, you are either scraping a site that changes its HTML quarterly or you are buying a subscription to a sports data API that costs more than my monthly coffee budget. The most reliable free route I found involves the 11v11 website (sevenfootball.com), which keeps archived tables for every completed season. It is not a perfect source. The table sometimes misses goal difference adjustments made after the season ends due to point deductions or administrative decisions. But it is better than nothing, and the HTML is stable enough to write a simple Python scraper against using requests and BeautifulSoup. I wrote one that pulls season-by-season final tables. It takes about 45 seconds to run for all 33 completed seasons since 1992, giving you roughly 1,400 rows of data.
Accessing Premier League Standings History Through APIs
If scraping feels too fragile, there are paid APIs. Football-Data.org offers a free tier with 10 requests per minute, which covers historical tables fine if you cache the results. The endpoint you want is /competitions/PL/standings, and it returns both overall and home/away splits. The free tier limits you to data from the 2008-09 season onward, so you are still scraping for earlier decades unless you pay for the premium tier at roughly €49 per month. API-Sports (formerly API-Football) is another option. Their /leagues/history endpoint returns season metadata, and /standings gives you the table. Their free plan allows 100 requests per day, which is enough for a one-time bulk download if you spread it over two weeks. Premium is €55 monthly. I went with Football-Data.org for two years, then switched to a local scrape of the Premier League's own archive pages when their rate limits started blocking my automated tasks during off-season data refreshes. For raw CSV downloads, Football Tables (football-tables.com) publishes downloadable spreadsheets for each season. There is no API, but the URLs follow a predictable pattern: https://www.football-tables.com/tables/premier-league/2023-2024. You can loop through those in a script and parse the table HTML. This method has worked consistently for me over four seasons without any account registration or API key.
What the Data Actually Looks Like
A complete historical standings record contains position, team name, matches played, wins, draws, losses, goals for, goals against, goal difference, and points. That is the standard template. But here is what trips people up: the points system changed. Before the 1995-96 season, a win was worth two points. The Premier League switched to three points for a win starting in 1995-96. If you are doing any analysis across eras, you need to flag which seasons use the old system. I made the mistake of normalizing everything to three-point values without marking the season boundary, and my regression model produced garbage results for three weeks before I caught it. Another edge case that is not obvious: point deductions and transfers. In the 2023-24 season, Everton started with a 10-point deduction that was later reduced on appeal. Some archives show the table with the deduction applied from matchday one, others retroactively adjust. When I was building my dataset, I had to manually cross-reference each instance against the Premier League's official club announcements. There is no automated way to detect this. You just have to know to look for it. I maintain a notes file with entries like "Everton 23/24: deduct 10 from MD1, reduce to 8 on appeal March 2024" The 2024-25 season introduced Leeds United and Leicester City back in the league, which means your historical queries need to handle team name changes. Sheffield United was "Sheffield Utd" in some sources and "Sheffield United" in others. I standardized everything to the official Premier League registered names and kept a mapping table. The mapping table itself has grown to 47 entries covering rebranding, ownership changes, and the occasional typo in early Premier League records.
Get the Full Details

A Practical Download Setup
If you just want the data without building a scraper, I can share the approach I ended up using. The script runs on Python 3.10 with requests, lxml, and pandas. It hits football-tables.com for each season from 1992-93 through the most recent completed season, parses the main table, and outputs a single CSV. The whole pipeline takes about 90 seconds on a decent connection. I host the script on GitHub under an MIT license. The link is straightforward to find if you search for "premier-league-standings-history-scraper" on GitHub. One thing the script does not handle automatically: relegated teams that get renamed or dissolved. There are a handful of cases in the early Premier League era where a club went into administration and a phoenix club took its place. Bolton Wanderers in 2019 is one. The scraper will pull whatever table exists on football-tables.com, which may or may not include the administration-era anomalies depending on when they updated their archive. I add those rows manually after the bulk scrape completes. The output CSV has columns for season, position, team, played, won, drawn, lost, goals for, goals against, goal difference, and points. There is also a boolean flag called two_point_win that is true for seasons before 1995-96. This lets you filter or normalize during analysis without having to maintain a separate lookup. The file is roughly 1.2 megabytes with all seasons included, which is small enough to keep in version control if you are tracking changes over time.
Common Mistakes When Working With Historical Tables
Mixing up promoted and relegated teams across seasons is the most frequent error. A team relegated in 2021-22 does not appear in 2022-23 standings. Your query needs to join on season correctly. I see this mistake in forum posts constantly. Someone will aggregate all positions across all seasons and wonder why Manchester City appears in the same dataset as Wigan Athletic in 2013, as if they were competitors in the same table. Not accounting for mid-season point deductions is another. The 2020-21 season had no major deductions, but 2022-23 and 2023-24 both had instances. If you are sorting by points naively, you might think a team dropped suddenly in the table when actually the points were adjusted retroactively. The Premier League publishes official statements for each deduction. Cross-referencing those statements with your data removes the confusion. It adds about 20 minutes of manual work per season with a deduction, but it prevents entirely wrong conclusions. Assuming goal difference is always calculated correctly in archived data sounds paranoid until you find a season where gf minus ga does not equal gd. This happened in one early archive I used, and the discrepancy was exactly two goals. Turns out a match result was corrected after the season due to an ineligible player, but the archive had not been updated. The Premier League's official historical table does not have this problem, but third-party sources sometimes do. Always verify gd against gf and ga before running any analysis that depends on it.
When the Data Falls Apart
Here is the blunt part: Premier League Standings History is incomplete before 1992 because the Premier League did not exist before then. The top flight was the Football League First Division. If you need data from the 19th century or early 20th century, you are looking at the Football League archives, not the Premier League. The data quality degrades significantly the further back you go. Scores are mostly reliable, but attendance figures, squad details, and even some match results have known errors that were only corrected decades later. For the modern era (1992 onward), the data is solid. The Premier League maintains its own official records, and third-party sources are generally accurate. The gaps are in the details: tactical formations, substitution timings, referee assignments. Those require specialized datasets that most casual researchers do not need. If you just want standings, goals, and points, the sources I described above will serve you fine. I used to recommend subscribing to Opta data for anyone doing serious Premier League analysis. Opta is the gold standard. But it costs thousands per year, and for standings history specifically, it is overkill. The free and low-cost approaches I outlined cover 95 percent of use cases. The remaining 5 percent usually involves something niche like tracking individual player minutes across a full season, which is a different dataset entirely.

If you want to start with something quick, download the GitHub script I mentioned, run it once, and inspect the first five rows to make sure the parsing looks correct. Then expand the range and let it run overnight if you want every season. The script handles timeouts and retries, so it is fairly robust. I have been running it every off-season for three years without a single failed run.