# Fixtures

> **Included in all plans**
>
> Fixture data is available on all base plans (Starter, Growth, Pro, Enterprise) at no additional cost. Plans differ in the number of accessible leagues (5 to all leagues) and hourly API call limits (2,000 to 5,000+).
>
> [Compare plans →](https://www.sportmonks.com/football-api/plans-pricing)

**Quick summary:** This tutorial covers the ten fixtures endpoints available in API v3, how to retrieve match data across different timeframes and criteria, and how to enrich responses with scores, events, and lineups.

**What you'll learn:**

* Which of the ten fixture endpoints to use for different scenarios
* How to retrieve fixtures by date, date range, team, head-to-head, and search
* How to enrich fixture responses using includes and narrow them with filters

**Time to complete:** 25 minutes **Skill level:** Beginner **Prerequisites:** [Authentication](https://docs.sportmonks.com/v3/welcome/authentication), [Includes](https://docs.sportmonks.com/v3/tutorials-and-guides/tutorials/includes), [Filter and Select Fields](https://docs.sportmonks.com/v3/tutorials-and-guides/tutorials/filter-and-select-fields)

### 1. When to use this feature

The fixtures endpoints are the most widely used in the API. They give you complete match data at any point in time (past, present, or future) across ten different retrieval options.

#### Real-world use cases

**Use case 1: Match detail pages** You're building a football app and a user clicks into a specific match. The fixture by ID endpoint returns everything: venue, officials, scores, and detailed metadata. Livescores would not work here because they only cover the 15-minute window around kickoff.

**Use case 2: Team schedule pages** You want to display all of Celtic's fixtures in September. The "Fixture by Date Range for Team" endpoint lets you pass a date range and a team ID to return only their matches in that period.

**Use case 3: Historical analysis and H2H displays** You want to show all past meetings between Rangers and Celtic before an upcoming derby. The Head to Head endpoint returns the full match history between any two team IDs.

**Use case 4: Database sync and catch-up** After downtime, you need to identify which records changed. The "Latest Updated Fixtures" endpoint returns all fixtures updated within the last 10 seconds, so you can patch only what changed.

#### When NOT to use this feature

* **Real-time live scoreboard:** Livescores are lighter and optimised for frequent polling during active matches. Use them for your live tracker, then fetch the full fixture when a user wants details.
* **Full season schedule:** For retrieving all fixtures in a season, the Schedules endpoint with `include=fixtures` is more efficient.

{% hint style="info" %}
**Recommended alternative:** [Livescores tutorial](https://docs.sportmonks.com/v3/tutorials-and-guides/tutorials/livescores-and-fixtures/livescores) for real-time match tracking.
{% endhint %}

### 2. How to retrieve data

#### Understanding the endpoints

**Base URL:**

```http
https://api.sportmonks.com/v3/football/fixtures
```

**Available endpoints:**

| Endpoint                           | Path suffix                         | Description                                 |
| ---------------------------------- | ----------------------------------- | ------------------------------------------- |
| GET All Fixtures                   | *(base URL)*                        | All fixtures in your subscription           |
| GET Fixture by ID                  | `/{fixture_id}`                     | Single fixture by ID                        |
| GET Fixtures by Multiple IDs       | `/multi/{id1,id2}`                  | Multiple fixtures in one request            |
| GET Fixture by Date                | `/date/{YYYY-MM-DD}`                | All fixtures on a specific date             |
| GET Fixture by Date Range          | `/between/{start}/{end}`            | Fixtures between two dates                  |
| GET Fixture by Date Range for Team | `/between/{start}/{end}/{team_id}`  | A specific team's fixtures in a date range  |
| GET Fixture by Head to Head        | `/head-to-head/{team_id}/{team_id}` | All matches between two teams               |
| GET Fixture by Search              | `/search/{query}`                   | Fixtures matching a name search             |
| GET Upcoming Fixtures by Market ID | `/upcoming/markets/{market_id}`     | Upcoming fixtures with odds for a market    |
| GET Latest Updated Fixtures        | `/latest`                           | Fixtures updated within the last 10 seconds |

**Available parameters:**

| Parameter   | Type   | Required | Description                            | Example                      |
| ----------- | ------ | -------- | -------------------------------------- | ---------------------------- |
| `api_token` | string | Yes      | Your API authentication token          | `YOUR_API_TOKEN`             |
| `include`   | string | No       | Related data to attach to the response | `scores;participants;events` |
| `select`    | string | No       | Specific fields to return              | `name,result_info`           |
| `filters`   | string | No       | Filter results by field values         | `fixtureLeagues:501`         |

#### Step-by-step implementation

**Step 1: Basic request — GET Fixture by ID**

The simplest useful request: retrieving a single known fixture.

**JavaScript:**

{% tabs %}
{% tab title="Javascript" %}

```js
const apiToken = 'YOUR_API_TOKEN';
const fixtureId = 18535517; // Celtic vs Rangers

async function getFixture(id) {
  const response = await fetch(
    `https://api.sportmonks.com/v3/football/fixtures/${id}?api_token=${apiToken}`
  );
  const data = await response.json();
  console.log(data);
}

getFixture(fixtureId);
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

api_token = 'YOUR_API_TOKEN'
fixture_id = 18535517

response = requests.get(
    f'https://api.sportmonks.com/v3/football/fixtures/{fixture_id}',
    params={'api_token': api_token}
)
data = response.json()
print(data)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
$apiToken = 'YOUR_API_TOKEN';
$fixtureId = 18535517;

$url = "https://api.sportmonks.com/v3/football/fixtures/{$fixtureId}?api_token={$apiToken}";
$response = file_get_contents($url);
$data = json_decode($response, true);

print_r($data);
?>
```

{% endtab %}
{% endtabs %}

**Step 2: GET Fixture by Date**

Returns all fixtures from your subscription for a given date. Pass the date in `YYYY-MM-DD` format.

```http
https://api.sportmonks.com/v3/football/fixtures/date/2022-09-03
?api_token=YOUR_API_TOKEN
```

**JavaScript:**

```javascript
async function getFixturesByDate(date) {
  const response = await fetch(
    `https://api.sportmonks.com/v3/football/fixtures/date/${date}?api_token=${apiToken}`
  );
  return response.json();
}
```

**Step 3: GET Fixtures by Date Range**

Returns all fixtures between two dates. Useful for week or month views.

```javascript
https://api.sportmonks.com/v3/football/fixtures/between/2022-09-01/2022-09-30
?api_token=YOUR_API_TOKEN
```

To restrict this to a specific team, append the team ID:

```javascript
https://api.sportmonks.com/v3/football/fixtures/between/2022-09-01/2022-09-30/53
?api_token=YOUR_API_TOKEN
```

**Step 4: Adding includes**

Fixture responses return only IDs by default. Use `include` to attach related data in the same request.

```
https://api.sportmonks.com/v3/football/fixtures/18535517?api_token=YOUR_API_TOKEN&include=scores;participants;statistics.type;events;lineups
```

The most commonly used includes for fixtures are:

* `scores` : Full-time, half-time, and penalty scores
* `participants` : Home and away team details
* `events` : Goals, cards, and substitutions
* `lineups` : Starting XI and bench
* `statistics.type` : Match statistics with type labels

{% hint style="warning" %}
⚠️ Including `.type` on large result sets is expensive. Retrieve all types once from the [Types endpoint](https://docs.sportmonks.com/v3/definitions/types) and cache them rather than including them on every request.
{% endhint %}

**Step 5: Selecting specific fields**

If you only need a subset of fields, use `select` to reduce payload size. For example, returning only the result of Celtic vs Rangers:

```
https://api.sportmonks.com/v3/football/fixtures/18535517?api_token=YOUR_API_TOKEN&select=result_info
```

Response:

```json
{
  "data": {
    "result_info": "Celtic won after full-time.",
    "id": 18535517
  }
}
```

**Step 6: Complete example — fixture class**

**JavaScript:**

{% tabs %}
{% tab title="Javascript" %}

```javascript
class FootballAPI {
  constructor(apiToken) {
    this.token = apiToken;
    this.baseURL = 'https://api.sportmonks.com/v3/football';
  }

  async getFixture(fixtureId, params = {}) {
    const query = new URLSearchParams({ api_token: this.token, ...params });
    const response = await fetch(
      `${this.baseURL}/fixtures/${fixtureId}?${query}`
    );
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    return response.json();
  }

  async getFixturesByDateRange(startDate, endDate, teamId = null, params = {}) {
    const path = teamId
      ? `/fixtures/between/${startDate}/${endDate}/${teamId}`
      : `/fixtures/between/${startDate}/${endDate}`;
    const query = new URLSearchParams({ api_token: this.token, ...params });
    const response = await fetch(`${this.baseURL}${path}?${query}`);
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    return response.json();
  }

  async getHeadToHead(teamIdA, teamIdB, params = {}) {
    const query = new URLSearchParams({ api_token: this.token, ...params });
    const response = await fetch(
      `${this.baseURL}/fixtures/head-to-head/${teamIdA}/${teamIdB}?${query}`
    );
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    return response.json();
  }
}

// Usage
const api = new FootballAPI('YOUR_API_TOKEN');

// Full match details for Celtic vs Rangers
const fixture = await api.getFixture(18535517, {
  include: 'scores;participants;events;lineups'
});

// Celtic's September fixtures
const schedule = await api.getFixturesByDateRange('2022-09-01', '2022-09-30', 53);

// Old Firm history
const h2h = await api.getHeadToHead(53, 62);
```

{% endtab %}

{% tab title="Python" %}

```python
import requests
from typing import Optional, Dict

class FootballAPI:
    def __init__(self, api_token: str):
        self.token = api_token
        self.base_url = 'https://api.sportmonks.com/v3/football'

    def get_fixture(self, fixture_id: int, params: Optional[Dict] = None) -> Dict:
        params = params or {}
        params['api_token'] = self.token
        r = requests.get(f'{self.base_url}/fixtures/{fixture_id}', params=params, timeout=30)
        r.raise_for_status()
        return r.json()

    def get_fixtures_by_date_range(
        self, start: str, end: str, team_id: Optional[int] = None, params: Optional[Dict] = None
    ) -> Dict:
        params = params or {}
        params['api_token'] = self.token
        path = f'/fixtures/between/{start}/{end}'
        if team_id:
            path += f'/{team_id}'
        r = requests.get(f'{self.base_url}{path}', params=params, timeout=30)
        r.raise_for_status()
        return r.json()

# Usage
api = FootballAPI('YOUR_API_TOKEN')

fixture = api.get_fixture(18535517, {'include': 'scores;participants;events'})
celtics_september = api.get_fixtures_by_date_range('2022-09-01', '2022-09-30', team_id=53)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
class FootballAPI {
    private $token;
    private $baseURL = 'https://api.sportmonks.com/v3/football';

    public function __construct($apiToken) {
        $this->token = $apiToken;
    }

    public function getFixture($fixtureId, $params = []) {
        $params['api_token'] = $this->token;
        $url = "{$this->baseURL}/fixtures/{$fixtureId}?" . http_build_query($params);

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode !== 200) throw new Exception("HTTP {$httpCode}");
        return json_decode($response, true);
    }

    public function getFixturesByDateRange($start, $end, $teamId = null, $params = []) {
        $params['api_token'] = $this->token;
        $path = $teamId
            ? "/fixtures/between/{$start}/{$end}/{$teamId}"
            : "/fixtures/between/{$start}/{$end}";
        $url = "{$this->baseURL}{$path}?" . http_build_query($params);

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        $response = curl_exec($ch);
        curl_close($ch);

        return json_decode($response, true);
    }
}

