# Списки и пагинация

> Форма любого списка, страницы и курсор, разделы и выбор полей.

> Оглавление документации для агентов: https://kinodata.space/ru/llms.txt — вся документация одним файлом: https://kinodata.space/ru/llms-full.txt

Все списки API устроены одинаково: каталог и поиск, чарты, подборки, состав, медиа, фильмография. Разобрав форму один раз, вы прочитаете любой список.

## Форма списка

```bash
curl -G -H "X-API-Key: $KINODATA_KEY" \
  --data-urlencode "q=матрица" \
  --data-urlencode "limit=3" \
  --data-urlencode "fields=title,year,rating_imdb" \
  "https://api.kinodata.space/v1.3/titles"
```

Ответ `200`:

```json
{
  "has_more": true,
  "items": [
    {
      "imdb_id": "tt0133093",
      "kp_id": "301",
      "tmdb_id": "603",
      "tmdb_tv_id": null,
      "kinorium_id": "116780",
      "title_ru": "Матрица",
      "title_en": "The Matrix",
      "year": 1999,
      "rating_imdb": 8.7
    },
    {
      "imdb_id": "tt0234215",
      "kp_id": "299",
      "tmdb_id": "604",
      "tmdb_tv_id": null,
      "kinorium_id": "190315",
      "title_ru": "Матрица: Перезагрузка",
      "title_en": "The Matrix Reloaded",
      "year": 2003,
      "rating_imdb": 7.2
    },
    {
      "imdb_id": "tt0242653",
      "kp_id": "316",
      "tmdb_id": "605",
      "tmdb_tv_id": null,
      "kinorium_id": "196883",
      "title_ru": "Матрица: Революция",
      "title_en": "The Matrix Revolutions",
      "year": 2003,
      "rating_imdb": 6.7
    }
  ],
  "limit": 3,
  "offset": 0,
  "total": null
}
```

| поле | что это |
|---|---|
| `items` | Элементы списка. |
| `total` | Сколько элементов всего. `null`, если API их не считает — об этом ниже. |
| `limit` | Сколько элементов на странице. Если вы попросили больше, чем можно, здесь будет настоящее число. |
| `offset` | Сколько элементов пропущено от начала. |
| `has_more` | Есть ли следующая страница. |
| `next_cursor` | Курсор следующей страницы. Только когда вы листаете курсором. |

Поля самого ресурса приходят рядом: `roles` у состава и фильмографии, `counts` у связей и текстов, `collection` у подборки.

## Когда total равен null

Список бывает двух видов.

- **Набор задан адресом**: состав тайтла, его связи и медиа, фильмография человека, подборка. Такой набор конечен, и `total` — число.
- **Набор задан запросом**: каталог и поиск, топ, скрытые жемчужины, поиск людей. Здесь `total: null`: точный подсчёт по фильтру — второй проход по всей базе на каждой странице. Чтобы понять, есть ли ещё, смотрите на `has_more`. Расхождения оценок и календарь релизов считают `total` и с фильтрами.

## Сколько элементов на странице

| список | по умолчанию | максимум |
|---|---|---|
| Каталог и поиск, чарты, витрины, календарь релизов, подборки, поиск людей | 50 | 50 |
| Состав, медиа, тексты, связи, серии, фильмография, совместные работы | 1000 | 1000 |
| Разделы, когда список разбит на них | 10 в разделе | 50 в разделе |

Набор, заданный адресом, приходит целиком: это и есть ответ. Набор, заданный запросом, отсортирован, и на странице приходит его начало.

`limit` — целое число от 1, `offset` — от 0. Число больше максимума не ошибка: API уменьшит его и вернёт настоящее значение в `limit`. `limit=5000` в каталоге даст `"limit": 50`. Ноль, отрицательное число или не число — ошибка `400`.

## Страницы: offset

```text
?limit=50&offset=100
```

`offset` — сколько элементов пропустить. Следующая страница — `offset` плюс `limit`, пока `has_more` равно `true`.

## Глубокая прокрутка: cursor

Для долгого листания каталога `/titles` берите курсор: он не сбивается, если база меняется, пока вы листаете. Передайте пустой `cursor=` — в ответе появится `next_cursor`:

```bash
curl -G -H "X-API-Key: $KINODATA_KEY" \
  --data-urlencode "sort=-rating_imdb" \
  --data-urlencode "votes_imdb.gte=100000" \
  --data-urlencode "limit=2" \
  --data-urlencode "fields=title,rating_imdb" \
  --data-urlencode "cursor=" \
  "https://api.kinodata.space/v1.3/titles"
```

Ответ `200`:

```json
{
  "has_more": true,
  "items": [
    {
      "imdb_id": "tt0903747",
      "kp_id": "404900",
      "tmdb_id": null,
      "tmdb_tv_id": "1396",
      "kinorium_id": "400796",
      "title_ru": "Во все тяжкие",
      "title_en": "Breaking Bad",
      "rating_imdb": 9.5
    },
    {
      "imdb_id": "tt5491994",
      "kp_id": "1007472",
      "tmdb_id": null,
      "tmdb_tv_id": "68595",
      "kinorium_id": "1605088",
      "title_ru": "Планета Земля 2",
      "title_en": "Planet Earth II",
      "rating_imdb": 9.4
    }
  ],
  "limit": 2,
  "next_cursor": "eyJzIjoicmF0aW5nX2ltZGIiLCJkIjp0cnVlLCJ2IjoiOS40MCIsImlkIjoyMjA4MDF9",
  "total": null
}
```

