How to Get Expected Goals (xG) Data via Football API
Get xG, npxG, and xA for football matches and players via API. Use llms.txt, Build with AI, endpoint examples, and prompts for xG dashboards and models.
Expected goals data is one of the most useful upgrades you can add to a football product. Scores tell you what happened. xG helps you understand the quality of chances behind the scoreline.
You can use TheStatsAPI to build xG dashboards, betting model features, fantasy scoring tools, shot maps, post-match reports, and player analysis pages. The easiest way to start is to give your AI coding tool the right API context, then ask it to build the xG workflow in natural language.
Use Build with AI to generate a coding prompt, or point your agent directly at API llms.txt. The llms file is the source of truth for endpoint paths, query parameters, and response fields, so your AI tool does not invent a fake xG endpoint.
The Fast Path
If you want a working xG feature quickly:
- Open Build with AI.
- Describe the product: "Build an xG dashboard for football matches with team xG, player xG, xA, npxG, and shot maps."
- Tell your coding agent to read
https://api.thestatsapi.com/llms.txt. - Ask it to map each xG feature to exact endpoints before coding.
- Build the first version with match stats, player stats, and shotmap panels.
- Add empty states for matches where
xg_availableis false.
That workflow is safer than asking an AI tool to "use an xG API" and hoping it guesses the right fields.
What You Can Build with xG
| Product feature | What the user sees | Endpoint |
|---|---|---|
| Match xG card | Home xG, away xG, first-half xG, second-half xG | GET /football/matches/{match_id}/stats |
| npxG comparison | Non-penalty xG by team | GET /football/matches/{match_id}/stats |
| Player xG table | Player shots, xG, npxG, xA, minutes | GET /football/matches/{match_id}/player-stats |
| Shot map | Every shot with xG, body part, result, coordinates | GET /football/matches/{match_id}/shotmap |
| Form model | Average xG difference over recent matches | GET /football/matches + stats endpoint |
| Availability checks | Whether to show xG UI at all | GET /football/matches or GET /football/matches/{match_id} |
Availability depends on the competition and fixture. Check xg_available before treating xG fields as mandatory.
Prompt: Build an xG Dashboard
Paste this into Claude Code, Cursor, Windsurf, Lovable, Bolt, Replit, or another coding agent:
Read this API reference before coding: https://api.thestatsapi.com/llms.txt
Build an xG dashboard using TheStatsAPI.
Requirements:
- Keep THESTATSAPI_KEY server-side.
- Use only endpoint paths and response fields listed in llms.txt.
- Let the user choose a match_id or select a match from GET /football/matches.
- Check xg_available before rendering xG-specific panels.
- Add these panels:
- Team xG from GET /football/matches/{match_id}/stats
- Team npxG from the same stats response
- Player xG, npxG, and xA from GET /football/matches/{match_id}/player-stats
- Shot map data from GET /football/matches/{match_id}/shotmap
- Add empty states when xG, player stats, or shotmap data is unavailable.
- Include loading and error states.
- Make the UI compact and useful for analysts.
Before coding, make a data plan showing endpoint, fields used, and fallback behavior for each panel.
This gets you a practical first version: data-safe, endpoint-aware, and easy to refine.
Endpoint Overview
| Data needed | Endpoint | Fields to use |
|---|---|---|
| Match xG | GET /football/matches/{match_id}/stats | overview.expected_goals |
| Match npxG | GET /football/matches/{match_id}/stats | np_expected_goals |
| Player xG | GET /football/matches/{match_id}/player-stats | shooting.expected_goals |
| Player npxG | GET /football/matches/{match_id}/player-stats | shooting.np_expected_goals |
| Player xA | GET /football/matches/{match_id}/player-stats | shooting.expected_assists |
| Shot xG | GET /football/matches/{match_id}/shotmap | data[].expected_goals |
| Match availability | GET /football/matches or GET /football/matches/{match_id} | xg_available |
These are exposed in the API tester and the API llms.txt.
Match-Level xG
Use the match stats endpoint to fetch team-level expected goals.
curl "https://api.thestatsapi.com/api/football/matches/mt_010249745/stats" \
-H "Authorization: Bearer YOUR_API_KEY"
Example response shape:
{
"data": {
"match_id": "mt_010249745",
"overview": {
"expected_goals": {
"all": { "home": 1.43, "away": 1.13 },
"first_half": { "home": 1.22, "away": 0.49 },
"second_half": { "home": 0.22, "away": 0.64 }
}
},
"np_expected_goals": {
"all": { "home": 1.21, "away": 0.94 },
"first_half": { "home": 1.05, "away": 0.41 },
"second_half": { "home": 0.16, "away": 0.53 }
}
}
}
Use overview.expected_goals.all for the main match xG card. Use np_expected_goals.all when you want a cleaner view of chance creation without penalties.
Player xG, npxG, and xA
Use player match stats when you need xG tied to individual players.
curl "https://api.thestatsapi.com/api/football/matches/mt_14502/player-stats" \
-H "Authorization: Bearer YOUR_API_KEY"
Example player object:
{
"player_id": "pl_6241",
"player_name": "Mohamed Salah",
"position": "F",
"minutes_played": 90,
"shooting": {
"goals": 1,
"total_shots": 4,
"shots_on_target": 2,
"expected_goals": 0.82,
"expected_assists": 0.31,
"np_expected_goals": 0.82,
"np_expected_goals_per_90": 0.82
}
}
This is the endpoint for player tables, fantasy scoring, post-match player cards, and expected-assist analysis.
Shot-Level xG
Use the shotmap endpoint when you want the raw shots behind the match total.
curl "https://api.thestatsapi.com/api/football/matches/mt_732012275/shotmap" \
-H "Authorization: Bearer YOUR_API_KEY"
Example shot:
{
"id": "sh_084145851",
"player_id": "pl_74112545",
"player_name": "Danilo",
"team_name": "Botafogo",
"x": 15.7,
"y": 57.3,
"minute": 54,
"result": "goal",
"expected_goals": 0.05,
"situation": "assisted",
"body_part": "left-foot",
"is_goal": true,
"is_penalty": false
}
Shot-level xG lets you build shot maps, chance timelines, finishing-overperformance views, and model features based on chance quality.
Agentic Workflow: Build the Product in Steps
Step 1: Data plan
Read https://api.thestatsapi.com/llms.txt.
Map an xG dashboard to exact endpoints and response fields.
Include fallback behavior for missing xG, odds, player stats, and shotmap data.
Do not write code yet.
Step 2: First implementation
Implement the xG dashboard from the data plan.
Create components:
- MatchSelector
- TeamXgSummary
- PlayerXgTable
- ShotMapTable
- EmptyState
- ErrorState
Keep the API key server-side.
Step 3: Add analyst features
Add a form panel that fetches a team's last 10 finished matches,
skips matches where xg_available is false, fetches match stats for the rest,
and calculates average xG difference and average npxG difference.
Step 4: Improve the UI
Make the xG dashboard easier to scan:
- Big home vs away xG numbers
- Separate xG and npxG rows
- Player xG table sorted by expected_goals
- Shot list sorted by minute
- Clear empty states when xG is unavailable
Python: Calculate xG Difference
Here is a small script that fetches match stats and calculates xG difference.
import requests
API_KEY = "your_api_key"
BASE_URL = "https://api.thestatsapi.com/api"
MATCH_ID = "mt_010249745"
headers = {"Authorization": f"Bearer {API_KEY}"}
response = requests.get(
f"{BASE_URL}/football/matches/{MATCH_ID}/stats",
headers=headers,
timeout=20,
)
response.raise_for_status()
stats = response.json()["data"]
xg = stats["overview"]["expected_goals"]["all"]
npxg = stats["np_expected_goals"]["all"]
print(f"xG: {xg['home']:.2f} - {xg['away']:.2f}")
print(f"npxG: {npxg['home']:.2f} - {npxg['away']:.2f}")
print(f"xG difference: {xg['home'] - xg['away']:.2f}")
Python: Pull xG for a Team's Last 10 Matches
For a basic form feature, fetch recent matches for a team, then enrich each finished match with match stats.
import requests
from statistics import mean
API_KEY = "your_api_key"
BASE_URL = "https://api.thestatsapi.com/api"
TEAM_ID = "tm_9145"
headers = {"Authorization": f"Bearer {API_KEY}"}
matches_response = requests.get(
f"{BASE_URL}/football/matches",
headers=headers,
params={"team_id": TEAM_ID, "status": "finished", "per_page": 10},
timeout=20,
)
matches_response.raise_for_status()
xg_diffs = []
for match in matches_response.json()["data"]:
if not match.get("xg_available"):
continue
stats_response = requests.get(
f"{BASE_URL}/football/matches/{match['id']}/stats",
headers=headers,
timeout=20,
)
stats_response.raise_for_status()
stats = stats_response.json()["data"]
xg = stats["overview"]["expected_goals"]["all"]
is_home = match["home_team"]["id"] == TEAM_ID
team_xg = xg["home"] if is_home else xg["away"]
opponent_xg = xg["away"] if is_home else xg["home"]
xg_diffs.append(team_xg - opponent_xg)
if xg_diffs:
print(f"Average xG difference: {mean(xg_diffs):.2f}")
else:
print("No xG matches found for this sample.")
Does api-football Have xG?
api-football is a broad football API, but developers should verify xG coverage before building around it. Public pricing makes endpoints available across plans, but expected goals, npxG, and xA are not safe to assume as a standard field set in every core response or every league.
The practical rule: if xG is central to your product, test the exact competition and endpoint before committing to the provider. TheStatsAPI exposes xG-related fields in documented match stats, player stats, and shotmap responses where xG is available.
FAQ
What is xG in football data APIs?
xG, or expected goals, is a shot-quality estimate. It assigns a probability to each chance based on factors such as shot location, body part, situation, and defensive context.
What is npxG?
npxG means non-penalty expected goals. It removes penalty shots from the xG total, which often makes it better for measuring open-play chance creation.
What is xA?
xA means expected assists. It estimates the chance quality created by a pass, regardless of whether the receiving player scores.
Can I build an xG dashboard with AI?
Yes. Use Build with AI, then tell your coding agent to read API llms.txt before coding. The prompts above give it the endpoint map and fallback behavior.
How accurate is xG data?
xG is a model-based estimate, not a fact. It is useful for comparing chance quality over time, but single-match xG can be noisy. It works best across larger samples.
Which TheStatsAPI plan includes xG?
Every plan includes access to the xG-related endpoints where xG is available for the competition and match. Plans differ by request volume, not endpoint access.
Ready to Power Your Sports App?
Start your 7-day free trial. All endpoints included on every plan.