// Usage
$api = new FootballAPI('YOUR_API_TOKEN');

$fixture = $api->getFixture(18535517, ['include' => 'scores;participants;events;lineups']);
$schedule = $api->getFixturesByDateRange('2022-09-01', '2022-09-30', 53);
?>
```

{% endtab %}
{% endtabs %}

### 3. Working with the data

#### Understanding the response structure

```json
{
  "data": {
    "id": 18535517,
    "sport_id": 1,
    "league_id": 501,
    "season_id": 19735,
    "stage_id": 77457866,
    "group_id": null,
    "aggregate_id": null,
    "round_id": 274719,
    "state_id": 5,
    "venue_id": 8909,
    "name": "Celtic vs Rangers",
    "starting_at": "2022-09-03 11:30:00",
    "result_info": "Celtic won after full-time.",
    "leg": "1/1",
    "details": null,
    "length": 90,
    "placeholder": false,
    "last_processed_at": "2023-03-02 17:47:38",
    "has_odds": true,
    "starting_at_timestamp": 1662204600
  }
}
```

**Key fields:**

| Field               | Type    | Description                                                                                     |
| ------------------- | ------- | ----------------------------------------------------------------------------------------------- |
| `id`                | integer | Unique fixture identifier                                                                       |
| `league_id`         | integer | The league this fixture belongs to                                                              |
| `season_id`         | integer | The season this fixture belongs to                                                              |
| `state_id`          | integer | Current match state (see [States reference](https://docs.sportmonks.com/v3/definitions/states)) |
| `name`              | string  | Display name in "Home vs Away" format                                                           |
| `starting_at`       | string  | Kickoff datetime in UTC                                                                         |
| `result_info`       | string  | Plain-text match result summary                                                                 |
| `has_odds`          | boolean | Whether odds data is available for this fixture                                                 |
| `placeholder`       | boolean | If true, this is a placeholder fixture (e.g. TBD in a cup draw)                                 |
| `last_processed_at` | string  | Last time this fixture received a data update                                                   |

#### Processing the data

**Extract fixture results from a date range:**

```javascript
const data = await api.getFixturesByDateRange('2022-09-01', '2022-09-30');

