Те же данные, что на сайте: события, сеансы, справочники и подбор по фразе. Ключ не нужен.
Демо-данныеСобытия условные: они показывают, как работает афиша.Три шага, и на странице министерства появляется живая афиша. Действующие системы сайта не меняются: виджет только читает открытый API афиши.
Добавьте её в то место страницы, где должна стоять афиша. Подойдёт любой редактор сайта, который разрешает вставку HTML.
<script src="https://89-125-129-170.nip.io/widget.js" data-city="ufa" data-limit="5" data-theme="light" data-lang="ru" async></script>На сайте запрещены сторонние скрипты? Возьмите вариант с iframe: он показывает тот же список.
<iframe src="https://89-125-129-170.nip.io/embed?city=ufa&limit=5&theme=light&lang=ru" title="Афиша событий" loading="lazy" width="100%" height="720" style="border:0"></iframe>Город, категорию, число событий, тему и язык задают атрибуты тега. Собрать строку без ручной правки можно в конфигураторе.
| Атрибут | Описание |
|---|---|
data-city | Код города: ufa, sterlitamak, salavat и другие. Несколько городов через запятую. Без атрибута показывается вся республика. |
data-category | Код категории п. 8.1: concert, performance, exhibition, festival, cinema, excursion, other. |
data-period | Период: today, tomorrow, weekend или week. Без атрибута показываются все ближайшие события. |
data-height | Высота в пикселях, от 240 до 1600. Длинный список прокручивается внутри виджета, страница не прыгает. |
data-limit |
Откройте страницу. В блоке должны появиться ближайшие события с датой, площадкой, ценой и статусом. Если блок пуст, проверьте адрес скрипта и откройте GET /api/v1/health: ответ "status": "ok" значит, что API доступен.
https://89-125-129-170.nip.io/api/v1Адрес подставлен тот, по которому открыта эта страница. Все примеры ниже рабочие: их можно копировать как есть.
snake_case.Asia/Yekaterinburg (UTC+5). «Сегодня», «завтра» и «выходные» считаются в этом поясе. В ответе списка есть reference_date: дата, от которой сервер считал периоды.price_min, price_max и is_free. 0 значит бесплатно, null значит «цена не указана». В параметрах запроса цена тоже в рублях.{ "ru": "Уфа", "ba": "Өфө" }. Названия событий приходят на языке источника. Башкирское название лежит в title_ba, если оно есть.Cache-Control: no-store: даты и признак is_past зависят от текущего времени. Справочники можно держать в кэше 5 минут.price_max, событие без возрастной метки не проходит фильтр age. Такие события не теряются молча: их число лежит в hidden_unknown_price и .| Адрес | Что отдаёт | Выполнить запрос |
|---|---|---|
GET/api/v1/events | Список событий с фильтрами | Попробовать (откроется в новой вкладке) |
GET/api/v1/events/{id} | Карточка события со всеми сеансами | Попробовать (откроется в новой вкладке) |
GET/api/v1/facets | Число событий по городам и категориям | Попробовать (откроется в новой вкладке) |
| GET |
GET/api/v1/eventsПопробовать (откроется в новой вкладке)
Список событий с фильтрами, сортировкой, счётчиками и вариантами ослабить условия. Все параметры необязательные. Списки передаются через запятую: city=ufa,salavat.
| Параметр | Тип | Описание |
|---|---|---|
city | список | Города: ufa, sterlitamak, salavat, neftekamsk, oktyabrsky, beloretsk, sibay, kumertau. |
category | список | Категории п. 8.1: concert, performance, exhibition, festival, cinema, excursion, other. |
date | строка | Готовый период: today, tomorrow, weekend, week. Или одна дата ГГГГ-ММ-ДД. Нельзя сочетать с date_from и date_to. |
GET/api/v1/events/{id}Попробовать (откроется в новой вкладке)
Карточка события со всеми сеансами, включая прошедшие, отменённые и перенесённые. В списке таких сеансов нет: там только те, что прошли условия.
curl "https://89-125-129-170.nip.io/api/v1/events/fc2d50a3-de94-5e4a-a427-33c6aa08e2d4"{
"id": "fc2d50a3-de94-5e4a-a427-33c6aa08e2d4",
"title": "Выставка графики",
"normalized_title": "выставка графики",
"category": "exhibition",
"tags": [],
"date_start": "2026-11-13T00:00:00+05:00",
"date_end": "2026-12-02T00:00:00+05:00",
"duration_min": null,
"city": "Уфа",
"venue": "Башкирский государственный художественный музей им. М. В. Нестерова",
"address": null,
"lat": null,
"lon": null,
"description": "Рисунок, акварель и печатная графика. Спокойная выставка, на которую хватит часа.",
"price_min": 300,
"price_max": 300,
"is_free": false,
"age_limit": 0,
"language": null,
"org_link": "http://89.125.129.170/about#demo",
"source_url": "http://89.125.129.170/about#demo",
"source_type": "manual",
"source_hash": "4310b5ea74ac0ab51ce6e87a5e59fe47b1aa415c9042bd806da38e9267c36aab",
"verified_at": "2026-11-14T07:54:57+05:00",
"confidence": 0.95,
"status": "published",
"dedup_key": "выставка графики|2026-11-13|ven-ufa-nesterov",
"created_at": "2026-11-07T07:54:57+05:00",
"updated_at": "2026-11-14T07:54:57+05:00",
"city_id": "ufa",
"venue_id": "ven-ufa-nesterov",
"time_precision": "date",
"kind": "exhibition",
"popularity": 70,
"organizer": null,
"image": "/images/events/exhibition-1.webp",
"synthetic": true,
"sources": [
{
"source_url": "http://89.125.129.170/about#demo",
"source_type": "manual",
"source_hash": "4310b5ea74ac0ab51ce6e87a5e59fe47b1aa415c9042bd806da38e9267c36aab",
"verified_at": "2026-11-14T07:54:57+05:00",
"title": "Демо-источник: афиша площадки"
},
{
"source_url": "http://89.125.129.170/about#demo",
"source_type": "page",
"source_hash": "b7f273e50325369d1da551fd3a1b46adad813a53413ed7f95ca9afc256c90ce1",
"verified_at": "2026-11-14T07:54:57+05:00",
"title": "Демо-источник: страница организатора"
}
],
"occurrences": [
{
"id": "occ_demo_e1_1",
"date_start": "2026-11-13T00:00:00+05:00",
"date_end": "2026-12-02T00:00:00+05:00",
"time_precision": "date",
"status": "scheduled",
"status_verified": true,
"availability": "available",
"is_past": false
}
],
"offers": [
{
"price": 300,
"is_free": false,
"audience": "adult"
},
{
"price": 150,
"is_free": false,
"audience": "reduced"
}
]
}GET/api/v1/facetsПопробовать (откроется в новой вкладке)
Число событий по городам, категориям, возрастным меткам и виду цены. Принимает те же параметры, что список событий. Без параметров считает по всему каталогу.
curl "https://89-125-129-170.nip.io/api/v1/facets?date=weekend&audience=kids"{
"cities": [
{
"id": "ufa",
"name": {
"ru": "Уфа",
"ba": "Өфө"
},
"count": 2
},
{
"id": "sterlitamak",
"name": {
"ru": "Стерлитамак",
"ba": "Стәрлетамаҡ"
},
"count": 1
},
{
"id": "salavat",
"name": {
"ru": "Салават",
"ba": "Салауат"
},
"count": 0
},
{
"id": "neftekamsk",
"name": {
"ru": "Нефтекамск",
"ba": "Нефтекама"
},
"count": 0
},
{
"id": "oktyabrsky",
"name": {
"ru": "Октябрьский",
"ba": "Октябрьский"
},
"count": 1
},
{
"id": "beloretsk",
"name": {
"ru": "Белорецк",
"ba": "Белорет"
},
"count": 2
},
{
"id": "sibay",
"name": {
"ru": "Сибай",
"ba": "Сибай"
},
"count": 0
},
{
"id": "kumertau",
"name": {
"ru": "Кумертау",
"ba": "Күмертау"
},
"count": 0
}
],
"categories": [
{
"id": "concert",
"name": {
"ru": "Концерты",
"ba": "Концерттар"
},
"count": 0
},
{
"id": "performance",
"name": {
"ru": "Спектакли",
"ba": "Спектаклдәр"
},
"count": 0
},
{
"id": "exhibition",
"name": {
"ru": "Выставки",
"ba": "Күргәҙмәләр"
},
"count": 6
},
{
"id": "festival",
"name": {
"ru": "Фестивали",
"ba": "Фестивалдәр"
},
"count": 0
},
{
"id": "cinema",
"name": {
"ru": "Кино",
"ba": "Кино"
},
"count": 0
},
{
"id": "excursion",
"name": {
"ru": "Экскурсии",
"ba": "Экскурсиялар"
},
"count": 0
},
{
"id": "other",
"name": {
"ru": "Другое",
"ba": "Башҡа"
},
"count": 0
}
],
"age_ratings": [
{
"id": "0+",
"name": {
"ru": "0+",
"ba": "0+"
},
"count": 5
},
{
"id": "6+",
"name": {
"ru": "6+",
"ba": "6+"
},
"count": 1
},
{
"id": "12+",
"name": {
"ru": "12+",
"ba": "12+"
},
"count": 0
},
{
"id": "16+",
"name": {
"ru": "16+",
"ba": "16+"
},
"count": 0
},
{
"id": "18+",
"name": {
"ru": "18+",
"ba": "18+"
},
"count": 0
},
{
"id": "unknown",
"name": {
"ru": "Возраст не указан",
"ba": "Йәше күрһәтелмәгән"
},
"count": 0
}
],
"price": {
"free": 2,
"paid": 4,
"unknown": 0
},
"total": 6
}GET/api/v1/dictionariesПопробовать (откроется в новой вкладке)
Справочники с подписями на русском и башкирском: категории, города, возрастные метки, статусы, виды цены, сортировки, готовые периоды. Поле tone подсказывает цвет плашки: positive, warning, negative, neutral.
curl "https://89-125-129-170.nip.io/api/v1/dictionaries"{
"timezone": "Asia/Yekaterinburg",
"currency": "RUB",
"categories": [
{
"id": "concert",
"name": {
"ru": "Концерты",
"ba": "Концерттар"
},
"name_one": {
"ru": "Концерт",
"ba": "Концерт"
}
},
{
"id": "performance",
"name": {
"ru": "Спектакли",
"ba": "Спектаклдәр"
},
"name_one": {
"ru": "Спектакль",
"ba": "Спектакль"
}
},
{
"id": "exhibition",
"name": {
"ru": "Выставки",
"ba": "Күргәҙмәләр"
},
"name_one": {
"ru": "Выставка",
"ba": "Күргәҙмә"
}
},
{
"id": "festival",
"name": {
"ru": "Фестивали",
"ba": "Фестивалдәр"
},
"name_one": {
"ru": "Фестиваль",
"ba": "Фестиваль"
}
},
{
"id": "cinema",
"name": {
"ru": "Кино",
"ba": "Кино"
},
"name_one": {
"ru": "Кинопоказ",
"ba": "Кино"
}
},
{
"id": "excursion",
"name": {
"ru": "Экскурсии",
"ba": "Экскурсиялар"
},
"name_one": {
"ru": "Экскурсия",
"ba": "Экскурсия"
}
},
{
"id": "other",
"name": {
"ru": "Другое",
"ba": "Башҡа"
},
"name_one": {
"ru": "Другое",
"ba": "Башҡа"
}
}
],
"cities": [
{
"slug": "ufa",
"name": {
"ru": "Уфа",
"ba": "Өфө"
},
"name_in": {
"ru": "в Уфе",
"ba": "Өфөлә"
},
"lat": 54.74,
"lon": 55.97
},
{
"slug": "sterlitamak",
"name": {
"ru": "Стерлитамак",
"ba": "Стәрлетамаҡ"
},
"name_in": {
"ru": "в Стерлитамаке",
"ba": "Стәрлетамаҡта"
},
"lat": 53.63,
"lon": 55.95
},
{
"slug": "salavat",
"name": {
"ru": "Салават",
"ba": "Салауат"
},
"name_in": {
"ru": "в Салавате",
"ba": "Салауатта"
},
"lat": 53.36,
"lon": 55.93
},
{
"slug": "neftekamsk",
"name": {
"ru": "Нефтекамск",
"ba": "Нефтекама"
},
"name_in": {
"ru": "в Нефтекамске",
"ba": "Нефтекамала"
},
"lat": 56.09,
"lon": 54.25
},
{
"slug": "oktyabrsky",
"name": {
"ru": "Октябрьский",
"ba": "Октябрьский"
},
"name_in": {
"ru": "в Октябрьском",
"ba": "Октябрьскийҙа"
},
"lat": 54.48,
"lon": 53.47
},
{
"slug": "beloretsk",
"name": {
"ru": "Белорецк",
"ba": "Белорет"
},
"name_in": {
"ru": "в Белорецке",
"ba": "Белоретта"
},
"lat": 53.96,
"lon": 58.4
},
{
"slug": "sibay",
"name": {
"ru": "Сибай",
"ba": "Сибай"
},
"name_in": {
"ru": "в Сибае",
"ba": "Сибайҙа"
},
"lat": 52.72,
"lon": 58.67
},
{
"slug": "kumertau",
"name": {
"ru": "Кумертау",
"ba": "Күмертау"
},
"name_in": {
"ru": "в Кумертау",
"ba": "Күмертауҙа"
},
"lat": 52.77,
"lon": 55.78
}
],
"age_ratings": [
{
"id": "0+",
"min_age": 0,
"label": {
"ru": "0+",
"ba": "0+"
}
},
{
"id": "6+",
"min_age": 6,
"label": {
"ru": "6+",
"ba": "6+"
}
},
{
"id": "12+",
"min_age": 12,
"label": {
"ru": "12+",
"ba": "12+"
}
},
{
"id": "16+",
"min_age": 16,
"label": {
"ru": "16+",
"ba": "16+"
}
},
{
"id": "18+",
"min_age": 18,
"label": {
"ru": "18+",
"ba": "18+"
}
}
],
"statuses": [
{
"id": "scheduled",
"label": {
"ru": "Состоится",
"ba": "Буласаҡ"
},
"tone": "positive"
},
{
"id": "cancelled",
"label": {
"ru": "Отменён",
"ba": "Юҡҡа сығарылған"
},
"tone": "negative"
},
{
"id": "postponed",
"label": {
"ru": "Перенесён, дата уточняется",
"ba": "Кисектерелгән, көнө асыҡлана"
},
"tone": "warning"
},
{
"id": "rescheduled",
"label": {
"ru": "Перенесён на новую дату",
"ba": "Яңы көнгә күсерелгән"
},
"tone": "warning"
},
{
"id": "unknown",
"label": {
"ru": "Статус не подтверждён",
"ba": "Статусы раҫланмаған"
},
"tone": "neutral"
}
],
"display_statuses": [
{
"id": "scheduled",
"label": {
"ru": "Состоится",
"ba": "Буласаҡ"
},
"tone": "positive"
},
{
"id": "cancelled",
"label": {
"ru": "Отменён",
"ba": "Юҡҡа сығарылған"
},
"tone": "negative"
},
{
"id": "postponed",
"label": {
"ru": "Перенесён, дата уточняется",
"ba": "Кисектерелгән, көнө асыҡлана"
},
"tone": "warning"
},
{
"id": "rescheduled",
"label": {
"ru": "Перенесён",
"ba": "Күсерелгән"
},
"tone": "warning"
},
{
"id": "sold_out",
"label": {
"ru": "Билеты кончились",
"ba": "Билеттар бөткән"
},
"tone": "neutral"
},
{
"id": "unverified",
"label": {
"ru": "Статус не подтверждён",
"ba": "Статусы раҫланмаған"
},
"tone": "neutral"
}
],
"availability": [
{
"id": "available",
"label": {
"ru": "Билеты есть",
"ba": "Билеттар бар"
},
"tone": "positive"
},
{
"id": "sold_out",
"label": {
"ru": "Билеты кончились",
"ba": "Билеттар бөткән"
},
"tone": "neutral"
},
{
"id": "unknown",
"label": {
"ru": "Наличие билетов не подтверждёнo",
"ba": "Билеттар барлығы раҫланмаған"
},
"tone": "neutral"
}
],
"price_kinds": [
{
"id": "free",
"label": {
"ru": "Бесплатно",
"ba": "Бушлай"
}
},
{
"id": "from",
"label": {
"ru": "Платно",
"ba": "Түләүле"
}
},
{
"id": "unknown",
"label": {
"ru": "Цена не указана",
"ba": "Хаҡы күрһәтелмәгән"
}
}
],
"offer_audiences": [
{
"id": "adult",
"label": {
"ru": "Взрослый",
"ba": "Өлкәндәр өсөн"
}
},
{
"id": "child",
"label": {
"ru": "Детский",
"ba": "Балалар өсөн"
}
},
{
"id": "reduced",
"label": {
"ru": "Льготный",
"ba": "Ташламалы"
}
}
],
"sorts": [
{
"id": "date",
"label": {
"ru": "Сначала ближайшие",
"ba": "Башта яҡындағылары"
}
},
{
"id": "price",
"label": {
"ru": "Сначала дешевле",
"ba": "Башта арзаныраҡтары"
}
},
{
"id": "popularity",
"label": {
"ru": "Сначала популярные",
"ba": "Башта популярҙары"
}
}
],
"date_presets": [
{
"id": "today",
"label": {
"ru": "Сегодня",
"ba": "Бөгөн"
}
},
{
"id": "tomorrow",
"label": {
"ru": "Завтра",
"ba": "Иртәгә"
}
},
{
"id": "weekend",
"label": {
"ru": "На выходных",
"ba": "Ял көндәрендә"
}
},
{
"id": "week",
"label": {
"ru": "На этой неделе",
"ba": "Ошо аҙнала"
}
}
]
}POST/api/v1/assistant/query
Подбор событий по свободной фразе. Ассистент превращает фразу в условия, условия проверяет код по полям событий. Событий, которых нет в каталоге, ассистент не называет.
В прототипе фразу разбирают правила без языковой модели: в ответе стоит "engine": "rules-mock-v1". Форма запроса и ответа та же, что будет у ассистента на языковой модели. Запросы понимаются на русском языке.
Сервер не хранит состояние диалога. Клиент присылает прежние условия в поле previous и получает в ответе новое полное состояние в поле constraints.
| Поле | Тип | Описание |
|---|---|---|
message | строка | Фраза пользователя, до 500 знаков. Нужна фраза или patch. |
previous | объект | Условия { hard, soft } из прошлого ответа: с ними диалог продолжается. |
patch | объект | Правка условий без текста: { reset, set: { hard, soft }, unset: ["hard.date"] }. Так работают чипы и варианты из relaxations. |
answering | строка | На какой вопрос отвечает пользователь: значение clarify.code из прошлого ответа. Помогает понять короткий ответ вроде «7». |
limit | целое | Сколько событий вернуть, от 1 до 12. По умолчанию 6. |
GET/api/v1/healthПопробовать (откроется в новой вкладке)
GET /api/v1/health проверяет, что API отвечает, и сообщает, откуда берутся данные.
curl "https://89-125-129-170.nip.io/api/v1/health"{
"status": "ok",
"checks": {
"database": "ok",
"model": "ok",
"assistant": "ok"
},
"version": "1.0.0",
"data": "postgres",
"events": 41,
"time": "2026-11-15T22:42:32+05:00",
"timezone": "Asia/Yekaterinburg"
}GET/api/v1Попробовать (откроется в новой вкладке)
Ошибка приходит с кодом HTTP и телом одного вида. Сообщение написано для человека: его можно показать как есть. В details перечислены все поля, которые не прошли проверку.
curl "https://89-125-129-170.nip.io/api/v1/events?city=moscow"{
"code": "validation_error",
"message": "Проверьте параметры поиска событий",
"details": [
{
"field": "city",
"message": "Неизвестный город «moscow». Допустимые: ufa, sterlitamak, salavat, neftekamsk, oktyabrsky, beloretsk, sibay, kumertau"
}
],
"request_id": "req_20e89f73f353414db607"
}| error.code | Код HTTP | Когда |
|---|---|---|
validation_error | 400 | Параметр не прошёл проверку. |
Каждый ответ, включая ошибки, приходит с этими заголовками. API можно вызывать из браузера с любого сайта: он только читает данные и не использует cookie.
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Accept, Authorization, If-Match, If-None-Match, X-Request-Id, X-Assistant-Demo
Access-Control-Max-Age: 86400На предварительный запрос OPTIONS любой адрес под /api/v1 отвечает кодом 204 с теми же заголовками.
Статус относится к сеансу, а не к событию целиком: отмена одного вечера не отменяет остальные. Плашка на сайте и в виджете сводит статус и наличие билетов в одно значение.
Правила проверяются сверху вниз. Отмена и перенос важнее наличия билетов, неподтверждённый статус не выдаётся за «состоится».
| Сеанс | Плашка | Что значит |
|---|---|---|
status = cancelled | Отменён | Сеанс отменён. Карточка остаётся в каталоге с этим статусом и не исчезает молча. |
status = postponed | Перенесён, дата уточняется | Сеанс перенесён, новой даты пока нет. |
status = unknownstatus_is_verified = false | Статус не подтверждён | Источник не подтвердил статус. Афиша не выдаёт такой сеанс за «состоится». |
availability = sold_out | Билеты кончились | Сеанс состоится, но билетов по данным источника нет. |
status = rescheduled | Перенесён | Сеанс перенесён, новая дата известна. Прежняя дата хранится рядом. |
| остальное | Состоится | Сеанс состоится: источник это подтверждает. |
Цена события лежит в price_min, price_max и is_free. Она считается по общим и взрослым билетам: детские и льготные тарифы на неё не влияют, они есть в offers.
Сейчас API отдаёт демонстрационные события с пометкой synthetic: true. Формат ответа тот же, что будет у боевого API.
| Сколько событий показать: от 1 до 10. По умолчанию 5. |
data-theme | light для светлых страниц (по умолчанию) или dark для тёмных. |
data-lang | ru или ba. Без атрибута виджет берёт язык страницы из <html lang>. |
data-target | CSS-селектор элемента, куда поставить виджет. Без атрибута он встаёт сразу после тега script. |
hidden_unknown_ageprice_min: null значит «цена не указана», а не «вход свободный».relaxations, и у каждого стоит число событий, посчитанное настоящим поиском.| Справочники |
| Попробовать (откроется в новой вкладке) |
POST/api/v1/assistant/query | Подбор по свободной фразе | Запрос с телом: пример ниже |
GET/api/v1/health | Проверка работы API | Попробовать (откроется в новой вкладке) |
GET/api/v1 | Оглавление API: список методов и источник данных | Попробовать (откроется в новой вкладке) |
date_fromdate_to| дата |
Границы периода ГГГГ-ММ-ДД, включаются обе. |
time_fromtime_to | время | Начало сеанса не раньше и не позже, ЧЧ:ММ. Выставка без времени начала такой фильтр не проходит. |
price_max | целое | Цена за человека не больше, в рублях. Событие с неизвестной ценой не проходит и считается в hidden_unknown_price. |
free | логическое | Только события с явной пометкой «бесплатно»: free=true. Сайт понимает и прежнюю запись free_only=1. |
age | целое | Возраст ребёнка. Проходят события с возрастной меткой не выше. Событие без метки не проходит и считается в hidden_unknown_age. |
age_rating | список | Точное совпадение метки: 0+, 6+, 12+, 16+, 18+. Можно писать без плюса: age_rating=6. |
audience | строка | kids: раздел «Детям» и события с метками 0+ и 6+. |
status | список | Статусы сеансов: scheduled, cancelled, postponed, rescheduled, unknown или all. По умолчанию scheduled,rescheduled. |
q | строка | Поиск по названию, описанию, площадке, городу и категории. Регистр и разница между «ё» и «е» не учитываются. До 100 знаков. |
sort | строка | date (по умолчанию, сначала ближайшие), price (сначала дешевле, без цены в конце), popularity. |
page | целое | Номер страницы с 1. |
page_size | целое | Размер страницы от 1 до 50, по умолчанию 12. |
weekend: в будни это ближайшие суббота и воскресенье, в субботу сегодня и завтра, в воскресенье только сегодня.week: с сегодня по ближайшее воскресенье включительно.curl "https://89-125-129-170.nip.io/api/v1/events?city=ufa&date=weekend&price_max=1000&age=7&page_size=1"{
"items": [
{
"id": "fc2d50a3-de94-5e4a-a427-33c6aa08e2d4",
"title": "Выставка графики",
"normalized_title": "выставка графики",
"category": "exhibition",
"tags": [],
"date_start": "2026-11-13T00:00:00+05:00",
"date_end": "2026-12-02T00:00:00+05:00",
"duration_min": null,
"city": "Уфа",
"venue": "Башкирский государственный художественный музей им. М. В. Нестерова",
"address": null,
"lat": null,
"lon": null,
"description": "Рисунок, акварель и печатная графика. Спокойная выставка, на которую хватит часа.",
"price_min": 300,
"price_max": 300,
"is_free": false,
"age_limit": 0,
"language": null,
"org_link": "http://89.125.129.170/about#demo",
"source_url": "http://89.125.129.170/about#demo",
"source_type": "manual",
"source_hash": "4310b5ea74ac0ab51ce6e87a5e59fe47b1aa415c9042bd806da38e9267c36aab",
"verified_at": "2026-11-14T07:54:57+05:00",
"confidence": 0.95,
"status": "published",
"dedup_key": "выставка графики|2026-11-13|ven-ufa-nesterov",
"created_at": "2026-11-07T07:54:57+05:00",
"updated_at": "2026-11-14T07:54:57+05:00",
"city_id": "ufa",
"venue_id": "ven-ufa-nesterov",
"time_precision": "date",
"kind": "exhibition",
"popularity": 70,
"organizer": null,
"image": "/images/events/exhibition-1.webp",
"synthetic": true,
"sources": [
{
"source_url": "http://89.125.129.170/about#demo",
"source_type": "manual",
"source_hash": "4310b5ea74ac0ab51ce6e87a5e59fe47b1aa415c9042bd806da38e9267c36aab",
"verified_at": "2026-11-14T07:54:57+05:00",
"title": "Демо-источник: афиша площадки"
},
{
"source_url": "http://89.125.129.170/about#demo",
"source_type": "page",
"source_hash": "b7f273e50325369d1da551fd3a1b46adad813a53413ed7f95ca9afc256c90ce1",
"verified_at": "2026-11-14T07:54:57+05:00",
"title": "Демо-источник: страница организатора"
}
],
"occurrences": [
{
"id": "occ_demo_e1_1",
"date_start": "2026-11-13T00:00:00+05:00",
"date_end": "2026-12-02T00:00:00+05:00",
"time_precision": "date",
"status": "scheduled",
"status_verified": true,
"availability": "available",
"is_past": false
}
],
"offers": [
{
"price": 300,
"is_free": false,
"audience": "adult"
},
{
"price": 150,
"is_free": false,
"audience": "reduced"
}
],
"matched_occurrence_ids": [
"occ_demo_e1_1"
],
"primary_occurrence_id": "occ_demo_e1_1"
}
],
"page": 1,
"page_size": 1,
"total": 2,
"total_pages": 2,
"facets": {
"cities": [
{
"id": "ufa",
"name": "Уфа",
"count": 2
},
{
"id": "sterlitamak",
"name": "Стерлитамак",
"count": 1
},
{
"id": "salavat",
"name": "Салават",
"count": 0
},
{
"id": "neftekamsk",
"name": "Нефтекамск",
"count": 0
},
{
"id": "oktyabrsky",
"name": "Октябрьский",
"count": 1
},
{
"id": "beloretsk",
"name": "Белорецк",
"count": 2
},
{
"id": "sibay",
"name": "Сибай",
"count": 0
},
{
"id": "kumertau",
"name": "Кумертау",
"count": 0
}
],
"categories": [
{
"id": "concert",
"name": "Концерты",
"count": 0
},
{
"id": "performance",
"name": "Спектакли",
"count": 0
},
{
"id": "exhibition",
"name": "Выставки",
"count": 2
},
{
"id": "festival",
"name": "Фестивали",
"count": 0
},
{
"id": "cinema",
"name": "Кино",
"count": 0
},
{
"id": "excursion",
"name": "Экскурсии",
"count": 0
},
{
"id": "other",
"name": "Другое",
"count": 0
}
],
"age_limits": [
{
"value": 0,
"count": 2
},
{
"value": 6,
"count": 0
},
{
"value": 12,
"count": 0
},
{
"value": 16,
"count": 0
},
{
"value": 18,
"count": 0
},
{
"value": null,
"count": 0
}
],
"price": {
"free": 1,
"paid": 1,
"unknown": 0
},
"total": 2
},
"applied": {
"city": [
"ufa"
],
"category": [],
"date": "weekend",
"date_from": "2026-11-15",
"date_to": "2026-11-15",
"time_from": null,
"time_to": null,
"price_max": 1000,
"free_only": false,
"age": 7,
"age_rating": [],
"audience": null,
"status": [
"scheduled",
"rescheduled"
],
"q": null,
"sort": "date",
"active": [
"city",
"date",
"price_max",
"age"
]
},
"relaxations": [],
"hidden_unknown_price": 0,
"hidden_unknown_age": 0,
"reference_date": "2026-11-15",
"timezone": "Asia/Yekaterinburg"
}| Поле | Описание |
|---|---|
items | События страницы. К полям события добавлены matched_occurrence_ids (сеансы, прошедшие условия) и primary_occurrence_id (ближайший из них: его дату и статус показывает карточка). |
totalpagepage_sizetotal_pages | Пагинация. |
facets | Счётчики для фильтров: те же, что отдаёт GET /api/v1/facets. |
applied | Условия, которые сервер реально применил: вычисленные даты, статусы по умолчанию. В active перечислены условия, заданные пользователем. |
hidden_unknown_price | Сколько событий подошло бы, но цена у них не указана. |
hidden_unknown_age | Сколько событий подошло бы, но возрастная метка у них не указана. |
relaxations | Варианты ослабить условия. Заполняется, только когда total = 0. В query лежат готовые условия для этого же метода, в count число событий, которое найдётся. |
reference_datetimezone | Дата и пояс, от которых считались периоды. |
Когда по условиям ничего нет, список пуст, а в relaxations лежат варианты: какое одно условие снять и сколько событий тогда найдётся.
curl "https://89-125-129-170.nip.io/api/v1/events?city=sibay&date=today"{
"items": [],
"page": 1,
"page_size": 12,
"total": 0,
"total_pages": 0,
"facets": {
"cities": [
{
"id": "ufa",
"name": "Уфа",
"count": 2
},
{
"id": "sterlitamak",
"name": "Стерлитамак",
"count": 1
},
{
"id": "salavat",
"name": "Салават",
"count": 1
},
{
"id": "neftekamsk",
"name": "Нефтекамск",
"count": 0
},
{
"id": "oktyabrsky",
"name": "Октябрьский",
"count": 1
},
{
"id": "beloretsk",
"name": "Белорецк",
"count": 2
},
{
"id": "sibay",
"name": "Сибай",
"count": 0
},
{
"id": "kumertau",
"name": "Кумертау",
"count": 1
}
],
"categories": [
{
"id": "concert",
"name": "Концерты",
"count": 0
},
{
"id": "performance",
"name": "Спектакли",
"count": 0
},
{
"id": "exhibition",
"name": "Выставки",
"count": 0
},
{
"id": "festival",
"name": "Фестивали",
"count": 0
},
{
"id": "cinema",
"name": "Кино",
"count": 0
},
{
"id": "excursion",
"name": "Экскурсии",
"count": 0
},
{
"id": "other",
"name": "Другое",
"count": 0
}
],
"age_limits": [
{
"value": 0,
"count": 0
},
{
"value": 6,
"count": 0
},
{
"value": 12,
"count": 0
},
{
"value": 16,
"count": 0
},
{
"value": 18,
"count": 0
},
{
"value": null,
"count": 0
}
],
"price": {
"free": 0,
"paid": 0,
"unknown": 0
},
"total": 0
},
"applied": {
"city": [
"sibay"
],
"category": [],
"date": "today",
"date_from": "2026-11-15",
"date_to": "2026-11-15",
"time_from": null,
"time_to": null,
"price_max": null,
"free_only": false,
"age": null,
"age_rating": [],
"audience": null,
"status": [
"scheduled",
"rescheduled"
],
"q": null,
"sort": "date",
"active": [
"city",
"date"
]
},
"relaxations": [
{
"code": "date",
"codes": [
"date"
],
"label": {
"ru": "В любой день",
"ba": "Теләһә ҡайһы көндә"
},
"patch": {
"reset": false,
"set": {},
"unset": [
"hard.date"
]
},
"query": {
"city": [
"sibay"
]
},
"count": 2
},
{
"code": "city",
"codes": [
"city"
],
"label": {
"ru": "По всей республике",
"ba": "Бөтә республика буйынса"
},
"patch": {
"reset": false,
"set": {
"hard": {
"region": "bashkortostan"
}
},
"unset": [
"hard.cities"
]
},
"query": {
"date": "today"
},
"count": 8
}
],
"hidden_unknown_price": 0,
"hidden_unknown_age": 0,
"reference_date": "2026-11-15",
"timezone": "Asia/Yekaterinburg"
}Если события нет, ответ приходит с кодом 404:
curl "https://89-125-129-170.nip.io/api/v1/events/none"{
"code": "not_found",
"message": "Событие не найдено",
"details": [],
"request_id": "req_c9433fd14eb345d3a1fc"
}Счётчик по измерению считается без условия этого же измерения: при выбранном городе остальные города показывают, сколько событий даст переключение на них. В списках всегда есть все значения справочника, в том числе с нулём.
curl -X POST "https://89-125-129-170.nip.io/api/v1/assistant/query" \
-H "Content-Type: application/json" \
-d '{
"message": "куда пойти в субботу с ребёнком до 1000 ₽"
}'{
"action": "clarify",
"message": "В каком городе ищем?",
"constraints": {
"hard": {
"date": {
"from": "2026-11-21",
"to": "2026-11-21"
},
"max_price_per_person": 1000,
"currency": "RUB",
"participants": {
"children_count": 1
}
},
"soft": {
"preferences": [
"family_friendly"
]
}
},
"chips": [
{
"key": "hard.date",
"kind": "date",
"label": "Дата",
"value": "21 ноября",
"removable": true,
"unset": [
"hard.date"
]
},
{
"key": "hard.participants",
"kind": "participants",
"label": "Кто идёт",
"value": "с ребёнком, возраст не указан",
"removable": true,
"unset": [
"hard.participants"
]
},
{
"key": "hard.max_price_per_person",
"kind": "budget",
"label": "Бюджет",
"value": "до 1000 ₽ за человека",
"unset": [
"hard.max_price_per_person",
"hard.currency"
],
"removable": true
}
],
"assumptions": [
{
"key": "date_resolved",
"text": "суббота = 21 ноября"
},
{
"key": "price_per_person_by_default",
"text": "до 1000 ₽ за человека"
}
],
"clarify": {
"code": "city",
"pending": [
"city",
"child_age"
],
"question": "В каком городе ищем?",
"quick_replies": [
"Уфа",
"Стерлитамак",
"Салават",
"Нефтекамск",
"Октябрьский",
"Белорецк",
"Сибай",
"Кумертау",
"Вся республика"
]
},
"reference_date": "2026-11-15",
"timezone": "Asia/Yekaterinburg",
"engine": "rules-v2",
"trace_id": "25f66a5e-8d7c-4369-83ed-27ca7c03c6f5"
}Ответ пользователя уходит вместе с constraints из прошлого ответа. В примере limit равен 1, чтобы ответ был короче.
curl -X POST "https://89-125-129-170.nip.io/api/v1/assistant/query" \
-H "Content-Type: application/json" \
-d '{
"message": "в Уфе, 7 лет",
"answering": "city",
"limit": 1,
"previous": {
"hard": {
"date": {
"from": "2026-11-21",
"to": "2026-11-21"
},
"max_price_per_person": 1000,
"currency": "RUB",
"participants": {
"children_count": 1
}
},
"soft": {
"preferences": [
"family_friendly"
]
}
}
}'{
"action": "search",
"message": "Нашлось 8 событий. Показываю 1: самые подходящие.",
"constraints": {
"hard": {
"date": {
"from": "2026-11-21",
"to": "2026-11-21"
},
"participants": {
"children_count": 1,
"children_ages": [
7
]
},
"max_price_per_person": 1000,
"currency": "RUB",
"cities": [
"ufa"
]
},
"soft": {}
},
"chips": [
{
"key": "hard.cities",
"kind": "city",
"label": "Город",
"value": "Уфа",
"removable": true,
"unset": [
"hard.cities"
]
},
{
"key": "hard.date",
"kind": "date",
"label": "Дата",
"value": "21 ноября",
"removable": true,
"unset": [
"hard.date"
]
},
{
"key": "hard.participants",
"kind": "participants",
"label": "Кто идёт",
"value": "ребёнок 7 лет",
"removable": true,
"unset": [
"hard.participants"
]
},
{
"key": "hard.max_price_per_person",
"kind": "budget",
"label": "Бюджет",
"value": "до 1000 ₽ за человека",
"unset": [
"hard.max_price_per_person",
"hard.currency"
],
"removable": true
}
],
"assumptions": [
{
"key": "date_resolved",
"text": "суббота = 21 ноября"
},
{
"key": "price_per_person_by_default",
"text": "до 1000 ₽ за человека"
},
{
"key": "default_scheduled_disclosed",
"text": "отменённые сеансы и сеансы с неподтверждённым статусом скрыты"
}
],
"results": [
{
"event": {
"id": "fc2d50a3-de94-5e4a-a427-33c6aa08e2d4",
"title": "Выставка графики",
"normalized_title": "выставка графики",
"category": "exhibition",
"tags": [],
"date_start": "2026-11-13T00:00:00+05:00",
"date_end": "2026-12-02T00:00:00+05:00",
"duration_min": null,
"city": "Уфа",
"venue": "Башкирский государственный художественный музей им. М. В. Нестерова",
"address": null,
"lat": null,
"lon": null,
"description": "Рисунок, акварель и печатная графика. Спокойная выставка, на которую хватит часа.",
"price_min": 300,
"price_max": 300,
"is_free": false,
"age_limit": 0,
"language": null,
"org_link": "http://89.125.129.170/about#demo",
"source_url": "http://89.125.129.170/about#demo",
"source_type": "manual",
"source_hash": "4310b5ea74ac0ab51ce6e87a5e59fe47b1aa415c9042bd806da38e9267c36aab",
"verified_at": "2026-11-14T07:54:57+05:00",
"confidence": 0.95,
"status": "published",
"dedup_key": "выставка графики|2026-11-13|ven-ufa-nesterov",
"created_at": "2026-11-07T07:54:57+05:00",
"updated_at": "2026-11-14T07:54:57+05:00",
"city_id": "ufa",
"venue_id": "ven-ufa-nesterov",
"time_precision": "date",
"kind": "exhibition",
"popularity": 70,
"organizer": null,
"image": "/images/events/exhibition-1.webp",
"synthetic": true,
"sources": [
{
"source_url": "http://89.125.129.170/about#demo",
"source_type": "manual",
"source_hash": "4310b5ea74ac0ab51ce6e87a5e59fe47b1aa415c9042bd806da38e9267c36aab",
"verified_at": "2026-11-14T07:54:57+05:00",
"title": "Демо-источник: афиша площадки"
},
{
"source_url": "http://89.125.129.170/about#demo",
"source_type": "page",
"source_hash": "b7f273e50325369d1da551fd3a1b46adad813a53413ed7f95ca9afc256c90ce1",
"verified_at": "2026-11-14T07:54:57+05:00",
"title": "Демо-источник: страница организатора"
}
],
"occurrences": [
{
"id": "occ_demo_e1_1",
"date_start": "2026-11-13T00:00:00+05:00",
"date_end": "2026-12-02T00:00:00+05:00",
"time_precision": "date",
"status": "scheduled",
"status_verified": true,
"availability": "available",
"is_past": false
}
],
"offers": [
{
"price": 300,
"is_free": false,
"audience": "adult"
},
{
"price": 150,
"is_free": false,
"audience": "reduced"
}
],
"matched_occurrence_ids": [
"occ_demo_e1_1"
],
"primary_occurrence_id": "occ_demo_e1_1"
},
"occurrence_id": "occ_demo_e1_1",
"reasons": [
"13 ноября",
"подходит по возрасту: 0+",
"цена от 300 ₽, в бюджете",
"Уфа: Башкирский государственный художественный музей им. М. В. Нестерова"
],
"caveats": []
}
],
"total": 8,
"hidden_unknown_price": 0,
"hidden_unknown_age": 0,
"events_query": {
"city": [
"ufa"
],
"date_from": "2026-11-21",
"date_to": "2026-11-21",
"price_max": 1000,
"age": 7
},
"reference_date": "2026-11-15",
"timezone": "Asia/Yekaterinburg",
"engine": "rules-v2",
"trace_id": "6ab1fe2b-1e39-4603-ac40-a48fe465d55f"
}| Поле | Описание |
|---|---|
action | search: условий хватило, в ответе выдача. clarify: нужен ответ на вопрос. out_of_scope: запрос не про афишу или попытка обойти правила. |
message | Короткая реплика ассистента. |
constraints | Полное состояние условий { hard, soft }. Передайте его в previous следующего запроса. |
chips | Понятые условия для показа. Чтобы снять условие, отправьте patch.unset = chip.unset. |
assumptions | Допущения, которые сделал ассистент: например, какая дата у «субботы». |
clarify | Для action = clarify: вопрос, его код и готовые ответы quick_replies. |
results | Для action = search: событие, подходящий сеанс, причины reasons из полей события и предупреждения caveats. |
totalhidden_unknown_pricehidden_unknown_age | Сколько событий прошло условия и сколько не показано из-за неуказанной цены или возраста. |
relaxations | Варианты ослабить условия, когда total = 0. |
unsupported_actions | Что пользователь просил, а афиша не делает: например, purchase. Афиша не продаёт билеты. |
events_query | Те же условия для каталога: из них строится ссылка «показать все». |
GET /api/v1 отдаёт оглавление методов. Любой другой адрес под /api/v1 отвечает 404 с кодом not_found и с заголовками CORS.
curl "https://89-125-129-170.nip.io/api/v1"{
"name": "Афиша культуры Башкортостана",
"version": "v1",
"data": "mock",
"timezone": "Asia/Yekaterinburg",
"docs": "/developers",
"endpoints": [
{
"method": "GET",
"path": "/api/v1/events",
"description": "Список событий с фильтрами, сортировкой и счётчиками"
},
{
"method": "GET",
"path": "/api/v1/events/{id}",
"description": "Карточка события со всеми сеансами"
},
{
"method": "GET",
"path": "/api/v1/dictionaries",
"description": "Справочники: категории, города, возрастные метки, статусы"
},
{
"method": "GET",
"path": "/api/v1/facets",
"description": "Число событий по городам, категориям, возрасту и цене"
},
{
"method": "POST",
"path": "/api/v1/assistant/query",
"description": "Подбор событий по свободной фразе"
},
{
"method": "POST",
"path": "/api/v1/feedback",
"description": "Заявка организатора, уточнение к карточке или вопрос"
},
{
"method": "GET",
"path": "/api/v1/health",
"description": "Проверка работы API"
},
{
"method": "GET",
"path": "/api/v1/admin/events",
"description": "Админка: все события любого статуса (Bearer-токен модератора)"
},
{
"method": "PATCH",
"path": "/api/v1/admin/events/{id}",
"description": "Админка: правка полей и статуса, заголовок If-Match"
},
{
"method": "POST",
"path": "/api/v1/admin/imports",
"description": "Админка: импорт CSV или JSON с отчётом"
},
{
"method": "GET",
"path": "/api/v1/admin/moderation",
"description": "Админка: очередь спорных карточек"
},
{
"method": "POST",
"path": "/api/v1/admin/moderation/{id}/resolve",
"description": "Админка: решение по спорной карточке"
},
{
"method": "GET",
"path": "/api/v1/admin/audit",
"description": "Админка: журнал изменений"
}
]
}invalid_json| 400 |
| Тело запроса к ассистенту не разбирается как JSON. |
not_found | 404 | Нет такого события или такого метода. |
method_not_allowed | 405 | Метод не подходит адресу: например, POST на список событий. Разрешённые методы названы в сообщении и в заголовке Allow. |
payload_too_large | 413 | Тело запроса к ассистенту длиннее 20 000 знаков. |
internal_error | 500 | Сбой на сервере. Подробности остаются в журнале сервера. |
Неизвестные параметры запроса игнорируются.
| kind | Цена | Что значит |
|---|
free | Бесплатно | Все общие билеты бесплатные: источник сказал это явно. |
from | от 500 ₽ | Есть цена. Минимальная цена общего билета лежит в price_min, в рублях. |
unknown | Цена не указана | Источник не сообщил цену. Это не значит, что вход свободный. |
Эти коды принимают параметры city и category, атрибуты виджета и адрес /embed.
ufaУфаsterlitamakСтерлитамакsalavatСалаватneftekamskНефтекамскoktyabrskyОктябрьскийbeloretskБелорецкsibayСибайkumertauКумертауconcertКонцертыperformanceСпектаклиexhibitionВыставкиfestivalФестивалиcinemaКиноexcursionЭкскурсииotherДругое