# Livescores

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

**Quick summary:** This tutorial covers the three livescore endpoints, when to use each one, how to enrich real-time responses, and how to filter by specific leagues or fixtures. It also explains when fixtures are the better choice.

**What you'll learn:**

* The difference between the three livescore endpoints and when to use each
* How to enrich livescore responses with scores, events, and team data
* How to filter livescores by league or fixture ID
* When to switch from livescores to fixtures

**Time to complete:** 15 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

Livescores are optimised for tracking active matches in real time. They return the same data structure as fixtures, but only cover matches within a 15-minute window around kickoff, from 15 minutes before the match starts to 15 minutes after it ends.

#### Livescores vs fixtures: when to use what

| Use case                         | Use            | Why                                                          |
| -------------------------------- | -------------- | ------------------------------------------------------------ |
| Live match tracker or scoreboard | **Livescores** | Lightweight payload, updated frequently, only active matches |
| Match detail page                | **Fixtures**   | Complete information: venue, officials, detailed metadata    |
| Pre-match information            | **Fixtures**   | Livescores only populate 15 minutes before kickoff           |
| Post-match analysis              | **Fixtures**   | Full statistics and detailed data                            |
| Historical matches               | **Fixtures**   | Livescores are not available outside the match window        |

{% hint style="info" %}
**Recommended workflow:** Use livescores for real-time updates in your live tracker. Switch to the full fixture endpoint when a user clicks for match details or when the match ends.
{% endhint %}

#### Real-world use cases

**Use case 1: Live scoreboard widget** You're building a widget that shows all currently active matches with live scores, match minutes, and events. The GET All Inplay Livescores endpoint gives you exactly this, only matches that are live right now (or about to kick off within 15 minutes).

**Use case 2: Today's match schedule** You want to show all matches happening today, including those that haven't started yet. GET All Livescores (without the `/inplay` suffix) returns all fixtures for the current day.

**Use case 3: Incremental live updates** You're polling frequently and want to minimise payload size. GET Latest Updated Livescores returns only the matches that received a data update within the last 10 seconds, goals, cards, substitutions, and score changes.

#### When NOT to use this feature