Передавайте `next_cursor` в следующий запрос с теми же параметрами, пока он не станет `null`. Курсор работает в каталоге `/titles` и в витринах, которые на нём построены, — топе и скрытых жемчужинах. Остальные списки листаются `offset`, а на `cursor` отвечают `400`.

С `sort=random` листать нельзя: у случайного порядка нет следующей страницы. `cursor` и `offset` с ним дают `400` — просто повторите запрос, чтобы получить новую выборку.

## Разделы

Списки, элементы которых различаются по одному признаку, приходят разделами. Каждый раздел — сам список, с теми же полями и подписью на двух языках:

```bash
curl -G -H "X-API-Key: $KINODATA_KEY" \
  --data-urlencode "limit=1" \
  --data-urlencode "fields=title" \
  "https://api.kinodata.space/v1.3/titles/kp/301/relations"
```

Ответ `200`:

```json
{
  "counts": {
    "reference": 86,
    "referenced_in": 672,
    "related": 9,
    "similar": 74
  },
  "has_more": false,
  "items": [
    {
      "has_more": true,
      "items": [
        {
          "imdb_id": "tt1375666",
          "kp_id": "447301",
          "tmdb_id": "27205",
          "tmdb_tv_id": null,
          "kinorium_id": "472809",
          "title_ru": "Начало",
          "title_en": "Inception"
        }
      ],
      "label_en": "Similar",
      "label_ru": "Похожие",
      "relation": "similar",
      "total": 74
    },
    {
      "has_more": true,
      "items": [
        {
          "imdb_id": "tt0366179",
          "kp_id": "63744",
          "tmdb_id": "24358",
          "tmdb_tv_id": null,
          "kinorium_id": null,
          "title_ru": "Аниматрица: Второе возрождение – Часть 1",
          "title_en": "The Second Renaissance Part I"
        }
      ],
      "label_en": "Related",
      "label_ru": "Связанные",
      "relation": "related",
      "total": 9
    },
    {
      "has_more": true,
      "items": [
        {
          "imdb_id": "tt1431045",
          "kp_id": "462360",
          "tmdb_id": "293660",
          "tmdb_tv_id": null,
          "kinorium_id": "485426",
          "title_ru": "Дэдпул",
          "title_en": "Deadpool"
        }
      ],
      "label_en": "Referenced in",
      "label_ru": "Упоминается в",
      "relation": "referenced_in",
      "total": 672
    },
    {
      "has_more": true,
      "items": [
        {
          "imdb_id": "tt0060196",
          "kp_id": "349",
          "tmdb_id": "429",
          "tmdb_tv_id": null,
          "kinorium_id": "56474",
          "title_ru": "Хороший, плохой, злой",
          "title_en": "The Good, the Bad and the Ugly"
        }
      ],
      "label_en": "References",
      "label_ru": "Отсылки к",
      "relation": "reference",
      "total": 86
    }
  ],
  "limit": 1,
  "offset": 0,
  "total": 4
}
```

| список | разделы по умолчанию | другие варианты `group` |
|---|---|---|
| Состав `/titles/{type}/{id}/cast` | по роли: `role` | `none` |
| Тексты `/titles/{type}/{id}/texts` | по виду: `kind` | `none` |
| Связи `/titles/{type}/{id}/relations` | по связи: `relation` | `none` |
| Медиа `/titles/{type}/{id}/media` | без разделов | `type`, `class`, `language` |
| Фильмография `/persons/{type}/{id}/filmography` | без разделов | `role` |
| Серии `/titles/{type}/{id}/episodes` | без разделов | `season` — сводка по сезонам |

`limit` в разделённом списке относится к каждому разделу, а `total` раздела — его настоящий размер. Чтобы получить плоский список, передайте `group=none`. Чтобы листать один раздел, сузьте список до него: `role=actor`, `kind=fact`, `relation=similar`.

Листать разделённый список нельзя: у разделов нет общей последовательности. `offset` без сужения даёт `400`:

```bash
curl -G -H "X-API-Key: $KINODATA_KEY" \
  --data-urlencode "limit=2" \
  --data-urlencode "offset=2" \
  "https://api.kinodata.space/v1.3/titles/kp/447301/cast"
```

Ответ `400`:

```json
{
  "error": {
    "code": "invalid_request",
    "message": "grouped lists have no single sequence to page through — narrow to one value to page inside it, or ask for group=none",
    "field": "offset"
  }
}
```

## Только нужные поля

Параметр `fields` оставляет в ответе то, что вы перечислили.

- **В карточке тайтла** имена — блоки карточки: `fields=title,ratings`. Можно и наоборот, убрать лишнее: `fields=-akas,-tags`. Смешивать в одном параметре нельзя: `fields=title,-akas` даёт `400`.
- **В списках** имена — поля элемента списка: `fields=title,year,rating_imdb` или `fields=-genres,-countries`. `title` оставляет `title_ru` и `title_en`, `poster_url` — `poster_url_ru` и `poster_url_en`.

ID тайтла приходят всегда, даже если вы их не перечислили: без них элемент не с чем связать. Поля идут в том же порядке, что и без `fields`. Неизвестное имя — `400` со списком допустимых.

`fields` работает в карточке тайтла, во всех списках тайтлов — каталоге, подборке, связях, фильмографии, совместных работах, витринах и календаре — и в поиске людей. В составе, медиа, текстах и сериях его нет: там `fields` даёт `400`.

## Дальше

- [Фильтры и сортировка](https://kinodata.space/ru/docs/filters.md) — как выбрать и упорядочить элементы.
- [Каталог и поиск](https://kinodata.space/ru/docs/titles.md) — главный список API.