const results = data.data.map(fixture => ({
  name: fixture.name,
  date: fixture.starting_at,
  result: fixture.result_info || 'Not yet played'
}));
```

**Filter to only completed fixtures:**

```javascript
const completed = data.data.filter(f => f.result_info !== null);
const upcoming = data.data.filter(f => f.result_info === null);
```

**Extract goals and cards from an enriched response:**

```javascript
const fixture = await api.getFixture(18535517, { include: 'events;participants' });

const events = fixture.data.events || [];
const goals = events.filter(e => e.type_id === 14);
const yellowCards = events.filter(e => e.type_id === 83);
const redCards = events.filter(e => e.type_id === 84);
```

### 4. Common pitfalls

#### Pitfall 1: Using livescores for historical or future fixtures

Livescores only cover the 15-minute window around a match. Requesting historical or future fixtures via livescores will return no data.

```javascript
// ❌ Wrong — livescores won't have this match
const match = await fetch('/v3/football/livescores?api_token=...');

// ✅ Correct — fixtures work at any point in time
const match = await fetch('/v3/football/fixtures/18535517?api_token=...');
```

#### Pitfall 2: Including `.type` on large responses

Including `statistics.type` on a paginated endpoint like GET All Fixtures triggers a type lookup for every statistic in every fixture. This is extremely expensive.

```javascript
// ❌ Wrong — slow and will likely hit rate limits
const data = await api.getFixtures({ include: 'statistics.type' });

