ПРОМПТ · Выгрузка статистики MAX через API БОТИКИ Помоги мне создать и запустить скрипт выгрузки статистики моего MAX-канала через API БОТИКИ. Ниже приложена полная документация API: используй её как технический источник, не придумывай методы, фильтры и значения параметров. Перед написанием кода уточни: - Что выгружаем: подписчиков, переходы по ссылкам на посты или визиты лендинга. - Начало и конец периода, часовой пояс и необходимые фильтры. - Мою операционную систему и предпочтительный язык. Если предпочтений нет, предложи Python. - Настоящий базовый адрес API, если он не указан. {botica-api-domain} в документации — шаблон, а не рабочий адрес. Не проси присылать API-токен в чат. Используй переменную окружения BOTICA_API_TOKEN и объясни, как задать её локально. Базовый адрес также сделай настраиваемым. Не выводи токен в логи. Требования к решению: 1. Проверь параметры и допустимость фильтров для выбранного типа. Преобразуй даты в Unix-время в секундах с учётом подтверждённого часового пояса. 2. Реализуй полный цикл: создание заявки, опрос статуса с интервалом 5–10 секунд, скачивание каждой части из массива files. 3. Передавай Accept: application/json и Authorization во всех запросах, Content-Type: application/json — для JSON-тела. 4. Используй готовые ссылки download из ответа статуса. Перед отправкой токена проверь, что ссылка принадлежит тому же API-серверу. 5. Различай 429 с request_id текущей выгрузки и 429 по лимиту частоты с Retry-After. Не создавай параллельные заявки одного типа для канала. 6. Ограничь общее время ожидания, обработай failed и все коды ошибок из документации. Сообщения об ошибках должны объяснять пользователю следующий шаг. 7. Скачивай файлы сразу после completed, учитывай expires_at и size = 0. Не перезаписывай существующие локальные файлы без согласия пользователя. 8. Сохраняй CSV без изменения кодировки, разделителей и содержимого. Скачай все части, даже если файлов несколько. 9. Не обращайся к реальному API до настройки адреса, токена и параметров пользователем. Дай готовый код, список зависимостей и понятную пошаговую инструкцию запуска. Отдельно покажи, где поменять период, тип выгрузки, фильтры и папку сохранения. Если ты не запускал код с реальным API, скажи об этом прямо. Мои пожелания (можно заполнить или оставить пустыми) Тип выгрузки: Период и часовой пояс: Фильтры: Операционная система: Язык программирования: Папка сохранения: ──────────────────────────────────────── ПОЛНАЯ ДОКУМЕНТАЦИЯ API ──────────────────────────────────────── # API выгрузки статистики API для автоматической выгрузки статистики канала в формате CSV. Предназначено для интеграций (CRM и другие внешние системы). Выгрузка **асинхронная**: 1. Создать заявку 2. Опросить её статус 3. Скачать файл --- ## Базовый адрес ``` https://{botica-api-domain}/api/stat/v1 ``` Все запросы и ответы — в кодировке UTF-8. Тело запросов — `application/json`. Во **всех** запросах необходимо передавать заголовок: ``` Accept: application/json ``` --- ## Аутентификация Все запросы требуют токен канала в заголовке: ``` Authorization: Bearer <ваш-токен> ``` - Токен выпускается владельцем канала в панели управления и **привязан к одному каналу** — по нему определяется, статистику какого канала вы выгружаете. Передавать `channel_id` в запросах не нужно. - Токен показывается **один раз** при создании. Сохраните его — восстановить нельзя, при потере выпускается новый. - **Срок жизни токена — 1 год** с момента выпуска. После истечения запросы получают `401`; выпустите новый токен в панели заранее. - Токен можно отозвать или перевыпустить (ротация) в панели. **Ротация обрывает старый токен немедленно** — он удаляется в тот же момент, когда выдаётся новый. Планируйте замену на техническое окно: сначала получите новый токен, затем сразу пропишите его в интеграции. Запрос без токена или с недействительным (отозванным, истёкшим) токеном получает `401 Unauthorized`. --- ## Запрос количества подписчиков Возвращает количество подписчиков канала. ~~~http POST /api/stat/v1/runtime/participants ~~~ Структура ответа (`integer` — целое число): ~~~text { "participants": integer } ~~~ --- ## Сценарий использования 1. **Создать заявку** — `POST /exports`. В ответ придёт `request_id`. 2. **Опрашивать статус** — `GET /exports/{request_id}` раз в несколько секунд, пока `status` не станет `completed` или `failed`. 3. **Скачать файлы** — когда `status = completed`, скачать каждый файл из списка `files` по его ссылке `download`. Забирайте файлы сразу: срок их хранения ограничен (см. [Хранение файлов](#хранение-файлов)). --- ## Эндпоинты ### 1. Создать заявку на выгрузку ``` POST /exports ``` **Тело запроса:** ```json { "type": "export_post_link_visits", "period_from": 1735689600, "period_to": 1738368000, "filters": { "subscribed": true, "utm_source": "yandex" } } ``` | Поле | Тип | Обяз. | Описание | |---|---|---|---| | `type` | string | да | Тип выгрузки (см. [Типы выгрузок](#типы-выгрузок)) | | `period_from` | int (Unix-время, сек) | **да** | Начало периода | | `period_to` | int (Unix-время, сек) | **да** | Конец периода | | `filters` | object | нет | Фильтры. Набор допустимых полей зависит от `type` (см. [Фильтры](#фильтры)) | Границы периода включаются в выборку. Если `period_from` / `period_to` не переданы — запрос завершится ошибкой `422`. **Ответ `202 Accepted`:** ```json { "request_id": "0f8c1e2a-3b4c-4d5e-9f01-2a3b4c5d6e7f", "status": "pending" } ``` **Если по этому каналу и типу уже идёт выгрузка — `429 Too Many Requests`:** ```json { "request_id": "0f8c1e2a-3b4c-4d5e-9f01-2a3b4c5d6e7f", "status": "processing", "message": "Export of this type is already in progress for the channel." } ``` Дождитесь завершения текущей заявки (её `request_id` возвращён) — параллельно две выгрузки одного типа на канал не запускаются. В этом ответе `status` — статус уже идущей заявки, то есть `pending` или `processing`. Заголовка `Retry-After` здесь нет (он приходит только при превышении [лимита частоты](#ограничения-по-частоте-rate-limits)). Повторная заявка с теми же `type`, периодом и фильтрами может выполниться почти мгновенно: результат предыдущей выгрузки какое-то время переиспользуется. --- ### 2. Получить статус заявки ``` GET /exports/{request_id} ``` Опрашивайте этот эндпоинт, пока `status` не станет финальным (`completed` или `failed`). **Статусы:** | Статус | Значение | |---|---| | `pending` | Заявка принята, ждёт обработки | | `processing` | Файл формируется | | `completed` | Готово, файлы можно скачивать | | `failed` | Ошибка при формировании | **Ответ, пока идёт обработка:** ```json { "request_id": "0f8c1e2a-3b4c-4d5e-9f01-2a3b4c5d6e7f", "type": "export_post_link_visits", "status": "processing" } ``` **Ответ `completed`:** ```json { "request_id": "0f8c1e2a-3b4c-4d5e-9f01-2a3b4c5d6e7f", "type": "export_post_link_visits", "status": "completed", "files": [ { "name": "postlink_click_stat_01-01-2026_31-01-2026_777_part1.csv", "size": 12345678, "download": "https://{botica-api-domain}/api/stat/v1/exports/0f8c1e2a-.../download/1" }, { "name": "postlink_click_stat_01-01-2026_31-01-2026_777_part2.csv", "size": 9876543, "download": "https://{botica-api-domain}/api/stat/v1/exports/0f8c1e2a-.../download/2" } ], "expires_at": 1738454400 } ``` - `files` — список файлов. Большие выгрузки разбиваются на несколько частей; скачивайте все. - `size` — размер файла в байтах. Значение `0` означает, что файл уже удалён с диска — скачать его не получится, создайте заявку заново. - `expires_at` — Unix-время, после которого выгрузка считается устаревшей (см. [Хранение файлов](#хранение-файлов)). **Ответ при ошибке (`failed`):** ```json { "request_id": "0f8c1e2a-3b4c-4d5e-9f01-2a3b4c5d6e7f", "type": "export_post_link_visits", "status": "failed", "error": "Описание ошибки" } ``` --- ### 3. Скачать файл ``` GET /exports/{request_id}/download/{part} ``` Используйте готовые ссылки `download` из ответа статуса (не собирайте URL вручную). Запрос требует того же заголовка `Authorization`. - Возвращает содержимое CSV-файла (`Content-Type: text/csv; charset=UTF-8`, `Content-Disposition: attachment`). - `{part}` — номер части, начиная с `1`. - Если файла с таким номером в заявке нет — `404 Not Found`. - Если файл уже удалён с диска — `410 Gone`. --- ## Хранение файлов - `expires_at` в ответе статуса — момент, после которого выгрузка считается устаревшей. По умолчанию это **1 час** с момента готовности файлов. - Физически файлы удаляются фоновой чисткой, поэтому какое-то время после `expires_at` ссылка ещё может отдавать файл. **Не полагайтесь на это**: гарантий доступности после `expires_at` нет, забирайте файлы сразу после `completed`. - После удаления файла с диска скачивание отвечает `410 Gone`, а `size` в статусе становится `0`. В этом случае создайте новую заявку. --- ## Типы выгрузок | `type` | Что выгружает | |---|---| | `export_post_link_visits` | Переходы по ссылкам на посты | | `export_webapp_visits` | Переходы с лендинг-вебапп | | `export_subscribers` | Подписчики канала | ### Колонки CSV **`export_post_link_visits`:** `Дата`, `Время`, `Подписка`, `MaxID`, `first_name`, `last_name`, `Ссылка на пост`, `Название поста`, `utm_source`, `utm_medium`, `utm_campaign`, `utm_content`, `utm_term`, `Подписан/Не подписан`, `Дата подписки`, `Время подписки`, `Дата отписки`, `Время отписки` **`export_webapp_visits`:** `Дата`, `Время`, `Подписка`, `MaxID`, `Yandex ClientID`, `first_name`, `last_name`, `utm_source`, `utm_medium`, `utm_campaign`, `utm_content`, `utm_term`, `Пост`, `Подписан/Не подписан`, `Дата подписки`, `Время подписки`, `Дата отписки`, `Время отписки` **`export_subscribers`:** `max_id`, `first_name`, `last_name`, `utm_source`, `utm_medium`, `utm_campaign`, `utm_content`, `utm_term`, `Подписан/Не подписан`, `Дата подписки`, `Время подписки`, `Дата отписки`, `Время отписки`, `Количество дней в канале` --- ## Фильтры Фильтры передаются в объекте `filters`. Набор допустимых полей **зависит от типа**. Поле, недопустимое для выбранного типа, приводит к ошибке `422`. | Фильтр | Тип | `post_link_visits` | `webapp_visits` | `subscribers` | Описание | |---|---|:---:|:---:|:---:|---| | `max_id` | int | ✓ | ✓ | ✓ | MAX ID пользователя | | `yandex_client_id` | string | — | ✓ | — | Yandex ClientID | | `post_link_id` | int | ✓ | — | — | ID ссылки на пост | | `subscribed` | bool | ✓ | ✓ | ✓ | `true` — только подписанные, `false` — только неподписанные | | `utm_source` | string | ✓ | ✓ | — | Точное совпадение | | `utm_medium` | string | ✓ | ✓ | — | Точное совпадение | | `utm_campaign` | string | ✓ | ✓ | — | Точное совпадение | | `utm_content` | string | ✓ | ✓ | — | Точное совпадение | | `utm_term` | string | ✓ | ✓ | — | Точное совпадение | Строковые фильтры — не длиннее 255 символов. Период (`period_from` / `period_to`) задаётся отдельными полями запроса, а не в `filters`. --- ## Формат CSV - Кодировка **UTF-8 с BOM** — файлы корректно открываются в Excel. - Разделитель — точка с запятой `;`. - Значения при необходимости берутся в двойные кавычки. - Перенос строк — `CRLF`. - Первая строка каждого файла — заголовки колонок (повторяются в каждой части). - Выгрузка режется на части по **100 000 строк** данных на файл. Если часть всего одна, суффикс `_partN` в имени не добавляется (например `postlink_click_stat_01-01-2026_31-01-2026_777.csv`); если частей несколько — `..._part1.csv`, `..._part2.csv` и т.д. Обрабатывайте весь список `files`, не полагаясь на имена. --- ## Ограничения по частоте (rate limits) Лимиты считаются по токену: | Действие | Лимит | |---|---| | Создание заявок (`POST /exports`) | 5 в минуту | | Опрос статуса и скачивание (`GET`) | 60 в минуту | Опрос статуса и скачивание делят общий счётчик в 60 запросов в минуту. При превышении — `429 Too Many Requests` с заголовком `Retry-After`. Рекомендуемый интервал опроса статуса — 5–10 секунд. --- ## Коды ответов | Код | Значение | |---|---| | `202 Accepted` | Заявка создана | | `200 OK` | Успешный ответ статуса / файл | | `401 Unauthorized` | Токен отсутствует, отозван или истёк | | `403 Forbidden` | Токену не хватает прав на выгрузку | | `404 Not Found` | Заявка не найдена (или принадлежит другому каналу), либо нет такой части файла | | `410 Gone` | Файл уже удалён с диска | | `422 Unprocessable Entity` | Ошибка валидации (неверный `type`, отсутствует период, недопустимый фильтр и т.п.) | | `429 Too Many Requests` | Превышен лимит частоты или по каналу/типу уже идёт выгрузка | Ошибки валидации возвращаются в формате: ```json { "errors": { "type": ["The selected type is invalid."] } } ``` --- ## Пример полного цикла (curl) **1. Создать заявку:** ```bash curl -X POST https://{botica-api-domain}/api/stat/v1/exports \ -H "Authorization: Bearer <ваш-токен>" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "type": "export_post_link_visits", "period_from": 1735689600, "period_to": 1738368000, "filters": { "subscribed": true, "utm_source": "yandex" } }' ``` Ответ: ```json { "request_id": "0f8c1e2a-3b4c-4d5e-9f01-2a3b4c5d6e7f", "status": "pending" } ``` **2. Опросить статус:** ```bash curl https://{botica-api-domain}/api/stat/v1/exports/0f8c1e2a-3b4c-4d5e-9f01-2a3b4c5d6e7f \ -H "Authorization: Bearer <ваш-токен>" \ -H "Accept: application/json" ``` Повторяйте, пока не придёт `"status": "completed"` со списком `files`. **3. Скачать файлы:** ```bash curl -OJ https://{botica-api-domain}/api/stat/v1/exports/0f8c1e2a-3b4c-4d5e-9f01-2a3b4c5d6e7f/download/1 \ -H "Authorization: Bearer <ваш-токен>" \ -H "Accept: application/json" ```