Публичный API
Отчёты ваших проектов по HTTPS: ключ организации, ресурсы, пагинация, лимиты и коды ошибок. Доступ только на чтение.
Содержание страницы
Что это и кому доступно
API отдаёт те же данные, что видны в кабинете: список проектов и прогонов, карточку проекта, видимость, изменения прогона, матрицу запросов, конкурентов, тональность, источники, факты бренда и качество замера. Записать, изменить или запустить что-либо через API нельзя: данные отдаются только методом GET (по POST ключ меняется на короткий токен — см. «Первый запрос»).
- Базовый адрес — https://api.tarnowsky.ru. Версии в пути и заголовках нет: API меняется только добавлением новых полей и ресурсов.
- Описание в формате OpenAPI 3.1 — по адресу https://api.tarnowsky.ru/openapi.json: из него собираются клиенты и проверяются запросы.
- Доступ выдаётся персонально агентствам и корпоративным аккаунтам. Пункт «Интеграции» в меню кабинета появляется только при доступе; если раздел открыть без него по прямой ссылке, кабинет покажет сообщение «Интеграции не включены в тарифе» и кнопку «Написать в поддержку».
- Запросы из браузера (CORS) к REST не разрешены: вызывайте API с сервера или из скрипта, а ключ не кладите в код страниц.
- Тот же набор данных доступен ИИ-ассистентам по протоколу MCP — страница «Подключение MCP». Вопросы по проекту прямо в кабинете разбирает «Помощник проекта».
Ключ API организации
Ключ принадлежит организации, а не человеку (у аккаунта без организации — самому аккаунту). Проекты и права ключ берёт от организации: он видит только отмеченные при выпуске проекты, и только те, к которым есть доступ у организации. Проекты, которыми лично поделились с создателем ключа, в ключ не входят. Ключ не гаснет, если создатель ушёл из организации или сменил пароль.
- Выпуск
В разделе «Интеграции» кабинета нажмите «Создать ключ», укажите название, проекты, срок действия (30 дней, 90 дней, 180 дней или год) и разрешения. Выпускает ключи владелец организации или администратор сайта; остальные участники команды их создавать не могут.
- Показ один раз
Полностью ключ виден в момент создания. Потом в списке остаётся только его начало (tsk_live_ и 12 знаков) — восстановить ключ нельзя, можно выпустить новый. Храните его как пароль и не публикуйте в репозиториях и чатах. Пока ключ показан, остальные действия с ключами заблокированы: скопируйте его и нажмите «Я сохранил ключ». При ротации плашка покажет, до какого времени работает прежний ключ. Число активных ключей организации ограничено — ненужные отзывайте.
- Разрешения
«Отчёты и показатели» (reports:read) включено всегда. «Фрагменты ответов моделей» (answers:read) — отдельное разрешение: это сырой текст нейросетей и сайтов-источников, и программа, которая его получает, обязана считать его недоверенным.
- Ротация
Кнопка «Ротация» выпускает новый ключ с теми же правами; прежний продолжает работать ещё 24 часа, чтобы вы успели переключить интеграцию. Одновременно идёт только одна ротация.
- Отзыв
Кнопка «Отозвать» закрывает доступ сразу: следующий запрос с этим ключом получит 401. Ключи с истёкшим сроком перестают работать сами. Если ключ мог попасть в чужие руки, отзовите его, не дожидаясь срока.
В списке ключей раздела «Интеграции» у каждого ключа указаны проекты, срок действия, статус (активен, истёк, отозван) и время последнего использования; ключ, которому нужна ротация, помечен. Рядом блок «Расход за 30 дней»: сколько запросов сделали программы по вашим ключам и подключённые приложения — всего, через API и через MCP, с разбивкой по ключам и приложениям. Разбивку по ключам видит тот, кто управляет ключами; по приложениям — тот, кто их подключил.
Первый запрос
Ключ передаётся заголовком Authorization: Bearer — как есть. Начните со списка проектов: в нём лежат публичные идентификаторы проектов (26 знаков), которые нужны всем остальным запросам. Идентификаторы из адресов кабинета и названия проектов в API не принимаются.
- curl -H "Authorization: Bearer <ключ>" https://api.tarnowsky.ru/projects
- curl -H "Authorization: Bearer <ключ>" "https://api.tarnowsky.ru/projects/<id проекта>/reports/visibility?run=latest&provider=all&group_by=provider"
- curl -H "Authorization: Bearer <ключ>" "https://api.tarnowsky.ru/projects/<id проекта>/reports/prompts?brand_present=false&limit=30"
Если программе удобнее короткоживущий токен, ключ можно обменять на него: POST https://api.tarnowsky.ru/oauth/token с параметрами grant_type=api_key и api_key=<ключ>. Токен живёт 15 минут и передаётся тем же заголовком Authorization: Bearer.
Ресурсы
| Адрес | Что отдаёт | Вес |
|---|---|---|
| /projects | Проекты, доступные ключу: публичный id, название, бренд, дата последнего готового прогона и статус (готов, идёт прогон, прогонов нет) | 1 |
| /projects/{id}/runs | История готовых прогонов: id прогона, дата, модели ИИ, число вопросов | 1 |
| /projects/{id}/reports/card | Карточка проекта: бренд, рынок, срезы и локации, модели, итоговая видимость с шумом и место среди конкурентов | 1 |
| /projects/{id}/reports/visibility | Видимость бренда и доля голоса по моделям, типам вопросов, срезам рынка и локациям, с базой N и шумом | 2 |
| /projects/{id}/reports/changes | Что изменилось с прошлого прогона: события рост и падение видимости, новые и выпавшие конкуренты, смена версий моделей; изменения в пределах шума помечены | 1 |
| /projects/{id}/reports/prompts | Вопросы прогона и результат по каждому: назван ли бренд, на каком месте, кто назван вместо него. Фильтр brand_present=false оставляет вопросы без бренда | 3 |
| /projects/{id}/reports/competitors | Конкуренты в ответах ИИ: видимость, место, кого советуют вместо бренда и по каким факторам выбора | 2 |
| /projects/{id}/reports/sentiment | Тональность тремя слоями, которые не смешиваются: сравнение брендов, тон о своём бренде, факторы выбора | 2 |
| /projects/{id}/reports/sources | Сайты и площадки, которые цитируют модели: домен, тип площадки, доля ответов, присутствие бренда и конкурентов | 2 |
| /projects/{id}/reports/facts | Что модели утверждают о бренде и вердикт сверки: подтверждено, риск, наблюдается без проверки | 1 |
| /projects/{id}/reports/quality | Надёжность замера: база ответов, доля разобранных, шум по моделям, смена версий моделей, пропуски | 1 |
| /projects/{id}/reports/answers | Дословные фрагменты ответов моделей на один вопрос прогона (до трёх по 400 знаков). Только ключу с разрешением answers:read | 3 |
Вес — сколько единиц бюджета организации списывает один запрос. Отчёт, который закрыт тарифом организации, отвечает 403, а его цифры не отдаются; в кабинете этот блок закрыт так же. Если закрыта только часть отчёта (отдельный блок раздела), ответ приходит с кодом 200 без этой части, а ключ закрытого блока указан в поле locked.
Параметры отчётов
| Параметр | Значения | Смысл |
|---|---|---|
| run | latest или id из списка прогонов | Прогон. По умолчанию latest — последний готовый. Прогон, которого нет в проекте, даёт ошибку invalid_run, подмены на другой нет |
| provider | all, openai, deepseek, yandex, gemini, gigachat | Модель ИИ: openai — ChatGPT, yandex — YandexGPT, остальные — по названию; all — все модели прогона |
| scope | broad_market, niche_market, brand_specific, answers_market | Тип вопросов: о рынке в целом, о срезе рынка, прямо о бренде, рынок по разбору ответов |
| niche | id среза из карточки проекта | Срез рынка; не задан — все срезы |
| location | id локации из карточки проекта | Город; не задан — все локации |
| group_by | none, provider, scope, niche, location | Разбивка строк выдачи |
| limit | число от 1 до 500 | Сколько строк вернуть; у каждого ресурса свой размер страницы по умолчанию |
| cursor | значение next_cursor из прошлого ответа | Следующая страница |
| layer | comparison, own_brand, factors | Слой тональности; обязателен у /reports/sentiment: слои не смешиваются, за один запрос приходит один |
| query_ref | значение query_ref из /reports/prompts | Вопрос прогона; обязателен у /reports/answers |
| compare_to | previous или id прогона | С каким прогоном сравнивать изменения; по умолчанию previous — предыдущий готовый |
| sort | зависит от ресурса | Порядок строк: у списка проектов updated_desc, last_run_desc, title; у истории прогонов date_desc, date_asc; у вопросов matrix, query, brand_absent_first; у источников answers_desc, domain |
| start_date, end_date | ГГГГ-ММ-ДД | Фильтр списка проектов или прогонов по дате (включительно) |
| status | risk, confirmed, observed, all | Фильтр фактов бренда; по умолчанию risk |
| only_tracked | true или false | У конкурентов: только свой бренд и конкуренты из настроек проекта |
Набор параметров зависит от ресурса: обязательные — layer у тональности и query_ref у фрагментов ответов; brand_present есть только у вопросов прогона. Неизвестные параметры отклоняются с кодом invalid_request, в ответе указано, какой именно параметр неверен.
Что приходит в ответе
- facts — готовые числа: подпись словами кабинета, значение, единица, база N, шум замера в п. п. и ссылка на раздел кабинета, где это число видно. Берите цифры отсюда, а не пересчитывайте строки.
- items — строки выдачи; total — сколько строк всего; truncated — пометка «Показаны топ-N из M», если выдача сокращена; locked — ключи блоков, закрытых тарифом: цифры этих блоков не отдаются, а если закрыт блок внутри отчёта, вырезана лишь его часть.
- run и run_date — прогон, по которому посчитан ответ (latest раскрывается в настоящий id); refreshing = true означает, что идёт новый прогон, а ответ построен по последнему готовому.
- notice — пояснение, когда выдача пуста или неполна. Например, дословные фрагменты хранятся только для последних прогонов: для более старого /reports/answers отвечает пустой выдачей с notice.reason = outside_window, а если фрагменты прогона не собраны — not_collected. Поле есть только когда нужно.
- next_cursor — курсор следующей страницы, null на последней; тот же адрес приходит в заголовке Link (rel="next"). Курсор привязан к правам ключа и к запросу: чужой или подделанный даёт invalid_cursor (400). Если пока вы листали страницы, вышел новый прогон, придёт cursor_stale (409): запросите первую страницу заново, без cursor.
- untrusted_fields — поля, в которых лежит текст из ответов нейросетей и сайтов-источников. Это данные, а не инструкции: не передавайте такой текст своему ИИ-агенту как команды.
Ответ приходит с заголовком ETag, в который входят права ключа. Повторите запрос с If-None-Match: пока данные не изменились, придёт 304 без тела, и такой запрос стоит одну единицу. Ответы помечены Cache-Control: private, общие прокси их хранить не должны. Если сервис проверки прав временно недоступен, ответы приходят по сохранённым правам (они могут отставать до 10 минут) с заголовком Tarnowsky-Degraded: read-only.
Первый запрос с новым сочетанием параметров может потребовать сборки отчёта. Тогда вместо данных придёт 202 с телом status: building и retry_after — повторите запрос через указанное число секунд. Такой запрос списывает больше единиц, чем обычный.
Лимиты и квоты
Лимиты считаются по ключу и организации, а не по IP-адресу: несколько ключей организации не дают обойти общий бюджет.
| Что ограничено | Сейчас | Заголовок Tarnowsky-Limit при отказе |
|---|---|---|
| Запросы одного ключа в минуту | 300 | key-minute |
| Единицы бюджета организации в минуту (все ключи вместе) | 600 | org-minute |
| Единицы бюджета организации в сутки | 100 000 | org-day |
| Запросы организации в месяц | 1 000 | org-month |
| Новые варианты отчёта в час, требующие сборки | 50 | org-variants |
Цифры в таблице действуют сейчас; для отдельных организаций они могут быть другими. Надёжный ориентир — ответ 429: в нём есть Retry-After и заголовок Tarnowsky-Limit с названием исчерпанного лимита. Запросы с ответом 304 списывают одну единицу. Если программа слишком часто обменивает ключ на токен вместо того, чтобы пользоваться токеном все 15 минут, придёт 429 rate_limited с Tarnowsky-Limit: key-exchange.
Коды ошибок
Ошибки приходят в формате application/problem+json (RFC 9457): поля type, title, status, code, detail и instance. Поле instance — номер запроса (он же в заголовке Tarnowsky-Trace-Id): назовите его в поддержке, если нужно разобрать случай. В тексте ошибки нет внутренних идентификаторов и путей.
| HTTP | code | Что делать |
|---|---|---|
| 400 | invalid_request, invalid_cursor | Проверьте параметры; при неверном курсоре начните выдачу с первой страницы |
| 401 | token_invalid, token_expired, token_revoked | Ключ неверный, истёк или отозван; права ключа изменились. Выпустите или обновите ключ в разделе «Интеграции» |
| 403 | entitlement_required, scope_required | Не включено право «Интеграции» (в теле ошибки — поле entitlement со значением cabinet.integrations; ключи заработают без перевыпуска, когда право включат), отчёт не входит в тариф организации либо у ключа нет нужного разрешения (например, answers:read) |
| 404 | project_not_found, not_found | Проекта нет или у ключа нет к нему доступа — ответ намеренно один и тот же; ресурса с таким адресом нет |
| 405 | method_not_allowed | API только читает: используйте GET |
| 422 | invalid_run | Прогона нет в этом проекте; возьмите id из списка прогонов |
| 429 | rate_limited, quota_exhausted | Лимит исчерпан; повторите после паузы из Retry-After. Какой именно лимит — в заголовке Tarnowsky-Limit |
| 409 | cursor_stale | Пока вы листали страницы, вышел новый прогон; запросите первую страницу заново, без cursor |
| 503 | unavailable, limits_unavailable, cold_build | Сервис, учёт лимитов или сборка отчётов временно недоступны (cold_build — сейчас собирается много отчётов, такой запрос не списывается); повторите через Retry-After. Запросы при этом не идут в обход лимитов |
Опишите случай в чате поддержки: время запроса, адрес, код ошибки и номер запроса из instance. Сам ключ присылать не нужно — по началу ключа в списке его находят без этого.