* **Past or future matches** : Livescores are not available outside the 15-minute match window. Use [Fixtures](https://docs.sportmonks.com/v3/tutorials-and-guides/tutorials/livescores-and-fixtures/fixtures) instead.
* **Complete match metadata** : Livescores return a lean response. For venue, officials, and detailed match data, fetch the full fixture by ID using the same `id` field in the response.

### 2. How to retrieve data

#### Understanding the endpoints

**Base URL:**

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

**Available endpoints:**

| Endpoint                      | Path suffix  | Description                                                                |
| ----------------------------- | ------------ | -------------------------------------------------------------------------- |
| GET All Inplay Livescores     | `/inplay`    | Only matches currently in play (±15 min window before and after the match) |
| GET All Livescores            | *(base URL)* | All fixtures for the current day                                           |
| GET Latest Updated Livescores | `/latest`    | Fixtures updated in 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,starting_at`           |
| `filters`   | string | No       | Filter results by league or fixture ID | `fixtureLeagues:172`         |

#### Step-by-step implementation

**Step 1: GET All Inplay Livescores**

Returns all fixtures currently in the livescore window (15 minutes before kickoff) through 15 minutes after the final whistle.

**JavaScript:**

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

```javascript
const apiToken = 'YOUR_API_TOKEN';

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

{% endtab %}

{% tab title="Python" %}

```python
import requests

def get_inplay_livescores(api_token):
    response = requests.get(
        'https://api.sportmonks.com/v3/football/livescores/inplay',
        params={'api_token': api_token},
        timeout=30
    )
    response.raise_for_status()
    return response.json()
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
function getInplayLivescores($apiToken) {
    $url = "https://api.sportmonks.com/v3/football/livescores/inplay?api_token={$apiToken}";
    $response = file_get_contents($url);
    return json_decode($response, true);
}

$data = getInplayLivescores('YOUR_API_TOKEN');
print_r($data);
?>
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
**Free plan note:** If you're on the free plan, the inplay response will contain matches from the Danish Superliga and Scottish Premiership only.
{% endhint %}

**Step 2: GET All Livescores**

Returns all fixtures for the current day, including matches that have not yet kicked off. This is the right endpoint for a full today's matches view.

```http
https://api.sportmonks.com/v3/football/livescores
?api_token=YOUR_API_TOKEN
```

The response uses the same data structure as fixtures. The number of results will be higher than the inplay endpoint.

**Step 3: GET Latest Updated Livescores**

Returns only the livescores that received a data update within the last 10 seconds. This is the most efficient endpoint for high-frequency polling, you only process what changed.

```http
https://api.sportmonks.com/v3/football/livescores/latest
?api_token=YOUR_API_TOKEN
```

**JavaScript:**

```javascript
async function pollLatestLivescores(intervalMs = 10000) {
  const poll = async () => {
    const response = await fetch(
      `https://api.sportmonks.com/v3/football/livescores/latest?api_token=${apiToken}`
    );
    const data = await response.json();
    processUpdates(data.data);
  };

  setInterval(poll, intervalMs);
}
```

{% hint style="danger" %}
Do not poll faster than 10 seconds. The endpoint updates on a 10-second cycle, and more frequent requests will produce duplicate data while consuming your rate limit.
{% endhint %}

**Step 4: Adding includes**

Livescore responses return only IDs and basic match data by default. Use `include` to attach scores, teams, events, and other related data.

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

The most commonly used includes for livescores are:

* `scores` : Current and half-time scores
* `participants` : Home and away team names and logos
* `events` : Goals, cards, and substitutions as they happen
* `statistics.type` : Live match stats
* `lineups` : Starting XI (available once confirmed)

**Step 5: Filtering by league**

If you only want livescores for a specific league, use the `filters` parameter with the `fixtureLeagues` filter. This is especially useful if your plan covers hundreds of leagues but your product focuses on one.

```http
https://api.sportmonks.com/v3/football/livescores/inplay
?api_token=YOUR_API_TOKEN&filters=fixtureLeagues:172&include=participants;scores
```

Example league IDs: Danish Superliga = `172`, Scottish Premiership = `501`, English Premier League = `8`.

**Step 6: Complete example — live tracker**

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

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

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

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

  startPolling(leagueId = null, intervalMs = 10000) {
    const filters = leagueId ? { filters: `fixtureLeagues:${leagueId}` } : {};
    const includes = { include: 'scores;participants;events' };

    const poll = async () => {
      try {
        const data = await this.getLatest({ ...filters, ...includes });
        this.onUpdate(data.data);
      } catch (err) {
        console.error('Livescore poll error:', err.message);
      }
    };

    poll();
    return setInterval(poll, intervalMs);
  }

  onUpdate(fixtures) {
    console.log('Updated fixtures:', fixtures.length);
  }
}

// Usage
const tracker = new LivescoreTracker('YOUR_API_TOKEN');
tracker.startPolling(501);
```

{% endtab %}

{% tab title="Python" %}

```python
import requests
import time

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

    def get_latest(self, params=None):
        params = params or {}
        params['api_token'] = self.token
        r = requests.get(f'{self.base_url}/latest', params=params, timeout=30)
        r.raise_for_status()
        return r.json()

    def poll(self, league_id=None, interval=10):
        params = {'include': 'scores;participants;events'}
        if league_id:
            params['filters'] = f'fixtureLeagues:{league_id}'

        while True:
            data = self.get_latest(params)
            print(f"Updated fixtures: {len(data.get('data', []))}")
            time.sleep(interval)

tracker = LivescoreTracker('YOUR_API_TOKEN')
tracker.poll(league_id=501)
```

{% endtab %}

{% tab title="PHP" %}

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

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

    public function getLatest($params = []) {
        $params['api_token'] = $this->token;
        $url = "{$this->baseURL}/latest?" . 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);
    }
}

$tracker = new LivescoreTracker('YOUR_API_TOKEN');
$data = $tracker->getLatest([
    'include' => 'scores;participants;events',
    'filters' => 'fixtureLeagues:501'
]);
print_r($data);
?>
```

{% endtab %}
{% endtabs %}

### 3. Working with the data

#### Understanding the response structure

The livescore response uses the same structure as fixtures. The key fields to monitor during a live match are:

```json
{
  "data": {
    "id": 18535517,
    "sport_id": 1,
    "league_id": 501,
    "season_id": 19735,
    "state_id": 5,
    "name": "Celtic vs Rangers",
    "starting_at": "2022-09-03 11:30:00",
    "result_info": "Celtic won after full-time.",
    "has_odds": true,
    "last_processed_at": "2023-03-02 17:47:38",
    "starting_at_timestamp": 1662204600
  }
}
```

**Fields most relevant to live tracking:**

| Field               | Type    | Description                                                                                     |
| ------------------- | ------- | ----------------------------------------------------------------------------------------------- |
| `id`                | integer | Unique fixture ID — use this to fetch full details from the fixtures endpoint                   |
| `state_id`          | integer | Current match state (see [States reference](https://docs.sportmonks.com/v3/definitions/states)) |
| `last_processed_at` | string  | Last time this fixture was updated                                                              |
| `result_info`       | string  | Plain-text result summary (null during live matches)                                            |

#### Processing the data

**Display a live scoreboard:**

```javascript
const data = await tracker.getInplay({ include: 'scores;participants' });