// ✅ Correct — fetch types once and cache them
const types = await api.getTypes();
const data = await api.getFixtures({ include: 'statistics' });
// Resolve type names locally using your cached types Map
```

#### Pitfall 3: Not handling null fields

Many fixture fields are null before the match is played or if data is unavailable.

```javascript
// ❌ Will throw if result_info is null
const winner = fixture.data.result_info.split(' ')[0];

// ✅ Safe access
const winner = fixture.data.result_info
  ? fixture.data.result_info.split(' ')[0]
  : 'TBD';
```

***

#### Pitfall 4: Fetching fixtures separately when includes cover it

A common pattern is to make separate requests for the fixture, then teams, then events. One request with includes handles all of this.

```javascript
// ❌ Wrong — three requests when one will do
const fixture = await api.getFixture(id);
const scores = await api.getScores(id);
const events = await api.getEvents(id);

// ✅ Correct — one request, all data
const fixture = await api.getFixture(id, {
  include: 'scores;participants;events'
});
```

#### Pitfall 5: Using GET All Fixtures to find fixtures by date

GET All Fixtures returns your entire subscription, which can be thousands of records. Always use the appropriate scoped endpoint.

```javascript
// ❌ Wrong — fetches everything, then filters client-side
const all = await api.getAllFixtures();
const today = all.data.filter(f => f.starting_at.startsWith('2022-09-03'));

