TUTORIAL

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.

8 min read

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:

  1. Open Build with AI.
  2. Describe the product: "Build an xG dashboard for football matches with team xG, player xG, xA, npxG, and shot maps."
  3. Tell your coding agent to read https://api.thestatsapi.com/llms.txt.
  4. Ask it to map each xG feature to exact endpoints before coding.
  5. Build the first version with match stats, player stats, and shotmap panels.
  6. Add empty states for matches where xg_available is 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 featureWhat the user seesEndpoint
Match xG cardHome xG, away xG, first-half xG, second-half xGGET /football/matches/{match_id}/stats
npxG comparisonNon-penalty xG by teamGET /football/matches/{match_id}/stats
Player xG tablePlayer shots, xG, npxG, xA, minutesGET /football/matches/{match_id}/player-stats
Shot mapEvery shot with xG, body part, result, coordinatesGET /football/matches/{match_id}/shotmap
Form modelAverage xG difference over recent matchesGET /football/matches + stats endpoint
Availability checksWhether to show xG UI at allGET /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 neededEndpointFields to use
Match xGGET /football/matches/{match_id}/statsoverview.expected_goals
Match npxGGET /football/matches/{match_id}/statsnp_expected_goals
Player xGGET /football/matches/{match_id}/player-statsshooting.expected_goals
Player npxGGET /football/matches/{match_id}/player-statsshooting.np_expected_goals
Player xAGET /football/matches/{match_id}/player-statsshooting.expected_assists
Shot xGGET /football/matches/{match_id}/shotmapdata[].expected_goals
Match availabilityGET /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.

Start building today

Ready to Power Your Sports App?

Start your 7-day free trial. All endpoints included on every plan.

Cancel anytime
7-day free trial
Setup in 5 minutes