{
  "openapi": "3.1.0",
  "info": {
    "title": "kinodata API",
    "version": "1.2",
    "summary": "Идентификаторы фильмов и сериалов, метаданные, каталог, люди, подборки.",
    "description": "Машинный справочник. Человеческая документация — https://docs.kinodata.space/\n\nКаждый запрос несёт ключ в заголовке `X-API-Key`; исключение одно — `/health`.\n\nУспех отдаётся телом напрямую, ошибка — объектом `error` и не-2xx статусом. Различать следует по HTTP-статусу, а внутри ошибок ветвиться на `error.code`.",
    "contact": {
      "name": "docs.kinodata.space",
      "url": "https://docs.kinodata.space/"
    }
  },
  "servers": [
    {
      "url": "https://api.kinodata.space/v1.2",
      "description": "Рабочий сервер"
    }
  ],
  "tags": [
    {
      "name": "Идентификаторы",
      "description": "Связка номеров между каталогами."
    },
    {
      "name": "Тайтлы",
      "description": "Карточка тайтла и её подресурсы."
    },
    {
      "name": "Каталог",
      "description": "Поиск, фильтрация и значения фасетов."
    },
    {
      "name": "Люди",
      "description": "Поиск человека, профиль, фильмография."
    },
    {
      "name": "Подборки",
      "description": "Именованные списки тайтлов."
    },
    {
      "name": "Витрины",
      "description": "Готовые срезы базы."
    },
    {
      "name": "Справочные данные",
      "description": "Перечисления и проверка доступности."
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "paths": {
    "/ids/{type}/{id}": {
      "get": {
        "tags": [
          "Идентификаторы"
        ],
        "summary": "Номера тайтла во всех каталогах",
        "description": "Отдаёт все известные номера тайтла по номеру в одном из каталогов. Ответ 404 означает «связка не построена», а не «такого фильма нет».",
        "operationId": "getIds",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "description": "Каталог, которому принадлежит номер.",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "imdb",
                "kp",
                "tmdb",
                "tmdb_tv",
                "kinorium"
              ]
            },
            "example": "tmdb"
          },
          {
            "name": "id",
            "in": "path",
            "description": "Номер тайтла в этом каталоге.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "27205"
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ids"
                }
              }
            }
          },
          "400": {
            "description": "Запрос не разобран: неизвестное значение перечисления, нечисловое число, несовместимые параметры.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Ключ отсутствует, неизвестен или отозван.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Такого идентификатора нет в базе.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/titles/{type}/{id}": {
      "get": {
        "tags": [
          "Тайтлы"
        ],
        "summary": "Карточка тайтла",
        "description": "Полная карточка: названия, описания, даты, оценки по источникам, жанры, компании, доступность, награды. Блок ids приходит всегда, даже если его не просили.",
        "operationId": "getTitle",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "description": "Каталог, которому принадлежит номер.",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "imdb",
                "kp",
                "tmdb",
                "tmdb_tv",
                "kinorium"
              ]
            },
            "example": "tmdb"
          },
          {
            "name": "id",
            "in": "path",
            "description": "Номер тайтла в этом каталоге.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "27205"
          },
          {
            "name": "lang",
            "in": "query",
            "description": "Язык производных полей и подписей.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "ru",
                "en"
              ],
              "default": "ru"
            }
          },
          {
            "name": "fields",
            "in": "query",
            "description": "Блоки карточки через запятую; со знаком «-» — какие убрать. Смешивать режимы нельзя.",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "title,ratings,release"
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TitleCard"
                }
              }
            }
          },
          "400": {
            "description": "Запрос не разобран: неизвестное значение перечисления, нечисловое число, несовместимые параметры.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Ключ отсутствует, неизвестен или отозван.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Такого идентификатора нет в базе.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/titles/{type}/{id}/cast": {
      "get": {
        "tags": [
          "Тайтлы"
        ],
        "summary": "Состав и съёмочная группа",
        "description": "По умолчанию приходит разделами по ролям, по десять человек в каждом; ?role= — это переход внутрь раздела: плоский список с пагинацией.",
        "operationId": "getCast",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "description": "Каталог, которому принадлежит номер.",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "imdb",
                "kp",
                "tmdb",
                "tmdb_tv",
                "kinorium"
              ]
            },
            "example": "tmdb"
          },
          {
            "name": "id",
            "in": "path",
            "description": "Номер тайтла в этом каталоге.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "27205"
          },
          {
            "name": "role",
            "in": "query",
            "description": "Оставить кредиты одной роли (набор credit_roles).",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "director"
          },
          {
            "name": "source",
            "in": "query",
            "description": "Оставить кредиты одного каталога.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "group",
            "in": "query",
            "description": "Разделы по ролям или плоский список.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "role",
                "none"
              ],
              "default": "role"
            }
          },
          {
            "name": "lang",
            "in": "query",
            "description": "Язык производных полей и подписей.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "ru",
                "en"
              ],
              "default": "ru"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Размер страницы.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1000,
              "minimum": 1,
              "maximum": 1000
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Смещение от начала выборки.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "Запрос не разобран: неизвестное значение перечисления, нечисловое число, несовместимые параметры.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Ключ отсутствует, неизвестен или отозван.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Такого идентификатора нет в базе.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/titles/{type}/{id}/media": {
      "get": {
        "tags": [
          "Тайтлы"
        ],
        "summary": "Изображения и видео",
        "description": "Одна форма на оба вида: у изображений пустуют youtube_id, preview_url и runtime_sec, у видео — width и height.",
        "operationId": "getMedia",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "description": "Каталог, которому принадлежит номер.",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "imdb",
                "kp",
                "tmdb",
                "tmdb_tv",
                "kinorium"
              ]
            },
            "example": "tmdb"
          },
          {
            "name": "id",
            "in": "path",
            "description": "Номер тайтла в этом каталоге.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "27205"
          },
          {
            "name": "class",
            "in": "query",
            "description": "Оставить только изображения или только видео.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "image",
                "video"
              ]
            }
          },
          {
            "name": "type",
            "in": "query",
            "description": "Вид: poster, backdrop, logo, trailer, teaser и прочие из наборов image_types и video_kinds.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "language",
            "in": "query",
            "description": "Язык изображения.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "group",
            "in": "query",
            "description": "Разделы по виду, классу или языку.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "type",
                "class",
                "language",
                "none"
              ]
            }
          },
          {
            "name": "lang",
            "in": "query",
            "description": "Язык производных полей и подписей.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "ru",
                "en"
              ],
              "default": "ru"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Размер страницы.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1000,
              "minimum": 1,
              "maximum": 1000
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Смещение от начала выборки.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "Запрос не разобран: неизвестное значение перечисления, нечисловое число, несовместимые параметры.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Ключ отсутствует, неизвестен или отозван.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Такого идентификатора нет в базе.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/titles/{type}/{id}/texts": {
      "get": {
        "tags": [
          "Тайтлы"
        ],
        "summary": "Факты, вопросы-ответы, цитаты",
        "description": "По умолчанию приходит разделами по виду. counts описывает все три вида независимо от ?kind=.",
        "operationId": "getTexts",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "description": "Каталог, которому принадлежит номер.",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "imdb",
                "kp",
                "tmdb",
                "tmdb_tv",
                "kinorium"
              ]
            },
            "example": "tmdb"
          },
          {
            "name": "id",
            "in": "path",
            "description": "Номер тайтла в этом каталоге.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "27205"
          },
          {
            "name": "kind",
            "in": "query",
            "description": "Виды через запятую.",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "quote"
          },
          {
            "name": "group",
            "in": "query",
            "description": "Разделы по виду или плоский список.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "kind",
                "none"
              ],
              "default": "kind"
            }
          },
          {
            "name": "lang",
            "in": "query",
            "description": "Язык производных полей и подписей.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "ru",
                "en"
              ],
              "default": "ru"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Размер страницы.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1000,
              "minimum": 1,
              "maximum": 1000
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Смещение от начала выборки.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "Запрос не разобран: неизвестное значение перечисления, нечисловое число, несовместимые параметры.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Ключ отсутствует, неизвестен или отозван.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Такого идентификатора нет в базе.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/titles/{type}/{id}/episodes": {
      "get": {
        "tags": [
          "Тайтлы"
        ],
        "summary": "Серии и сезоны",
        "description": "У фильма список пуст — это не ошибка. ?group=season даёт не разделы, а сводку: строку на сезон.",
        "operationId": "getEpisodes",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "description": "Каталог, которому принадлежит номер.",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "imdb",
                "kp",
                "tmdb",
                "tmdb_tv",
                "kinorium"
              ]
            },
            "example": "tmdb"
          },
          {
            "name": "id",
            "in": "path",
            "description": "Номер тайтла в этом каталоге.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "27205"
          },
          {
            "name": "season",
            "in": "query",
            "description": "Номер сезона; 0 — спецвыпуски.",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          },
          {
            "name": "group",
            "in": "query",
            "description": "Сводка по сезонам вместо списка серий.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "season",
                "none"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Размер страницы.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1000,
              "minimum": 1,
              "maximum": 1000
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Смещение от начала выборки.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "Запрос не разобран: неизвестное значение перечисления, нечисловое число, несовместимые параметры.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Ключ отсутствует, неизвестен или отозван.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Такого идентификатора нет в базе.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/titles/{type}/{id}/relations": {
      "get": {
        "tags": [
          "Тайтлы"
        ],
        "summary": "Связи с другими тайтлами",
        "description": "Похожие, части франшизы, отсылки и упоминания. По умолчанию — четыре раздела со своими итогами; сужение до одного вида даёт плоский пагинируемый список. Дополнительно принимает всю фильтрацию каталога: `kind`, `genre`, `country`, `year.gte`/`year.lte`, `rating_imdb.gte`, `votes_imdb.gte` и остальные поля с операторами `.any` `.all` `.not` `.gte` `.lte`.",
        "operationId": "getRelations",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "description": "Каталог, которому принадлежит номер.",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "imdb",
                "kp",
                "tmdb",
                "tmdb_tv",
                "kinorium"
              ]
            },
            "example": "tmdb"
          },
          {
            "name": "id",
            "in": "path",
            "description": "Номер тайтла в этом каталоге.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "27205"
          },
          {
            "name": "relation",
            "in": "query",
            "description": "Виды связей через запятую.",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "related"
          },
          {
            "name": "subtype",
            "in": "query",
            "description": "Подтипы связи related: sequel, prequel, spinoff, parent, chronology, remake, original, version.",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "sequel"
          },
          {
            "name": "source",
            "in": "query",
            "description": "Оставить связи одного каталога.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "group",
            "in": "query",
            "description": "Разделы по виду связи или плоский список.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "relation",
                "none"
              ],
              "default": "relation"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "Ключ сортировки со знаком, как в каталоге.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "lang",
            "in": "query",
            "description": "Язык производных полей и подписей.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "ru",
                "en"
              ],
              "default": "ru"
            }
          },
          {
            "name": "fields",
            "in": "query",
            "description": "Через запятую — какие ключи оставить; со знаком «-» — какие убрать. Смешивать режимы нельзя.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Размер страницы.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 10,
              "minimum": 1,
              "maximum": 1000
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Смещение от начала выборки.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "Запрос не разобран: неизвестное значение перечисления, нечисловое число, несовместимые параметры.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Ключ отсутствует, неизвестен или отозван.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Такого идентификатора нет в базе.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/titles": {
      "get": {
        "tags": [
          "Каталог"
        ],
        "summary": "Поиск и фильтрация по всей базе",
        "description": "Свободный текст, полсотни фильтров, сортировка, пагинация. total здесь всегда null: точный счёт по фильтру — второй полный агрегат по базе. Дополнительно принимает всю фильтрацию каталога: `kind`, `genre`, `country`, `year.gte`/`year.lte`, `rating_imdb.gte`, `votes_imdb.gte` и остальные поля с операторами `.any` `.all` `.not` `.gte` `.lte`.",
        "operationId": "browseTitles",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Свободный поиск по названиям; сортировка становится релевантностью.",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "интерстелар"
          },
          {
            "name": "sort",
            "in": "query",
            "description": "Ключ со знаком: -rating_imdb, +year; запятая добавляет тай-брейкер.",
            "required": false,
            "schema": {
              "type": "string",
              "default": "release_date"
            }
          },
          {
            "name": "preset",
            "in": "query",
            "description": "Готовый набор фильтров.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "top",
                "hidden_gems",
                "trending",
                "new",
                "classics"
              ]
            }
          },
          {
            "name": "lang",
            "in": "query",
            "description": "Язык производных полей и подписей.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "ru",
                "en"
              ],
              "default": "ru"
            }
          },
          {
            "name": "fields",
            "in": "query",
            "description": "Через запятую — какие ключи оставить; со знаком «-» — какие убрать. Смешивать режимы нельзя.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Курсор глубокой прокрутки; пустое значение начинает обход.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Размер страницы.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Смещение от начала выборки.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CardList"
                }
              }
            }
          },
          "400": {
            "description": "Запрос не разобран: неизвестное значение перечисления, нечисловое число, несовместимые параметры.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Ключ отсутствует, неизвестен или отозван.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Превышен предел ключа: запросов в секунду или дневная квота.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/filters": {
      "get": {
        "tags": [
          "Каталог"
        ],
        "summary": "Значения фасетов, диапазоны и пресеты",
        "description": "Всё, из чего строится панель фильтров: значения с числом тайтлов, границы годов и раскрытие каждого пресета.",
        "operationId": "getFilters",
        "parameters": [
          {
            "name": "facet",
            "in": "query",
            "description": "Имена фасетов через запятую.",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "genres"
          },
          {
            "name": "q",
            "in": "query",
            "description": "Искать по названию или слагу значения, на любом языке.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Значений на фасет; 0 снимает предел.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Запрос не разобран: неизвестное значение перечисления, нечисловое число, несовместимые параметры.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Ключ отсутствует, неизвестен или отозван.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/persons": {
      "get": {
        "tags": [
          "Люди"
        ],
        "summary": "Поиск человека по имени",
        "description": "Ищет по русскому, английскому и оригинальному написанию, прощает опечатки. total всегда null — поиск ранжирует, а не считает.",
        "operationId": "searchPersons",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Имя или его часть; достаточно фамилии.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "нолан"
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Размер страницы.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Смещение от начала выборки.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "Запрос не разобран: неизвестное значение перечисления, нечисловое число, несовместимые параметры.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Ключ отсутствует, неизвестен или отозван.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/persons/{type}/{id}": {
      "get": {
        "tags": [
          "Люди"
        ],
        "summary": "Профиль человека",
        "description": "Человек, сводка по ролям за всю карьеру и самые известные работы полными карточками.",
        "operationId": "getPerson",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "description": "Каталог, которому принадлежит номер человека.",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "imdb",
                "kp",
                "tmdb",
                "kinorium"
              ]
            },
            "example": "tmdb"
          },
          {
            "name": "id",
            "in": "path",
            "description": "Номер человека в этом каталоге.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "525"
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "person": {
                      "$ref": "#/components/schemas/Person"
                    },
                    "credits_summary": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "known_for": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Card"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Запрос не разобран: неизвестное значение перечисления, нечисловое число, несовместимые параметры.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Ключ отсутствует, неизвестен или отозван.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Такого идентификатора нет в базе.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/persons/{type}/{id}/filmography": {
      "get": {
        "tags": [
          "Люди"
        ],
        "summary": "Фильмография",
        "description": "Одна карточка на тайтл: все роли человека в нём собраны в credits. roles считает по всей карьере, даже когда задан ?role=. Дополнительно принимает всю фильтрацию каталога: `kind`, `genre`, `country`, `year.gte`/`year.lte`, `rating_imdb.gte`, `votes_imdb.gte` и остальные поля с операторами `.any` `.all` `.not` `.gte` `.lte`.",
        "operationId": "getFilmography",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "description": "Каталог, которому принадлежит номер человека.",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "imdb",
                "kp",
                "tmdb",
                "kinorium"
              ]
            },
            "example": "tmdb"
          },
          {
            "name": "id",
            "in": "path",
            "description": "Номер человека в этом каталоге.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "525"
          },
          {
            "name": "role",
            "in": "query",
            "description": "Оставить тайтлы, где у человека есть эта роль.",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "director"
          },
          {
            "name": "group",
            "in": "query",
            "description": "Разделы по ролям вместо плоского списка.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "role",
                "none"
              ]
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "Ключ со знаком, как в каталоге.",
            "required": false,
            "schema": {
              "type": "string",
              "default": "release_date"
            }
          },
          {
            "name": "lang",
            "in": "query",
            "description": "Язык производных полей и подписей.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "ru",
                "en"
              ],
              "default": "ru"
            }
          },
          {
            "name": "fields",
            "in": "query",
            "description": "Через запятую — какие ключи оставить; со знаком «-» — какие убрать. Смешивать режимы нельзя.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Размер страницы.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1000,
              "minimum": 1,
              "maximum": 1000
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Смещение от начала выборки.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "Запрос не разобран: неизвестное значение перечисления, нечисловое число, несовместимые параметры.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Ключ отсутствует, неизвестен или отозван.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Такого идентификатора нет в базе.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/persons/{type}/{id}/with/{type2}/{id2}": {
      "get": {
        "tags": [
          "Люди"
        ],
        "summary": "Совместные работы двоих",
        "description": "Тайтлы, над которыми работали оба. Без ?role= совпадение считается по любому участию.",
        "operationId": "getTogether",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "description": "Каталог, которому принадлежит номер человека.",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "imdb",
                "kp",
                "tmdb",
                "kinorium"
              ]
            },
            "example": "tmdb"
          },
          {
            "name": "id",
            "in": "path",
            "description": "Номер человека в этом каталоге.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "525"
          },
          {
            "name": "type2",
            "in": "path",
            "description": "Каталог второго человека.",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "imdb",
                "kp",
                "tmdb",
                "kinorium"
              ]
            },
            "example": "tmdb"
          },
          {
            "name": "id2",
            "in": "path",
            "description": "Номер второго человека.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "6193"
          },
          {
            "name": "role",
            "in": "query",
            "description": "Оба в этой роли.",
            "required": false,
            "schema": {
              "type": "string"
            },
            "example": "actor"
          },
          {
            "name": "sort",
            "in": "query",
            "description": "rating — по оценке; иначе новые сверху.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "rating"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Размер страницы.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1000,
              "minimum": 1,
              "maximum": 1000
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Смещение от начала выборки.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CardList"
                }
              }
            }
          },
          "400": {
            "description": "Запрос не разобран: неизвестное значение перечисления, нечисловое число, несовместимые параметры.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Ключ отсутствует, неизвестен или отозван.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Такого идентификатора нет в базе.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/collections": {
      "get": {
        "tags": [
          "Подборки"
        ],
        "summary": "Список подборок",
        "description": "Именованные списки тайтлов, собранные редактором.",
        "operationId": "listCollections",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Размер страницы.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Смещение от начала выборки.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "Запрос не разобран: неизвестное значение перечисления, нечисловое число, несовместимые параметры.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Ключ отсутствует, неизвестен или отозван.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/collections/{slug}": {
      "get": {
        "tags": [
          "Подборки"
        ],
        "summary": "Подборка и её тайтлы",
        "description": "Без параметров тайтлы приходят в порядке редактора, и ord показывает место в нём. Любой фильтр, ?q= или ?sort= превращает состав подборки в область поиска. Дополнительно принимает всю фильтрацию каталога: `kind`, `genre`, `country`, `year.gte`/`year.lte`, `rating_imdb.gte`, `votes_imdb.gte` и остальные поля с операторами `.any` `.all` `.not` `.gte` `.lte`.",
        "operationId": "getCollection",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "description": "Идентификатор подборки из списка.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "all-popular1000"
          },
          {
            "name": "sort",
            "in": "query",
            "description": "Ключ со знаком, как в каталоге.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "lang",
            "in": "query",
            "description": "Язык производных полей и подписей.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "ru",
                "en"
              ],
              "default": "ru"
            }
          },
          {
            "name": "fields",
            "in": "query",
            "description": "Через запятую — какие ключи оставить; со знаком «-» — какие убрать. Смешивать режимы нельзя.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Размер страницы.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Смещение от начала выборки.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "collection": {
                          "$ref": "#/components/schemas/Collection"
                        },
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Card"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Запрос не разобран: неизвестное значение перечисления, нечисловое число, несовместимые параметры.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Ключ отсутствует, неизвестен или отозван.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Такого идентификатора нет в базе.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/charts/top": {
      "get": {
        "tags": [
          "Витрины"
        ],
        "summary": "Лучшее по рейтингу",
        "description": "Чарт среди тайтлов, набравших достаточно голосов: порог votes_imdb.gte по умолчанию 100000 и переопределяется своим значением. Конверт приходит без total. Дополнительно принимает всю фильтрацию каталога: `kind`, `genre`, `country`, `year.gte`/`year.lte`, `rating_imdb.gte`, `votes_imdb.gte` и остальные поля с операторами `.any` `.all` `.not` `.gte` `.lte`.",
        "operationId": "getChartsTop",
        "parameters": [
          {
            "name": "sort",
            "in": "query",
            "description": "Ключ со знаком, как в каталоге.",
            "required": false,
            "schema": {
              "type": "string",
              "default": "-rating"
            }
          },
          {
            "name": "votes_imdb.gte",
            "in": "query",
            "description": "Порог голосов IMDb.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 100000
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Размер страницы.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Смещение от начала выборки.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CardList"
                }
              }
            }
          },
          "400": {
            "description": "Запрос не разобран: неизвестное значение перечисления, нечисловое число, несовместимые параметры.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Ключ отсутствует, неизвестен или отозван.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Превышен предел ключа: запросов в секунду или дневная квота.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/releases": {
      "get": {
        "tags": [
          "Витрины"
        ],
        "summary": "Календарь выхода",
        "description": "Что выходит в заданном окне дат. У карточки появляется поле date — дата события в окне. Фильтры карточки сужают уже набранную страницу, а total описывает окно до них.",
        "operationId": "getReleases",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "Начало окна; по умолчанию сегодня.",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-09-01"
          },
          {
            "name": "to",
            "in": "query",
            "description": "Конец окна; по умолчанию +30 дней.",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-09-30"
          },
          {
            "name": "region",
            "in": "query",
            "description": "Какая дата считается датой события.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "ru",
                "world",
                "digital"
              ]
            },
            "example": "ru"
          },
          {
            "name": "released",
            "in": "query",
            "description": "true — уже вышедшие, false — предстоящие.",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "include_obscure",
            "in": "query",
            "description": "Включить тайтлы без постера и почти без голосов.",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Размер страницы.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Смещение от начала выборки.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CardList"
                }
              }
            }
          },
          "400": {
            "description": "Запрос не разобран: неизвестное значение перечисления, нечисловое число, несовместимые параметры.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Ключ отсутствует, неизвестен или отозван.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Превышен предел ключа: запросов в секунду или дневная квота.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/discover/hidden-gems": {
      "get": {
        "tags": [
          "Витрины"
        ],
        "summary": "Высокая оценка при малой известности",
        "description": "Три порога задают витрину и переопределяются своими значениями: votes_total в диапазоне 200–5000 и rating_imdb от 7.5. Конверт приходит без total. Дополнительно принимает всю фильтрацию каталога: `kind`, `genre`, `country`, `year.gte`/`year.lte`, `rating_imdb.gte`, `votes_imdb.gte` и остальные поля с операторами `.any` `.all` `.not` `.gte` `.lte`.",
        "operationId": "getHiddenGems",
        "parameters": [
          {
            "name": "votes_total.gte",
            "in": "query",
            "description": "Нижний порог известности.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 200
            }
          },
          {
            "name": "votes_total.lte",
            "in": "query",
            "description": "Верхний порог известности.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 5000
            }
          },
          {
            "name": "rating_imdb.gte",
            "in": "query",
            "description": "Порог оценки.",
            "required": false,
            "schema": {
              "type": "number",
              "default": 7.5
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "Ключ со знаком, как в каталоге.",
            "required": false,
            "schema": {
              "type": "string",
              "default": "-rating"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Размер страницы.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Смещение от начала выборки.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CardList"
                }
              }
            }
          },
          "400": {
            "description": "Запрос не разобран: неизвестное значение перечисления, нечисловое число, несовместимые параметры.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Ключ отсутствует, неизвестен или отозван.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Превышен предел ключа: запросов в секунду или дневная квота.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/discover/divergence": {
      "get": {
        "tags": [
          "Витрины"
        ],
        "summary": "Где Кинопоиск и IMDb расходятся",
        "description": "У карточки появляются divergence — величина разрыва — и пара votes_kp_count / votes_imdb_count. Фильтры карточки сужают уже набранную страницу.",
        "operationId": "getDivergence",
        "parameters": [
          {
            "name": "direction",
            "in": "query",
            "description": "ru — Кинопоиск выше, west — IMDb выше.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "ru",
                "west"
              ]
            },
            "example": "ru"
          },
          {
            "name": "min_delta",
            "in": "query",
            "description": "Минимальный разрыв оценок.",
            "required": false,
            "schema": {
              "type": "number",
              "default": 1.0
            }
          },
          {
            "name": "min_votes",
            "in": "query",
            "description": "Порог голосов с обеих сторон.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 10000
            }
          },
          {
            "name": "released",
            "in": "query",
            "description": "true — вышедшие, false — предстоящие.",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Размер страницы.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Смещение от начала выборки.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CardList"
                }
              }
            }
          },
          "400": {
            "description": "Запрос не разобран: неизвестное значение перечисления, нечисловое число, несовместимые параметры.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Ключ отсутствует, неизвестен или отозван.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Превышен предел ключа: запросов в секунду или дневная квота.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/enums": {
      "get": {
        "tags": [
          "Справочные данные"
        ],
        "summary": "Закрытые перечисления",
        "description": "Все наборы значений, которые встречаются в ответах, с подписями сразу на двух языках. Меняются только вместе с выкладкой, поэтому кэшируются на сутки.",
        "operationId": "getEnums",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Успешный ответ.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Enums"
                }
              }
            }
          },
          "401": {
            "description": "Ключ отсутствует, неизвестен или отозван.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/health": {
      "get": {
        "tags": [
          "Справочные данные"
        ],
        "summary": "Проверка доступности",
        "description": "Единственный путь без ключа и без префикса версии — адрес проверки не должен меняться.",
        "operationId": "getHealth",
        "security": [],
        "servers": [
          {
            "url": "https://api.kinodata.space"
          }
        ],
        "responses": {
          "200": {
            "description": "Сервис отвечает.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "example": "ok"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Ключ выдаёт владелец сервиса. Держите его на сервере, не в клиентском коде."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "Тело любого неуспешного ответа.",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Машинный код ошибки; ветвиться следует на него.",
                "enum": [
                  "invalid_request",
                  "unauthorized",
                  "forbidden",
                  "not_found",
                  "conflict",
                  "rate_limited",
                  "internal",
                  "source_unavailable"
                ]
              },
              "message": {
                "type": "string",
                "description": "Человекочитаемое объяснение."
              },
              "field": {
                "type": "string",
                "description": "Параметр, из-за которого отказ."
              },
              "allowed": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Допустимые значения, если набор закрыт."
              },
              "hint": {
                "type": "string",
                "description": "Подсказка вида «возможно, вы имели в виду…»."
              }
            }
          }
        }
      },
      "Ids": {
        "type": "object",
        "description": "Номера одного тайтла во всех каталогах.",
        "properties": {
          "imdb_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "kp_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "tmdb_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Номер TMDB для фильма; взаимоисключающ с tmdb_tv_id."
          },
          "tmdb_tv_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Номер TMDB для сериала; взаимоисключающ с tmdb_id."
          },
          "kinorium_id": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Country": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "ISO-3166 alpha-2, нижним регистром."
          },
          "name_ru": {
            "type": "string"
          },
          "name_en": {
            "type": "string"
          }
        }
      },
      "Card": {
        "type": "object",
        "description": "Короткая карточка тайтла — элемент всех списков.",
        "properties": {
          "imdb_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "kp_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "tmdb_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "tmdb_tv_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "kinorium_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "title_ru": {
            "type": [
              "string",
              "null"
            ]
          },
          "title_en": {
            "type": [
              "string",
              "null"
            ]
          },
          "title_original": {
            "type": [
              "string",
              "null"
            ]
          },
          "year": {
            "type": [
              "integer",
              "null"
            ]
          },
          "release_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "kind": {
            "type": "string",
            "description": "Вид тайтла, набор title_kinds."
          },
          "poster_kp": {
            "type": [
              "string",
              "null"
            ]
          },
          "poster_tmdb": {
            "type": [
              "string",
              "null"
            ]
          },
          "poster_imdb": {
            "type": [
              "string",
              "null"
            ]
          },
          "rating_kp": {
            "type": [
              "number",
              "null"
            ]
          },
          "rating_tmdb": {
            "type": [
              "number",
              "null"
            ]
          },
          "rating_imdb": {
            "type": [
              "number",
              "null"
            ]
          },
          "rating_kr": {
            "type": [
              "number",
              "null"
            ]
          },
          "rating_letterboxd": {
            "type": [
              "number",
              "null"
            ]
          },
          "votes_kp": {
            "type": [
              "integer",
              "null"
            ]
          },
          "votes_tmdb": {
            "type": [
              "integer",
              "null"
            ]
          },
          "votes_imdb": {
            "type": [
              "integer",
              "null"
            ]
          },
          "countries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Country"
            }
          },
          "genres": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Слаги жанров."
          },
          "title": {
            "type": [
              "string",
              "null"
            ],
            "description": "Название, разрешённое по ?lang=. Приходит в списках движка выборки."
          },
          "poster_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Постер, выбранный из источников. Приходит в списках движка выборки."
          }
        }
      },
      "ListEnvelope": {
        "type": "object",
        "description": "Общая оболочка списка. Ресурсные поля едут рядом с этими.",
        "properties": {
          "items": {
            "type": "array",
            "items": {}
          },
          "total": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Размер выборки до пагинации; null, когда её задал запрос."
          },
          "limit": {
            "type": "integer",
            "description": "Реальный размер страницы после клампа."
          },
          "offset": {
            "type": "integer"
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Только в ответе с курсором, вместо offset."
          }
        }
      },
      "CardList": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ListEnvelope"
          },
          {
            "type": "object",
            "properties": {
              "items": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Card"
                }
              }
            }
          }
        ]
      },
      "Section": {
        "type": "object",
        "description": "Раздел сгруппированного ответа; устроен как список.",
        "properties": {
          "label": {
            "type": [
              "string",
              "null"
            ],
            "description": "Подпись значения на языке ?lang=."
          },
          "total": {
            "type": "integer",
            "description": "Настоящий размер раздела, даже если элементы обрезаны."
          },
          "has_more": {
            "type": "boolean"
          },
          "items": {
            "type": "array",
            "items": {}
          }
        }
      },
      "TitleCard": {
        "type": "object",
        "description": "Полная карточка тайтла: двадцать два блока верхнего уровня, они же имена для ?fields=. Блок ids приходит всегда.",
        "properties": {
          "ids": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Ids"
              }
            ],
            "description": "Номера каталогов и ссылки на страницы источников."
          },
          "meta": {
            "type": "object",
            "description": "hydrated, filled_at, lang ответа."
          },
          "title": {
            "type": "object",
            "description": "Названия по источникам и производное title."
          },
          "type": {
            "type": "object",
            "description": "kind, is_series, status и их подписи."
          },
          "synopsis": {
            "type": "object",
            "description": "Описания и слоганы по источникам, производные overview и tagline."
          },
          "release": {
            "type": "object",
            "description": "year, runtime и даты выхода по регионам."
          },
          "ratings": {
            "type": "object",
            "description": "Оценки по источникам, votes_total, распределение, популярность."
          },
          "finance": {
            "type": "object",
            "description": "budget, revenue, box_office."
          },
          "awards": {
            "type": "object",
            "description": "Сводка наград и список церемоний."
          },
          "countries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Country"
            }
          },
          "classification": {
            "type": "object",
            "description": "age_limit, mpaa, original_language, is_adult."
          },
          "series": {
            "type": "object",
            "description": "Сезоны, серии, годы выхода сериала."
          },
          "media": {
            "type": "object",
            "description": "Постеры, задник, трейлеры, саундтрек."
          },
          "genres": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "companies": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "akas": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Альтернативные названия по регионам."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "watch": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Где смотреть."
          },
          "parental": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Возрастные предупреждения по категориям."
          },
          "credits_top": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Верх состава."
          },
          "franchise": {
            "type": "object",
            "description": "Есть только у тайтлов, входящих во франшизу."
          },
          "counts": {
            "type": "object",
            "description": "Сколько чего лежит на подресурсах."
          }
        }
      },
      "CreditPerson": {
        "type": "object",
        "properties": {
          "imdb_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "kp_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "tmdb_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "kinorium_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "name_ru": {
            "type": [
              "string",
              "null"
            ]
          },
          "name_en": {
            "type": [
              "string",
              "null"
            ]
          },
          "name_orig": {
            "type": [
              "string",
              "null"
            ]
          },
          "photo_url_kp": {
            "type": [
              "string",
              "null"
            ]
          },
          "photo_url_tmdb": {
            "type": [
              "string",
              "null"
            ]
          },
          "photo_url_kinorium": {
            "type": [
              "string",
              "null"
            ]
          },
          "gender": {
            "type": [
              "string",
              "null"
            ]
          },
          "birth_year": {
            "type": [
              "integer",
              "null"
            ]
          },
          "professions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Credit": {
        "type": "object",
        "description": "Один кредит: человек и то, что он делал.",
        "properties": {
          "person": {
            "$ref": "#/components/schemas/CreditPerson"
          },
          "role": {
            "type": "string",
            "description": "Роль, набор credit_roles."
          },
          "character": {
            "type": [
              "string",
              "null"
            ]
          },
          "job": {
            "type": [
              "string",
              "null"
            ],
            "description": "Должность внутри роли: Screenplay, Producer."
          },
          "dubs": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "object"
            }
          },
          "ord": {
            "type": "integer",
            "description": "Место в титрах."
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Каталоги, подтвердившие кредит."
          }
        }
      },
      "Person": {
        "type": "object",
        "description": "Человек, сведённый из каталогов в одну запись.",
        "properties": {
          "imdb_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "kp_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "tmdb_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "kinorium_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "name_ru": {
            "type": [
              "string",
              "null"
            ]
          },
          "name_en": {
            "type": [
              "string",
              "null"
            ]
          },
          "name_orig": {
            "type": [
              "string",
              "null"
            ]
          },
          "birth_year": {
            "type": [
              "integer",
              "null"
            ]
          },
          "death_year": {
            "type": [
              "integer",
              "null"
            ]
          },
          "gender": {
            "type": [
              "string",
              "null"
            ]
          },
          "birthplace": {
            "type": [
              "string",
              "null"
            ]
          },
          "photo_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "professions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "biography": {
            "type": [
              "string",
              "null"
            ]
          },
          "popularity": {
            "type": [
              "number",
              "null"
            ]
          },
          "also_known_as": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "homepage": {
            "type": [
              "string",
              "null"
            ]
          },
          "instagram": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "MediaItem": {
        "type": "object",
        "description": "Изображение или видео — одна форма на оба вида.",
        "properties": {
          "class": {
            "type": "string",
            "enum": [
              "image",
              "video"
            ]
          },
          "type": {
            "type": "string",
            "description": "Вид: наборы image_types и video_kinds."
          },
          "url": {
            "type": "string"
          },
          "language": {
            "type": [
              "string",
              "null"
            ]
          },
          "width": {
            "type": [
              "integer",
              "null"
            ]
          },
          "height": {
            "type": [
              "integer",
              "null"
            ]
          },
          "youtube_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "preview_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "runtime_sec": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "TextItem": {
        "type": "object",
        "description": "Факт, вопрос-ответ или цитата. Набор полей зависит от kind.",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "fact",
              "faq",
              "quote"
            ]
          },
          "text": {
            "type": [
              "string",
              "null"
            ]
          },
          "original": {
            "type": [
              "string",
              "null"
            ],
            "description": "Оригинал цитаты."
          },
          "author": {
            "type": [
              "string",
              "null"
            ]
          },
          "author_role": {
            "type": [
              "string",
              "null"
            ]
          },
          "fact_kind": {
            "type": [
              "string",
              "null"
            ],
            "description": "fact, trivia или blooper."
          },
          "is_spoiler": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "question": {
            "type": [
              "string",
              "null"
            ]
          },
          "answer": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Episode": {
        "type": "object",
        "properties": {
          "season": {
            "type": "integer"
          },
          "episode": {
            "type": "integer"
          },
          "name_ru": {
            "type": [
              "string",
              "null"
            ]
          },
          "name_en": {
            "type": [
              "string",
              "null"
            ]
          },
          "air_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "rating_imdb": {
            "type": [
              "number",
              "null"
            ]
          },
          "votes_imdb": {
            "type": [
              "integer",
              "null"
            ]
          },
          "rating_kr": {
            "type": [
              "number",
              "null"
            ]
          },
          "special": {
            "type": "boolean"
          }
        }
      },
      "Season": {
        "type": "object",
        "description": "Строка сводки при ?group=season.",
        "properties": {
          "season": {
            "type": "integer"
          },
          "episodes": {
            "type": "integer"
          },
          "first_air": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "last_air": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "rating_imdb": {
            "type": [
              "number",
              "null"
            ]
          },
          "votes_imdb": {
            "type": [
              "integer",
              "null"
            ]
          },
          "specials": {
            "type": "boolean"
          }
        }
      },
      "Collection": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "description": "Идентификатор подборки в пути."
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "poster_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "language": {
            "type": "string"
          },
          "items_count": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "EnumValue": {
        "type": "object",
        "properties": {
          "value": {
            "type": "string"
          },
          "label_ru": {
            "type": "string"
          },
          "label_en": {
            "type": "string"
          }
        }
      },
      "Enums": {
        "type": "object",
        "description": "Восемнадцать закрытых наборов: имя набора → его значения с подписями.",
        "additionalProperties": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/EnumValue"
          }
        }
      }
    }
  }
}