// ✅ Correct — only fetches what you need
const today = await api.getFixturesByDate('2022-09-03');
```

### 5. Advanced usage

#### Advanced technique 1: Keeping your database in sync

The GET Latest Updated Fixtures endpoint returns all fixtures updated within the last 10 seconds. Poll this at a sensible interval to keep your local data current without re-fetching everything.

```javascript
async function syncFixtures() {
  const r = await fetch(
    'https://api.sportmonks.com/v3/football/fixtures/latest?api_token=YOUR_TOKEN'
  );
  const data = await r.json();

  for (const fixture of data.data) {
    await db.upsert('fixtures', fixture);
  }
}

setInterval(syncFixtures, 30000);
```

#### Advanced technique 2: Head-to-head combined with statistics

For a pre-match analysis page, combine the H2H endpoint with statistics includes to show not just results but how each team performed in previous meetings.

```javascript
async function getH2HAnalysis(teamA, teamB) {
  const h2h = await api.getHeadToHead(teamA, teamB, {
    include: 'scores;participants;statistics.type;events'
  });

  return h2h.data.map(fixture => ({
    name: fixture.name,
    date: fixture.starting_at,
    result: fixture.result_info,
    goals: fixture.events?.filter(e => e.type_id === 14) || [],
    stats: fixture.statistics || []
  }));
}
```

#### Advanced technique 3: Parallel requests for a match-day dashboard

When building a dashboard that shows multiple data types at once, use `Promise.all` to fetch in parallel rather than in sequence.

```javascript
async function getMatchDayDashboard(fixtureId) {
  const [fixture, standings, h2h] = await Promise.all([
    api.getFixture(fixtureId, { include: 'scores;events;lineups;participants' }),
    api.getStandingsByLeague(501),
    api.getHeadToHead(53, 62)
  ]);

  return { fixture: fixture.data, standings: standings.data, h2h: h2h.data };
}
```

### 6. Common errors

#### 401 Unauthorised

```json
{ "message": "Unauthenticated." }
```

**Cause:** Missing or invalid `api_token`. **Fix:** Check the token is correct and appended to every request. Retrieve it from [MySportmonks](https://my.sportmonks.com/).

#### 404 Not Found

```json
{ "message": "No query results for model..." }
```

**Cause:** The fixture ID does not exist, or the fixture belongs to a league outside your subscription. **Fix:** Verify the ID via the [ID Finder](https://my.sportmonks.com/resources/id-finder) and confirm the league is in your plan.

#### 429 Too Many Requests

```json
{ "message": "Too many requests." }
```

**Cause:** Exceeded your plan's hourly call limit. **Fix:** Implement exponential backoff and cache reference data to reduce call volume. See the [Rate Limiting guide](https://docs.sportmonks.com/v3/api/rate-limit).

#### CORS error (browser only)

```
Access to fetch has been blocked by CORS policy
```

**Cause:** Calling the API directly from the browser exposes your token. **Fix:** Proxy requests through your own backend.

```javascript
// ✅ Backend proxy (Express example)
app.get('/api/fixtures/:id', async (req, res) => {
  const r = await fetch(
    `https://api.sportmonks.com/v3/football/fixtures/${req.params.id}?api_token=${process.env.SPORTMONKS_TOKEN}`
  );
  res.json(await r.json());
});
```

### See also

**Prerequisites**

* [Authentication](https://docs.sportmonks.com/v3/welcome/authentication) : How to authenticate requests
* [Includes](https://docs.sportmonks.com/v3/tutorials-and-guides/tutorials/includes) : How to attach related data
* [Filter and Select Fields](https://docs.sportmonks.com/v3/tutorials-and-guides/tutorials/filter-and-select-fields) : How to narrow responses

**Real-time match data**

* [Livescores](https://docs.sportmonks.com/v3/tutorials-and-guides/tutorials/livescores-and-fixtures/livescores) : Lightweight real-time updates for active matches
* [States](https://docs.sportmonks.com/v3/definitions/states) : Understanding fixture states and when to poll

**Working with fixture data**

* [Statistics](https://docs.sportmonks.com/v3/tutorials-and-guides/tutorials/statistics) : Match and player stats accessible via fixture includes
* [Lineups and Formations](https://docs.sportmonks.com/v3/tutorials-and-guides/tutorials/lineups-and-formations) : Starting XI data attached via the `lineups` include
* [Season Schedule](https://docs.sportmonks.com/v3/tutorials-and-guides/tutorials/season-schedule) : Organise fixtures by round and stage
* [Standings](https://docs.sportmonks.com/v3/tutorials-and-guides/tutorials/standings) : Combine with fixture filters to build league tables in context

**Optimisation**

* [Filter and Select Fields](https://docs.sportmonks.com/v3/tutorials-and-guides/tutorials/filter-and-select-fields) : Optimise responses by selecting specific data
* [Rate Limiting](https://docs.sportmonks.com/v3/api/rate-limit) : Best practices for frequent fixture requests
* [Best Practices](https://docs.sportmonks.com/v3/welcome/best-practices) : Build efficient applications

**Related endpoints**

* [Fixtures Endpoints](https://docs.sportmonks.com/v3/endpoints-and-entities/endpoints/fixtures) : Complete API endpoint reference
* [Livescores Endpoints](https://docs.sportmonks.com/v3/endpoints-and-entities/endpoints/livescores) : Real-time data endpoints

### FAQ

**Q: What's the difference between fixtures and livescores?**

Fixtures give you complete match data at any time (past, present, or future). Livescores are optimised for real-time polling during active matches and only return data in a 15-minute window before kickoff, when the match is inplay and until 15 minutes after the match has concluded. The response structure is identical; the difference is scope and timing.

**Q: How do I find a fixture ID if I don't already have it?**

Use the [GET Fixture by Search](https://docs.sportmonks.com/v3/endpoints-and-entities/endpoints/fixtures/get-fixture-search-by-name) endpoint with a team name, or use the [ID Finder tool](https://my.sportmonks.com/resources/id-finder) in MySportmonks.

**Q: Can I request fixtures for leagues outside my subscription?**

No. The fixtures endpoints only return data for leagues included in your active plan. If a specific league is missing, check your subscription or consider upgrading.

**Q: How do I know if a fixture has been updated recently?**

Check the `last_processed_at` field on any fixture object. You can also use the GET Latest Updated Fixtures endpoint to retrieve all fixtures updated in the last 10 seconds.

**Q: How often is fixture data updated?**

Fixtures are updated in near real-time during live matches. Scores, events, and lineups are typically processed within seconds of an in-game event occurring.

**Q: What does `placeholder: true` mean?**

Placeholder fixtures represent matches where the participants are not yet determined, common in knockout tournament draws. Real team data is populated once the bracket is confirmed.

#### Best practices summary

✅ DO:

* Use the most specific endpoint for your use case (by date, by team, by ID)
* Fetch types once from the Types endpoint and cache them locally
* Use includes to combine related data into a single request
* Proxy API calls through your backend to protect your token
* Use GET Latest Updated Fixtures for incremental database sync

❌ DON'T:

* Use livescores for historical or future fixture data
* Include `.type` on large paginated requests
* Call GET All Fixtures when a scoped endpoint is available
* Expose your API token in frontend code
* Make separate requests for data that includes can provide in one call