const scoreboard = data.data.map(fixture => {
  const home = fixture.participants?.find(p => p.meta?.location === 'home');
  const away = fixture.participants?.find(p => p.meta?.location === 'away');
  const score = fixture.scores?.find(s => s.description === 'CURRENT');

  return {
    home: home?.name || 'Home',
    away: away?.name || 'Away',
    score: score ? `${score.score.goals} - ${score.score.participant}` : '0 - 0'
  };
});
```

**Detect goals from events:**

```javascript
const data = await tracker.getLatest({ include: 'events;participants' });

for (const fixture of data.data) {
  const goals = (fixture.events || []).filter(e => e.type_id === 14);
  if (goals.length) {
    console.log(`Goal in ${fixture.name}:`, goals.map(g => g.player_name));
  }
}
```

### 4. Common pitfalls

#### Pitfall 1: Expecting livescores outside the match window

Livescores are unavailable before the 15-minute pre-match window and disappear 15 minutes after the final whistle.

```javascript
// ❌ Returns nothing for a match starting in 2 hours
const data = await getLivescores();

// ✅ For future matches, use the fixtures endpoint
const upcoming = await fetch('/v3/football/fixtures/date/2026-06-14?api_token=...');
```

#### Pitfall 2: Polling the inplay endpoint too frequently

The inplay endpoint is not a WebSocket — it does not push updates. Polling faster than every 10 seconds gives you duplicate data and burns through your rate limit.

```javascript
// ❌ Too frequent — data does not change this fast
setInterval(fetchInplay, 1000);

// ✅ Use /latest at 10-second intervals instead
setInterval(fetchLatest, 10000);
```

#### Pitfall 3: Not accounting for an empty response

Outside of active match windows (e.g. early morning), the inplay endpoint may return an empty `data` array. Always check before iterating.

```javascript
// ❌ Will throw if data is empty
const firstName = data.data[0].name;

// ✅ Safe access
if (data.data && data.data.length > 0) {
  console.log('Matches in progress:', data.data.length);
} else {
  console.log('No matches currently live.');
}
```

***

#### Pitfall 4: Fetching full fixture data for every livescore update

If you're polling every 10 seconds and making an additional fixture request for each match in the response, you'll exhaust your rate limit quickly.

```javascript
// ❌ Wrong — N fixture requests per poll cycle
const live = await getLivescores({ include: '' });
for (const match of live.data) {
  const full = await getFixture(match.id, { include: 'scores;events;lineups' });
}

// ✅ Correct — use includes on the livescore request itself
const live = await getLivescores({
  include: 'scores;events;lineups;participants'
})
```

### 5. Advanced usage

#### Advanced technique 1: Hybrid live + detail pattern

Use the livescore `/latest` endpoint for frequent polling, then fetch the full fixture on demand when a user opens a match detail view.

```javascript
const poller = setInterval(async () => {
  const updates = await tracker.getLatest({ include: 'scores;events' });
  updateScoreboard(updates.data);
}, 10000);

async function openMatchDetail(fixtureId) {
  clearInterval(poller);
  const detail = await api.getFixture(fixtureId, {
    include: 'scores;participants;events;lineups;statistics.type'
  });
  renderMatchDetail(detail.data);
}
```

#### Advanced technique 2: Multi-league filtering with parallel requests

If your product shows multiple leagues with different priorities, fetch them in parallel and merge results.

```javascript
async function getMultiLeagueLivescores(leagueIds) {
  const requests = leagueIds.map(id =>
    fetch(
      `https://api.sportmonks.com/v3/football/livescores/inplay?api_token=${token}&filters=fixtureLeagues:${id}&include=scores;participants`
    ).then(r => r.json())
  );

  const results = await Promise.all(requests);
  return results.flatMap(r => r.data || []);
}

