Education Access
Real football data for coursework: fixtures, results, standings, in-play statistics, and the odds history that makes a student project more than a table of goals. Granted by a partner institution, per course, for the length of the course.
The figures below are the public baseline. What a course actually gets — deeper history, a higher rate — is agreed per programme to fit what it teaches.
Coverage
130+ competitions — every top flight and the second divisions with real odds, the same as Pro.
Odds depth
19 bookmakers and the full tick-by-tick odds history, as on Ultra. Every price move, pre-match and in-play.
History & rate
Last 12 months of results and odds. 10 requests a minute per account.
How access works
- Your institution gives you a course code. Each course has its own; your lecturer or programme office hands it out. Codes are not case-sensitive and hyphens do not matter.
- Sign up like anyone else at 5dollarfootballapi.com/signup — email code or Google, no card. You get a working free key straight away, so you can start reading the docs and calling the API while you wait.
- Enter the code on your profile page under “Have a course code?”, with the name you enrolled under. If your institution sent you a sign-up link, the code is already filled in.
- Your institution approves you. You get an email, and every key on your account switches to Education Access — same key, same base URL, more data and a higher rate limit. Nothing to change in your code.
- It runs until the course ends. Then the account returns to the free plan; your keys keep working. Enrolled in the next course? Enter its code.
For institutions
Education Access is set up per partner institution: administrators create courses and approve students from their own back office, lecturers run the courses assigned to them, and staff hold the plan and a key of their own. To bring a course, write to [email protected] with the programme, the expected number of students and the dates.
Any language: it is plain HTTPS and JSON
There is nothing to install. One header carries your key; every endpoint answers with the same JSON envelope. Whatever your course uses — R, JavaScript, Julia, a spreadsheet that can fetch a URL — the calls look like this.
# Your plan and today's usage
curl "https://api.5dollarfootballapi.com/v1/status" \
-H "Authorization: Bearer $FOOTBALL_API_KEY"
# Every match in play right now, with odds, events and statistics on each row
curl "https://api.5dollarfootballapi.com/v1/fixtures?status=live&include=odds,events,stats" \
-H "Authorization: Bearer $FOOTBALL_API_KEY"
# A whole season of one league, page by page
curl "https://api.5dollarfootballapi.com/v1/leagues/LEAGUE_ID/fixtures?status=finished&page=1&per_page=100" \
-H "Authorization: Bearer $FOOTBALL_API_KEY"
{
"data": [ ... ],
"pagination": { "page": 1, "per_page": 100, "count": 100, "has_more": true }
}
- Pagination: keep requesting
page+ 1 whilehas_moreis true. - Rate limit: a 429 comes with a
Retry-Afterheader in seconds — wait that long and retry.X-RateLimit-Remainingon every response tells you how much of the minute is left. - Errors use the same envelope with an
errorobject; the error reference lists every code. - Every endpoint, parameter and response shape is in the endpoint reference.
Getting started in Python
The same calls from Python, with requests and pandas — no SDK needed. Put your key in an environment variable rather than in the notebook — it is yours, and a notebook gets shared.
pip install requests pandas
export FOOTBALL_API_KEY=fb_live_your_key
1. One helper, one session
A session keeps the connection open and the header attached. The helper reads the JSON envelope, backs off on a 429 using the Retry-After header, and raises on anything else.
import os, time, requests
BASE = "https://api.5dollarfootballapi.com/v1"
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['FOOTBALL_API_KEY']}"
def get(path, **params):
"""GET one endpoint and return the parsed JSON envelope."""
while True:
r = session.get(f"{BASE}{path}", params=params, timeout=30)
if r.status_code == 429:
time.sleep(int(r.headers.get("Retry-After", "5")))
continue
r.raise_for_status()
return r.json()
print(get("/status")["data"]["plan"]) # 'education' once you are approved
2. Today's fixtures into a DataFrame
List endpoints take a time window of up to 24 hours in unix seconds and return a pagination object; keep going while has_more is true. include=odds expands every row with the Bet365 lines in the same call.
from datetime import datetime, timezone, timedelta
import pandas as pd
def get_all(path, **params):
"""Follow pagination and return every row."""
rows, page = [], 1
while True:
env = get(path, page=page, per_page=100, **params)
rows += env["data"]
if not env["pagination"]["has_more"]:
return rows
page += 1
start = datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0)
fixtures = get_all("/fixtures",
start_time=int(start.timestamp()),
end_time=int((start + timedelta(days=1)).timestamp()),
include="odds")
df = pd.json_normalize(fixtures)
print(df[["id", "league.name", "home.name", "away.name", "kickoff_utc", "status"]].head())
3. A season, a team, a table
leagues = pd.json_normalize(get_all("/leagues"))
epl = int(leagues.loc[leagues["name"] == "England Premier League", "id"].iloc[0])
season = pd.json_normalize(get_all(f"/leagues/{epl}/fixtures", status="finished"))
table = pd.json_normalize(get("/standings", league=epl)["data"])
last10 = pd.json_normalize(get_all(f"/teams/{int(season['home.id'].iloc[0])}/fixtures"))[:10]
4. The odds history — the interesting part
Every recorded price change for one market of one fixture, oldest first, with the score at that moment. This is what Education Access adds over the free plan, and it is the raw material for most of the project ideas below.
fixture_id = int(df["id"].iloc[0])
ticks = pd.json_normalize(get_all(f"/fixtures/{fixture_id}/odds/history", market="asian"))
ticks["time"] = pd.to_datetime(ticks["time"], utc=True)
print(ticks[["time", "line", "home", "away", "score"]].tail())
# Other bookmakers, one at a time — see /v1/bookmakers for the slugs:
pinnacle = get_all(f"/fixtures/{fixture_id}/odds/history", market="1x2", bookmaker="pinnacle")
5. Be kind to your rate limit
- Cache what does not change. A finished match and its odds history never change again. Save responses to disk (
df.to_parquet, orrequests-cache) and read from there while you iterate. - Use the batch call.
/fixtures?include=odds,events,statsis one request for a whole day, not one per match. - Read the headers.
X-RateLimit-Remainingtells you how much of the minute is left; the helper above already waits out a 429. - Pull once, analyse many times. A notebook that re-downloads a season on every cell run spends the whole class's patience. Download, save, then work on the file.
Project ideas
Analysis projects on real match data — results, events, statistics, corners, cards, and the odds history most free sources do not have. Each is a question and a starting point; each fits a notebook, a Streamlit or Dash app, or a large-format visual.
Home advantage
How big is home advantage, league by league, and is it shrinking? Points per game and goal difference at home versus away across a season, then across the seasons you can reach.
When goals happen
Use the event timeline to map goals by minute. Which leagues score late, how often does a half-time lead survive, and what does a comeback look like in the numbers?
Corner profiles
Corners for and against by team, first half versus second half, home versus away. Build each team's corner profile for the season and see which styles of play it reveals.
Cards and what follows
Bookings by minute and by league from the event timeline. After a red card, what happens to goals, corners and the final result for the team that lost a player?
Market efficiency
Are opening odds or closing odds the better forecast? Turn both into probabilities, score them against a season of results, and see where and when the market learns.
Model backtest
Build a match-outcome model from results and statistics, then backtest it on a season you held out. How does it compare with the probabilities the market published before kickoff?
Live dashboard
A Streamlit page that polls status=live once a minute and shows every match in play with score, corners, cards and in-play statistics. Small, real, and demo-able.
Publishing your work
Please do. Repositories, blog posts, dashboards and portfolio pieces are exactly what the programme is for. Two things to keep in mind:
- Credit the data. Education Access is a $0 plan, so anything public-facing — a website, app, bot, notebook on GitHub or published dataset — carries “Football data by 5DollarFootballAPI”, linked to the site. A README line or a footer is fine. Formats.
- Publish your analysis, not the feed. Charts, models, aggregates, samples that illustrate your work — all fine. The raw data as a bulk download or a competing feed is the one thing the terms do not allow, on any plan.