# Pagination

> **✅ Included in All Plans**
>
> Pagination is included at no additional cost.
>
> Plans differ only in leagues and API call limits.
>
> [Compare plans →](https://www.sportmonks.com/football-api/plans-pricing/)

This page explains how pagination works in the Sportmonks Football API v3 and how to walk through large result sets using cursor-based pagination.

### Why pagination exists

Responses to broad requests can return thousands of results. Returning everything at once is slow and hard to work with. Instead, the API splits results into pages and gives you a cursor to move to the next one.

### How cursor pagination works

Every paginated response includes a `pagination` object at the end of the response body:

```json
"pagination": {
  "count": 2,
  "per_page": 2,
  "current_page": 1,
  "next_cursor": "WzEsMixbMTk3MTAxMDBdLFtbInNwb3J0cy5maXh0dXJlcy5pZCIsMV1dXQ",
  "has_more": true
}
```

| Field          | Description                                        |
| -------------- | -------------------------------------------------- |
| `count`        | Number of results in this page                     |
| `per_page`     | Page size used in this request                     |
| `current_page` | Page number (always `1` for cursor-based requests) |
| `next_cursor`  | Opaque string to pass in your next request         |
| `has_more`     | `true` if there are more results to fetch          |

#### First request

Make a standard request with no cursor. Use `per_page` to control how many results come back (range: 1-50, default: 25):

```http
https://api.sportmonks.com/v3/football/fixtures
?api_token=YOUR_TOKEN&order=desc&per_page=25
```

#### Subsequent requests

Take the `next_cursor` value from the response and pass it as a `cursor` parameter:

```http
https://api.sportmonks.com/v3/football/fixtures
?api_token=YOUR_TOKEN&order=desc&per_page=25&cursor=WzEsMixbMTk3MTAxMD...
```

Keep repeating this until `has_more` is `false`. At that point you have retrieved all available results.

#### Checking for more results

Always check `has_more` before making the next call. When it is `false`, there is no `next_cursor` to use:

```json
"pagination": {
  "count": 14,
  "per_page": 25,
  "current_page": 1,
  "next_cursor": null,
  "has_more": false
}
```

### Code examples

```javascript
async function fetchAllFixtures(apiToken) {
  const baseUrl = 'https://api.sportmonks.com/v3/football/fixtures';
  let cursor = null;
  let allFixtures = [];

  do {
    const url = new URL(baseUrl);
    url.searchParams.set('api_token', apiToken);
    url.searchParams.set('order', 'desc');
    url.searchParams.set('per_page', '25');
    if (cursor) url.searchParams.set('cursor', cursor);

    const response = await fetch(url.toString());
    const data = await response.json();

    allFixtures = allFixtures.concat(data.data);
    cursor = data.pagination.has_more ? data.pagination.next_cursor : null;
  } while (cursor);

  return allFixtures;
}
```

```php
function fetchAllFixtures(string $apiToken): array {
    $baseUrl = 'https://api.sportmonks.com/v3/football/fixtures';
    $cursor = null;
    $allFixtures = [];

    do {
        $params = [
            'api_token' => $apiToken,
            'order'     => 'desc',
            'per_page'  => 25,
        ];
        if ($cursor) $params['cursor'] = $cursor;

        $url = $baseUrl . '?' . http_build_query($params);
        $response = json_decode(file_get_contents($url), true);

        $allFixtures = array_merge($allFixtures, $response['data']);
        $cursor = $response['pagination']['has_more']
            ? $response['pagination']['next_cursor']
            : null;
    } while ($cursor);

    return $allFixtures;
}
```

#### cURL

First page:

```bash
curl "https://api.sportmonks.com/v3/football/fixtures
?api_token=YOUR_TOKEN&order=desc&per_page=25"
```

Next page (replace `CURSOR_VALUE` with the value of `next_cursor` from the previous response):

```bash
curl "https://api.sportmonks.com/v3/football/fixtures
?api_token=YOUR_TOKEN&order=desc&per_page=25&cursor=CURSOR_VALUE"
```

### Initial data load

To seed a local database quickly, add `filters=populate` to your request. This raises the page size limit to 1,000 and strips all includes so responses are as lightweight as possible:

```
https://api.sportmonks.com/v3/football/fixtures?api_token=YOUR_TOKEN&filters=populate
```

> **Note:** includes are disabled when `filters=populate` is set. Use this only for bulk data loads where you want base entity fields only.

### Rate limits

Every page request counts as one API call against your rate limit. Fetching 10 pages costs 10 calls. If you need to bulk-load data, use `filters=populate` with a large `per_page` to minimise the number of calls required.

### Backwards compatibility

The original `page` and `next_page` approach continues to work for existing integrations. New integrations should use cursor pagination, which is faster and puts less load on both your client and our infrastructure.

### Related

* [Best practices](https://docs.sportmonks.com/football/welcome/best-practices)
* [Rate limiting](https://docs.sportmonks.com/football/welcome/rate-limiting)
* [Fixtures endpoint](https://claude.ai/v3/endpoints-and-entities/entities/fixture.md)


---

# 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/introduction/pagination.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.