const fixtures = await getMultiLeagueLivescores([8, 501, 172]);
```

### 6. Common errors

#### 401 Unauthorised

**Cause:** Missing or invalid `api_token`. **Fix:** Confirm the token is appended to every request. Find it at [MySportmonks](https://my.sportmonks.com/).

#### 429 Too Many Requests

**Cause:** Polling too frequently or making too many enriched requests in parallel. **Fix:** Switch to the `/latest` endpoint, reduce polling frequency, and implement exponential backoff. See the [Rate Limiting guide](https://docs.sportmonks.com/v3/api/rate-limit).

#### Empty response (no error, but `data: []`)

**Cause:** No matches are currently within the livescore window. This is expected outside of typical match hours. **Fix:** Handle empty arrays gracefully and show an appropriate message to users.

#### CORS error (browser only)

**Cause:** Calling the API directly from the browser exposes your token. **Fix:** Route all API calls through a backend proxy.

```javascript
// ✅ Backend proxy (Node.js/Express)
app.get('/api/livescores', async (req, res) => {
  const r = await fetch(
    `https://api.sportmonks.com/v3/football/livescores/inplay?api_token=${process.env.SPORTMONKS_TOKEN}&include=scores;participants`
  );
  res.json(await r.json());
});
```

### See also

**Prerequisites**

* [Authentication](https://docs.sportmonks.com/v3/welcome/authentication) : Setting up API access
* [Includes](https://docs.sportmonks.com/v3/tutorials-and-guides/tutorials/includes) : Enriching your responses
* [Filter and Select Fields](https://docs.sportmonks.com/v3/tutorials-and-guides/tutorials/filter-and-select-fields) : Filtering by league or fixture

**Understanding the full picture**

* [Fixtures](https://docs.sportmonks.com/v3/tutorials-and-guides/tutorials/livescores-and-fixtures/fixtures) : Complete match data at any time; use for match detail views
* [States](https://docs.sportmonks.com/v3/definitions/states) : Understanding match states (NS, LIVE, HT, FT, etc.)

**Building real-time apps**

* [Rate Limiting](https://docs.sportmonks.com/v3/api/rate-limit) : Optimise polling frequency for livescores
* [Best Practices](https://docs.sportmonks.com/v3/welcome/best-practices) : Build efficient livescore applications
* [Lineups and Formations](https://docs.sportmonks.com/v3/tutorials-and-guides/tutorials/lineups-and-formations) : Attach confirmed lineups to your live view

**Related endpoints**

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

### 8. FAQ

**Q: What does the 15-minute window mean exactly?**

A match appears in the livescore endpoints from 15 minutes before its scheduled kickoff time and remains there until 15 minutes after the final whistle. Outside this window, use the fixtures endpoint with the match ID to access the data.

**Q: Why is my livescore response empty even though there are matches today?**

Either no matches in your subscription are currently within the 15-minute window, or you're using the `/inplay` endpoint (which only shows active matches) rather than the base livescores endpoint (which shows all of today's fixtures).

**Q: The livescore response looks the same as a fixture. What's the difference?**

The data structure is identical. Livescores are a filtered, real-time view of the fixtures data, optimised for polling during active matches. Any livescore record can be fetched at any time via the fixtures endpoint using the same `id`.

**Q: How do I show the current score?**

Include `scores` in your request and look for the entry with `description: "CURRENT"` in the scores array.

**Q: Is livescore data available on all plans?**

Yes. All plans include livescore access. The difference is which leagues are available based on your plan. Check your subscription via [MySportmonks](https://my.sportmonks.com/).

#### Best practices summary

✅ DO:

* Use `/latest` for high-frequency polling, it returns only what changed
* Use `filters=fixtureLeagues:{id}` to narrow results to leagues you care about
* Include `scores;participants;events` in your live requests to avoid follow-up calls
* Handle empty responses gracefully
* Switch to the fixtures endpoint for match details

❌ DON'T:

* Poll more frequently than every 10 seconds
* Use livescores for past or future match data
* Make additional fixture requests per match inside your polling loop
* Expose your API token in frontend JavaScript
* Assume the response will always contain data


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.sportmonks.com/v3/tutorials-and-guides/tutorials/livescores-and-fixtures/livescores.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
