openfootball

openfootball is a free, open, public domain collection of football (soccer) data. The football.json repository provides pre-built JSON files for major leagues and tournaments worldwide. No API key or authentication is required.

outsharp/shipp-skills127 installsApache-2.0Synced Aug 27

Works with

Claude CodeCursorCodex CLIGitHub CopilotGemini CLI

Agent Skills format with YAML frontmatter. Claude Code reads it as-is.

---
name: "openfootball"
description: "openfootball is a free, open, public domain collection of football (soccer) data. The football.json repository provides pre-built JSON files for major leagues and tournaments worldwide. No API key or authentication is required."
license: "Apache-2.0"
---

# openfootball / football.json API

[openfootball](https://github.com/openfootball) is a free, open, public domain collection of football (soccer) data. The [football.json](https://github.com/openfootball/football.json) repository provides pre-built JSON files for major leagues and tournaments worldwide. **No API key or authentication is required.**

---

## Data Sources

There are two ways to access the data:

### 1. Raw GitHub URLs (Primary)

```
https://raw.githubusercontent.com/openfootball/football.json/master/{season}/{league}.json
```

### 2. GitHub Pages Mirror

```
https://openfootball.github.io/{country}/{season}/{league-name}.json
```

> **Recommendation:** Use the raw GitHub URLs for the `football.json` repo — they use a simple, consistent naming convention and are the most reliable.

---

## URL Structure

```
https://raw.githubusercontent.com/openfootball/football.json/master/{season}/{code}.json
```

| Component | Description | Examples |
|-----------|-------------|---------|
| `{season}` | Season directory — cross-year or calendar year | `2024-25`, `2023-24`, `2025`, `2019` |
| `{code}` | League code in `{country}.{division}` format | `en.1`, `de.1`, `es.1`, `it.1`, `fr.1` |

---

## Available Leagues

### England

| Code | League | Tier |
|------|--------|------|
| `en.1` | English Premier League | 1st division |
| `en.2` | English Championship | 2nd division |
| `en.3` | English League One | 3rd division |
| `en.4` | English League Two | 4th division |

### Germany

| Code | League | Tier |
|------|--------|------|
| `de.1` | Deutsche Bundesliga | 1st division |
| `de.2` | 2. Bundesliga | 2nd division |
| `de.3` | 3. Liga | 3rd division |

### Spain

| Code | League | Tier |
|------|--------|------|
| `es.1` | Primera División (La Liga) | 1st division |
| `es.2` | Segunda División | 2nd division |

### Italy

| Code | League | Tier |
|------|--------|------|
| `it.1` | Serie A | 1st division |
| `it.2` | Serie B | 2nd division |

### France

| Code | League | Tier |
|------|--------|------|
| `fr.1` | Ligue 1 | 1st division |
| `fr.2` | Ligue 2 | 2nd division |

> **Note:** Not all leagues are available for all seasons. The `football.json` repo is continuously updated — check the [repository](https://github.com/openfootball/football.json) for the full list of available files.

---

## Available Seasons

Season directories in the `football.json` repo go back to `2010-11`. European leagues use cross-year format (`2024-25`), while some calendar-year leagues use single-year format (`2025`).

| Format | Usage | Examples |
|--------|-------|---------|
| `YYYY-YY` | European club seasons (Aug–May) | `2024-25`, `2023-24`, `2015-16` |
| `YYYY` | Calendar-year competitions | `2025`, `2020`, `2019` |

Known season directories: `2010-11`, `2011-12`, `2012-13`, `2013-14`, `2014-15`, `2015-16`, `2016-17`, `2017-18`, `2018-19`, `2019-20`, `2020-21`, `2021-22`, `2022-23`, `2023-24`, `2024-25`, `2025-26`, `2019`, `2020`, `2025`.

---

## JSON Response Format

All files follow the same JSON schema:

```json
{
  "name": "English Premier League 2024/25",
  "matches": [
    {
      "round": "Matchday 1",
      "date": "2024-08-16",
      "time": "20:00",
      "team1": "Manchester United FC",
      "team2": "Fulham FC",
      "score": {
        "ht": [0, 0],
        "ft": [1, 0]
      }
    }
  ]
}
```

### Top-Level Fields

| Field | Type | Description |
|-------|------|-------------|
| `name` | string | Human-readable league name and season |
| `matches` | array | Array of match objects |

### Match Object Fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `round` | string | Yes | Round/matchday name (e.g., `"Matchday 1"`, `"Round of 16"`) |
| `date` | string | Yes | Match date in `YYYY-MM-DD` format |
| `time` | string | No | Kick-off time in `HH:MM` format (24-hour, local time) |
| `team1` | string | Yes | Home team name |
| `team2` | string | Yes | Away team name |
| `score` | object | No | Score object (absent for unplayed future matches) |
| `status` | string | No | Special status (e.g., `"awarded"` for administratively decided results) |

### Score Object Fields

| Field | Type | Description |
|-------|------|-------------|
| `ft` | `[int, int]` | Full-time score `[home, away]` |
| `ht` | `[int, int]` | Half-time score `[home, away]` (may be absent for some matches) |

> **Note:** Some matches only have `ft` (full-time) without `ht` (half-time). Always check for the presence of `ht` before accessing it.

---

## Common Patterns

### Fetch a League Season (curl)

```bash
curl -s "https://raw.githubusercontent.com/openfootball/football.json/master/2024-25/en.1.json" | jq .
```

### Fetch and Parse Match Data (Python)

```python
import requests

url = "https://raw.githubusercontent.com/openfootball/football.json/master/2024-25/en.1.json"
data = requests.get(url).json()

print(f"League: {data['name']}")
print(f"Total matches: {len(data['matches'])}")

for match in data["matches"][:10]:
    ft = match.get("score", {}).get("ft")
    if ft:
        print(f"  {match['date']}  {match['team1']} {ft[0]}-{ft[1]} {match['team2']}")
    else:
        print(f"  {match['date']}  {match['team1']} vs {match['team2']} (no score)")
```

### Build a League Table from Results (Python)

```python
import requests
from collections import defaultdict

url = "https://raw.githubusercontent.com/openfootball/football.json/master/2024-25/en.1.json"
data = requests.get(url).json()

table = defaultdict(lambda: {"played": 0, "won": 0, "drawn": 0, "lost": 0,
                              "gf": 0, "ga": 0, "points": 0})

for match in data["matches"]:
    score = match.get("score", {}).get("ft")
    if not score:
        continue

    t1, t2 = match["team1"], match["team2"]
    g1, g2 = score

    for team, gf, ga in [(t1, g1, g2), (t2, g2, g1)]:
        table[team]["played"] += 1
        table[team]["gf"] += gf
        table[team]["ga"] += ga
        if gf > ga:
            table[team]["won"] += 1
            table[team]["points"] += 3
        elif gf == ga:
            table[team]["drawn"] += 1
            table[team]["points"] += 1
        else:
            table[team]["lost"] += 1

# Sort by points, then goal difference
sorted_table = sorted(table.items(),
                       key=lambda x: (x[1]["points"], x[1]["gf"] - x[1]["ga"]),
                       reverse=True)

print(f"{'Team':<35} {'P':>3} {'W':>3} {'D':>3} {'L':>3} {'GF':>4} {'GA':>4} {'GD':>4} {'Pts':>4}")
print("-" * 70)
for i, (team, stats) in enumerate(sorted_table, 1):
    gd = stats["gf"] - stats["ga"]
    print(f"{i:>2}. {team:<32} {stats['played']:>3} {stats['won']:>3} "
          f"{stats['drawn']:>3} {stats['lost']:>3} {stats['gf']:>4} "
          f"{stats['ga']:>4} {gd:>+4} {stats['points']:>4}")
```

### Filter Matches by Team (Python)

```python
import requests

url = "https://raw.githubusercontent.com/openfootball/football.json/master/2024-25/en.1.json"
data = requests.get(url).json()

team = "Arsenal FC"
matches = [m for m in data["matches"]
           if team in (m["team1"], m["team2"]) and m.get("score", {}).get("ft")]

for m in matches:
    ft = m["score"]["ft"]
    opponent = m["team2"] if m["team1"] == team else m["team1"]
    venue = "H" if m["team1"] == team else "A"
    my_goals = ft[0] if m["team1"] == team else ft[1]
    opp_goals = ft[1] if m["team1"] == team else ft[0]
    result = "W" if my_goals > opp_goals else ("D" if my_goals == opp_goals else "L")
    print(f"  {m['date']} ({venue}) {result} {my_goals}-{opp_goals} vs {opponent}")
```

### Fetch Multiple Leagues (Python)

```python
import requests

leagues = {
    "Premier League": "en.1",
    "Bundesliga": "de.1",
    "La Liga": "es.1",
    "Serie A": "it.1",
    "Ligue 1": "fr.1",
}

season = "2024-25"
base = "https://raw.githubusercontent.com/openfootball/football.json/master"

for name, code in leagues.items():
    url = f"{base}/{season}/{code}.json"
    resp = requests.get(url)
    if resp.status_code == 200:
        data = resp.json()
        total = len(data["matches"])
        played = sum(1 for m in data["matches"] if m.get("score", {}).get("ft"))
        print(f"{name}: {played}/{total} matches played")
    else:
        print(f"{name}: not available for {season}")
```

### Fetch and Parse (Node.js)

```javascript
const url = "https://raw.githubusercontent.com/openfootball/football.json/master/2024-25/en.1.json";

const res = await fetch(url);
const data = await res.json();

console.log(`League: ${data.name}`);
console.log(`Matches: ${data.matches.length}`);

data.matches.slice(0, 10).forEach((m) => {
  const ft = m.score?.ft;
  if (ft) {
    console.log(`  ${m.date}  ${m.team1} ${ft[0]}-${ft[1]} ${m.team2}`);
  }
});
```

### Fetch and Parse (bash + jq)

```bash
# Get all results for a specific team
curl -s "https://raw.githubusercontent.com/openfootball/football.json/master/2024-25/en.1.json" \
  | jq -r '.matches[]
    | select(.team1 == "Liverpool FC" or .team2 == "Liverpool FC")
    | select(.score.ft)
    | "\(.date) \(.team1) \(.score.ft[0])-\(.score.ft[1]) \(.team2)"'
```

### Compare Head-to-Head Results (Python)

```python
import requests

url = "https://raw.githubusercontent.com/openfootball/football.json/master/2024-25/en.1.json"
data = requests.get(url).json()

team_a = "Arsenal FC"
team_b = "Liverpool FC"

h2h = [m for m in data["matches"]
       if {m["team1"], m["team2"]} == {team_a, team_b}
       and m.get("score", {}).get("ft")]

for m in h2h:
    ft = m["score"]["ft"]
    ht = m["score"].get("ht", ["?", "?"])
    print(f"{m['date']}: {m['team1']} {ft[0]}-{ft[1]} {m['team2']} (HT: {ht[0]}-{ht[1]})")
```

### Aggregate Stats Across Seasons (Python)

```python
import requests

base = "https://raw.githubusercontent.com/openfootball/football.json/master"
seasons = ["2022-23", "2023-24", "2024-25"]
team = "Manchester City FC"
all_results = {"W": 0, "D": 0, "L": 0, "GF": 0, "GA": 0}

for season in seasons:
    resp = requests.get(f"{base}/{season}/en.1.json")
    if resp.status_code != 200:
        continue
    data = resp.json()
    for m in data["matches"]:
        ft = m.get("score", {}).get("ft")
        if not ft:
            continue
        if m["team1"] == team:
            gf, ga = ft
        elif m["team2"] == team:
            ga, gf = ft
        else:
            continue
        all_results["GF"] += gf
        all_results["GA"] += ga
        if gf > ga:
            all_results["W"] += 1
        elif gf == ga:
            all_results["D"] += 1
        else:
            all_results["L"] += 1

print(f"{team} across {', '.join(seasons)}:")
print(f"  W{all_results['W']} D{all_results['D']} L{all_results['L']}")
print(f"  Goals: {all_results['GF']} scored, {all_results['GA']} conceded")
```

### Find High-Scoring Matches (bash + jq)

```bash
# Find all matches with 5+ total goals in the Premier League 2024/25
curl -s "https://raw.githubusercontent.com/openfootball/football.json/master/2024-25/en.1.json" \
  | jq -r '.matches[]
    | select(.score.ft)
    | select((.score.ft[0] + .score.ft[1]) >= 5)
    | "\(.date) \(.team1) \(.score.ft[0])-\(.score.ft[1]) \(.team2) (Total: \(.score.ft[0] + .score.ft[1]))"'
```

### Get Results for a Specific Matchday (Python)

```python
import requests

url = "https://raw.githubusercontent.com/openfootball/football.json/master/2024-25/en.1.json"
data = requests.get(url).json()

matchday = "Matchday 38"
matches = [m for m in data["matches"] if m["round"] == matchday]

print(f"--- {matchday} ---")
for m in matches:
    ft = m.get("score", {}).get("ft")
    if ft:
        print(f"  {m['date']} {m.get('time', '')}  {m['team1']} {ft[0]}-{ft[1]} {m['team2']}")
    else:
        print(f"  {m['date']} {m.get('time', '')}  {m['team1']} vs {m['team2']}")
```

---

## Other Repositories (Source Data)

The `football.json` files are auto-generated from plain-text Football.TXT source files in country-specific repos:

| Repo | Content | GitHub URL |
|------|---------|------------|
| `england` | EPL, Championship, League One, League Two | [openfootball/england](https://github.com/openfootball/england) |
| `deutschland` | Bundesliga, 2. Bundesliga, 3. Liga, DFB Pokal | [openfootball/deutschland](https://github.com/openfootball/deutschland) |
| `espana` | La Liga, Segunda División | [openfootball/espana](https://github.com/openfootball/espana) |
| `italy` | Serie A, Serie B, Coppa Italia | [openfootball/italy](https://github.com/openfootball/italy) |
| `france` | Ligue 1, Ligue 2 | [openfootball/europe](https://github.com/openfootball/europe) (in `/europe`) |
| `worldcup` | FIFA World Cup (2022, 2018, 2014, etc.) | [openfootball/worldcup](https://github.com/openfootball/worldcup) |
| `euro` | Euro 2024, 2020, 2016, etc. | [openfootball/euro](https://github.com/openfootball/euro) |
| `champions-league` | UCL & Europa League | [openfootball/champions-league](https://github.com/openfootball/champions-league) |
| `clubs` | Club & stadium metadata | [openfootball/clubs](https://github.com/openfootball/clubs) |
| `world` | Leagues from N. America, Asia, Africa, Australia | [openfootball/world](https://github.com/openfootball/world) |

Country repos also have their own GitHub Pages JSON mirrors. For example:

```
https://openfootball.github.io/england/2024-25/1-premierleague.json
```

You can convert Football.TXT source files to JSON yourself using the `fbtxt2json` CLI tool:

```bash
# Convert a single league file
fbtxt2json england/2025-26/1-premierleague.txt -o en.1.json

# Convert an entire country repo at once
fbtxt2json . -o ./_site
```

---

## Rate Limits

GitHub does not publish specific rate limits for raw content, but general guidelines:

| Guideline | Recommendation |
|-----------|----------------|
| Polling interval | ≥ 60 seconds between requests for the same file |
| Concurrent requests | Keep reasonable (< 20 concurrent) |
| Caching | Cache responses locally — data changes infrequently |
| Unauthenticated GitHub API | 60 requests/hour per IP (only applies to API endpoints, not raw content) |

> **Tip:** Since match data doesn't change after a game is completed, you can aggressively cache historical seasons. Only poll the current season for updates.

---

## Error Handling

| HTTP Status | Meaning | Action |
|-------------|---------|--------|
| 200 | Success | Parse the JSON |
| 404 | File not found | Check the season, league code, or URL spelling |
| 429 | Rate limited | Back off and retry after a delay |
| 5xx | Server error | Retry with exponential backoff |

---

## Tips

- **No auth needed** — all data is fully public. Start fetching immediately.
- **Check for `score` before accessing** — future/unplayed matches won't have a `score` field.
- **Check for `ht` separately** — some matches have `ft` but no `ht` data.
- **Team names include suffixes** — e.g., `"Arsenal FC"`, `"Manchester United FC"`, `"Borussia Dortmund"`. Use exact string matching.
- **Team names vary by league** — German teams use German names (`"FC Bayern München"`), English teams use English names (`"Arsenal FC"`).
- **The `status` field is rare** — it appears on administratively decided matches (e.g., `"awarded"`).
- **Cache aggressively** — completed seasons never change. Only the current season gets updates.
- **Build tables from the data** — the dataset provides raw match results; you compute standings, form, H2H, etc.
- **Cross-reference leagues** — fetch multiple league files to compare across countries.
- **Public domain (CC0)** — use the data however you want with no restrictions whatsoever.
- **Don't edit the JSON directly** — if contributing, edit the Football.TXT source files in the country repos; JSON is auto-generated.
- **Time field is local** — the `time` value represents the local kick-off time for the match venue.
- **Scores are `[home, away]`** — `score.ft[0]` is always the `team1` (home) goals, `score.ft[1]` is always the `team2` (away) goals.

---

## Changelog

- **0.1.0** — Initial release with league data access, JSON schema documentation, and common usage patterns.

More General & Other skills

← All General & Other skills

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free · No signup · No trial clock

SEE THE DIRECTORY