Версия API: v1. Версия документа 1.3 от 24.10.2026. Адрес сервиса — https://pdn-152.ru.
1. Что умеет API
API даёт программный доступ к той же проверке, что и сайт, и возвращает полный подробный отчёт: нарушения со ссылками на закон, статью и часть, доказательства, санкции, практику, рекомендации и задания разработчику.
Для кого: веб-студии и агентства (массовые проверки клиентских сайтов, отчёты в своих системах), разработчики CMS и конструкторов, юристы и консультанты, интеграторы CRM.
Что можно делать:
| Возможность | Стоит кредитов |
|---|---|
| Запустить проверку сайта | 1 кредит за проверку |
| Получить статус, полный отчёт в JSON или PDF, список проверок | нет |
| Справочники: проверки каталога, профили организаций, расчёт штрафов | нет |
| Поиск по базе норм и практики | нет |
| Настроить вебхуки о завершении проверок | нет |
Генерация пакета документов через API пока недоступна — только в личном кабинете.
2. Как начать
- Войдите в личный кабинет через Яндекс ID или VK ID.
- В разделе «API и MCP» купите пакет проверок (кредитов).
- Создайте ключ: укажите название, права и, по желанию, IP-адреса, с которых разрешены запросы. Ключ показывается один раз — сохраните его в хранилище секретов.
- Выполните первый запрос:
export BASE="https://pdn-152.ru/v1"
export KEY="ck_live_ab12cd34_…"
curl -s "$BASE/account" -H "Authorization: Bearer $KEY"
{
"user_id": "01927f3a-5c1e-7b2a-9d4e-0a1b2c3d4e5f",
"credits": {"balance": 48, "expiring": [{"amount": 48, "expires_at": "2027-09-23T00:00:00Z"}]},
"key": {"prefix": "ab12cd34", "name": "CRM агентства", "scopes": ["scans:write", "scans:read", "kb:read"], "mode": "live"},
"limits": {"requests_per_minute": 60, "scan_creates_per_minute": 10, "concurrent_scans": 5}
}
3. Основы
3.1 Адрес и формат
- Базовый адрес:
https://pdn-152.ru/v1. Только HTTPS. - Запросы и ответы — JSON в UTF-8 (
Content-Type: application/json), кроме PDF. - Даты — ISO 8601 в UTC (
2026-09-23T14:05:00Z). - Идентификаторы — UUID.
- Деньги — рубли целыми числами (
150000= 150 000 ₽).
3.2 Аутентификация
Каждый запрос содержит заголовок:
Authorization: Bearer ck_live_<префикс>_<секрет>
Передавать ключ в адресе запроса нельзя: такие запросы отклоняются с кодом 400, а ключ рекомендуется перевыпустить.
Типы ключей:
| Префикс | Режим | Поведение |
|---|---|---|
ck_live_ |
боевой | настоящие проверки, списываются кредиты |
ck_test_ |
тестовый | кредиты не тратятся, сайт не сканируется: через 5–10 секунд возвращается демонстрационный отчёт с фиксированными данными. До 100 проверок в сутки. Для отладки интеграции |
Права ключа:
| Право | Разрешает |
|---|---|
scans:write |
создавать проверки (тратит кредиты) |
scans:read |
читать проверки и отчёты |
kb:read |
справочники, штрафы, база норм и практики |
webhooks:manage |
управлять вебхуками |
3.3 Кредиты
- 1 проверка = 1 кредит, списывается при создании проверки.
- Если проверка завершилась статусом
error(сайт недоступен, заблокировал обход, внутренняя ошибка), кредит возвращается автоматически. Проверка со статусомpartialсчитается выполненной. - Повтор запроса с тем же
Idempotency-Keyв течение 24 часов не списывает кредит повторно и возвращает ту же проверку. - Кредиты действуют 365 дней с даты покупки; сначала расходуются те, что истекают раньше.
- При нехватке кредитов — ошибка
402 insufficient_credits.
Пакеты кредитов покупаются в личном кабинете («API и MCP» → «Купить проверки»):
| Пакет | Скидка | Цена одной проверки |
|---|---|---|
| 10 проверок | 10% | 1 701 ₽ |
| 50 проверок | 20% | 1 512 ₽ |
| 100 проверок | 30% | 1 323 ₽ |
Скидка считается от цены подробной проверки на сайте (1 890 ₽). Актуальные цены — на странице /tarify.
3.4 Идемпотентность
Для POST /scans рекомендуется заголовок Idempotency-Key — любая уникальная строка до 64 символов (например, UUID или crm-client-1842-2026-09-23). Если ответ не дошёл из-за сети, повторите запрос с тем же ключом: будет возвращена уже созданная проверка. Тот же ключ с другим телом запроса — ошибка 409 idempotency_conflict.
3.5 Лимиты
| Лимит | Значение |
|---|---|
| Запросов в минуту на ключ | 60 |
| Создание проверок в минуту | 10 |
| Одновременно выполняемых проверок | 5 (остальные ждут в очереди со статусом queued) |
Каждый ответ содержит заголовки X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset (секунд до сброса). При превышении — 429 rate_limited и заголовок Retry-After. Для тарифов агентств лимиты повышаются по запросу.
3.6 Пагинация
Списки возвращают до limit элементов (по умолчанию 20, максимум 100) и курсор следующей страницы:
{"data": [ … ], "next_cursor": "eyJpZCI6IjAxOTI3…", "has_more": true}
Следующая страница: ?cursor=<next_cursor>.
3.7 Ошибки
Формат — application/problem+json (RFC 9457):
{
"type": "https://pdn-152.ru/docs/api/errors#insufficient_credits",
"title": "Недостаточно кредитов",
"status": 402,
"code": "insufficient_credits",
"detail": "На балансе 0 кредитов. Пополните баланс в личном кабинете.",
"request_id": "req_01927f3b8c2d"
}
| HTTP | code |
Что значит | Что делать |
|---|---|---|---|
| 400 | validation_error |
неверные параметры; в ответе поле errors со списком |
исправить запрос |
| 400 | invalid_url |
адрес не разобран или схема не http/https | исправить адрес |
| 400 | key_in_url |
ключ передан в адресе | перевыпустить ключ, передавать в заголовке |
| 401 | unauthorized |
нет ключа, ключ неверный или отозван | проверить ключ |
| 402 | insufficient_credits |
нет кредитов | пополнить баланс |
| 403 | forbidden_scope |
у ключа нет нужного права | выпустить ключ с правом |
| 403 | ip_not_allowed |
запрос с IP вне списка ключа | добавить IP в настройках ключа |
| 404 | not_found |
объекта нет или он принадлежит другому аккаунту | проверить идентификатор |
| 409 | idempotency_conflict |
тот же Idempotency-Key с другим телом |
новый ключ идемпотентности |
| 409 | scan_not_ready |
отчёт ещё не готов | дождаться done или partial |
| 422 | url_forbidden |
адрес ведёт во внутреннюю сеть, на IP-адрес или нестандартный порт | проверять только публичные сайты по доменному имени |
| 422 | domain_opted_out |
владелец сайта отказался от автоматических проверок | — |
| 429 | rate_limited |
превышен лимит | повторить после Retry-After |
| 500 | internal_error |
ошибка сервиса | повторить позже; сообщить request_id в поддержку |
| 503 | service_busy |
очередь перегружена | повторить после Retry-After |
3.8 Версии
Путь содержит версию (/v1). Внутри версии добавляются только новые необязательные поля и новые значения перечислений — клиенты должны игнорировать неизвестные поля. Несовместимые изменения выходят в /v2; старая версия работает не меньше 6 месяцев после объявления, её ответы содержат заголовки Deprecation и Sunset. История — в разделе 9.
4. Проверки
4.1 Создать проверку
POST /scans — право scans:write, 1 кредит.
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
url |
строка | да | адрес сайта, например https://example.ru. Проверяется главная страница и до 70 страниц сайта |
profile |
строка | нет | профиль организации (GET /profiles): general (по умолчанию), shop, services, clinic, education, b2b |
org_type |
строка | нет | для расчёта штрафов: legal (по умолчанию), ip, self_employed, person |
callback_url |
строка | нет | адрес для одноразового уведомления о завершении этой проверки (в дополнение к вебхукам из раздела 7); только https. Уведомление приходит без подписи (общего секрета нет), в теле — номер проверки, статус, external_id и краткие итоги: результат перепроверяйте запросом GET /scans/{id} |
external_id |
строка | нет | ваш идентификатор (до 128 символов), возвращается в ответах и вебхуках |
reuse_recent |
логический | нет | true — вернуть свою проверку этого домена не старше 4 часов без списания кредита, если она есть. По умолчанию false |
curl -s -X POST "$BASE/scans" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: crm-client-1842-2026-09-23" \
-d '{"url": "https://example.ru", "profile": "shop", "org_type": "legal", "external_id": "client-1842"}'
Ответ 202 Accepted:
{
"id": "01927f40-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
"status": "queued",
"url": "https://example.ru/",
"domain": "example.ru",
"profile": "shop",
"org_type": "legal",
"external_id": "client-1842",
"credits_charged": 1,
"created_at": "2026-09-23T14:05:00Z",
"links": {"self": "/v1/scans/01927f40-…", "report": "/v1/scans/01927f40-…/report"}
}
Проверка обычно занимает 1–10 минут, максимум 10 минут на сайт. По умолчанию каждый запрос запускает свежую проверку. Если передать "reuse_recent": true, а тот же домен с тем же профилем проверялся вашим аккаунтом менее 4 часов назад, результат выдаётся из этой проверки мгновенно и кредит не списывается; поле source_scan_id покажет исходную проверку, а created_at — время, когда она выполнена. Показывайте это время своим пользователям: сайт мог измениться после проверки, а новая полная проверка того же сайта при reuse_recent возможна через 4 часа после предыдущей. Чужие проверки (других аккаунтов и анонимные) через API никогда не выдаются. Если владелец сайта запретил проверки другими, возвращается 422 domain_opted_out, кредит не списывается.
4.2 Статус проверки
GET /scans/{id} — право scans:read.
{
"id": "01927f40-…",
"status": "done",
"stage": "completed",
"url": "https://example.ru/",
"domain": "example.ru",
"profile": "shop",
"org_type": "legal",
"external_id": "client-1842",
"created_at": "2026-09-23T14:05:00Z",
"finished_at": "2026-09-23T14:07:12Z",
"summary": {
"score": 41,
"zone": "red",
"check_status": "ok",
"check_failed_reason": null,
"pages_checked": 34,
"violations": 9,
"risks": 4,
"manual": 7,
"passed": 58,
"fines_total": {"min": 520000, "max": 1360000, "currency": "RUB", "org_type": "legal"}
},
"kb_version": "нормы на 23.09.2026",
"catalog_version": "2026-09-21"
}
Статусы:
status |
Значение |
|---|---|
queued |
в очереди |
crawling |
обход сайта |
analyzing |
анализ правил и документов |
done |
готово |
partial |
готово частично: обход остановило время или лимит страниц, часть страниц не открылась или сработала защита от ботов; в отчёте указано, что не проверено |
error |
проверка не выполнена; поле error.code: site_unreachable, blocked_by_site, timeout, internal; кредит возвращён |
Рекомендуемый опрос: каждые 5–10 секунд, либо используйте вебхуки.
Поля summary, добавленные в версии 1.35 плана (расширение ответа, существующие поля не менялись):
| Поле | Значение |
|---|---|
check_status |
ok — полная проверка; partial — частичная (причины в отчёте); failed — «Не удалось проверить сайт» |
check_failed_reason |
только при failed: empty_page — главная не открылась или пуста; few_pages — просмотрено слишком мало страниц для проверки |
pages_checked |
сколько страниц просмотрено |
score, zone |
null, если check_status = failed: оценки нет |
Если проверка не состоялась (check_status = failed), оценки и зоны нет (null), нарушений, рисков и штрафов нет (в summary нули), кредит возвращается автоматически, как при ошибке; статус при этом остаётся done или partial. Это не ошибка вашего запроса: повторите проверку позже. Сайт, у которого всего одна страница, проверкой, которая не состоялась, не считается: отчёт строится по ней по всем пунктам.
4.3 Список проверок
GET /scans?domain=example.ru&status=done&created_after=2026-09-01T00:00:00Z&external_id=client-1842&limit=20 — право scans:read. Возвращает проверки аккаунта (включая созданные на сайте и через MCP), новые первыми, в формате ответа 4.2.
4.4 Полный отчёт
GET /scans/{id}/report — право scans:read. Доступен при статусе done или partial.
Параметры:
| Параметр | Значения | По умолчанию |
|---|---|---|
format |
json, pdf |
json |
include |
через запятую: findings, passed, manual, services, cookies, mismatches, pages, plan |
всё |
status |
фильтр карточек: violation, risk, manual, pass |
все |
law |
фильтр по закону: 152-FZ, ZOZPP, 38-FZ, 436-FZ, 168-FZ, 118n, 1493 |
все |
PDF: GET /scans/{id}/report?format=pdf возвращает 302 на временную ссылку (5 минут) либо файл с Content-Type: application/pdf при заголовке Accept: application/pdf.
Как работают параметры. Итоги (summary), оценка и разбивка по законам (by_law) есть в любом ответе. include=findings — карточки со статусами violation, risk и error; include=passed добавляет в тот же список findings карточки pass; include=manual даёт списки manual_checks и recommended — пункты для ручной проверки. Фильтр status сужает карточки: violation, risk и pass — в findings, manual — в manual_checks. Фильтр law оставляет карточки и пункты, которые относятся к этому закону, и его строку в by_law; итоги при этом не пересчитываются. Без параметров в ответе всё, включая пройденные проверки.
Формат JSON — раздел 5.
4.5 Справочники
| Запрос | Что возвращает |
|---|---|
GET /checks |
все проверки каталога: id, category, name, law, severity, priority, automation (auto, auto+ai+review, manual), applies_to_profiles, verification |
GET /checks/{id} |
одна проверка с описанием метода, нормами и связанными делами практики |
GET /profiles |
профили организаций и какие группы проверок в них включены |
POST /fines/estimate |
расчёт диапазона штрафов по списку проверок или составов КоАП (тот же расчёт, что в отчётах) |
curl -s -X POST "$BASE/fines/estimate" -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"check_ids": ["DOC-01", "FRM-03", "CKI-02"], "org_type": "ip"}'
{
"org_type": "ip",
"items": [
{"fine_key": "13.11.3", "article": "13.11", "part": "3", "min": 10000, "max": 20000, "from_checks": ["DOC-01"]},
{"fine_key": "13.11.2", "article": "13.11", "part": "2", "min": 100000, "max": 300000, "from_checks": ["FRM-03", "CKI-02"]}
],
"total": {"min": 110000, "max": 320000},
"disclaimer": "Теоретический диапазон по составам КоАП РФ; не прогноз и не юридическое заключение."
}
Суммы в примере иллюстративные; действующие значения берутся из справочника штрафов, сверенного юристом.
4.6 База норм и практики
| Запрос | Что возвращает |
|---|---|
GET /kb/search?q=согласие на распространение&type=norm,case&limit=10 |
найденные фрагменты норм (акт, статья, часть, текст, редакция) и дела практики (орган, дата, номер, итог, достоверность A/B/C) |
GET /kb/norms/{norm_id}/{article} |
текст статьи по частям, например /kb/norms/152-FZ/9 |
GET /kb/cases/{id} |
карточка дела практики |
Право kb:read, кредиты не тратятся, лимит — 30 запросов в минуту.
5. Формат полного отчёта (JSON)
Сокращённый пример; полная схема — в справочнике /docs/api/reference.
{
"scan_id": "01927f40-…",
"site": {"url": "https://example.ru/", "domain": "example.ru", "ip_country": "RU", "hosting": "…", "tls": {"valid": true, "expires_at": "2027-01-10"}},
"profile": "shop",
"org_type": "legal",
"generated_at": "2026-09-23T14:07:12Z",
"kb_version": "нормы на 23.09.2026",
"disclaimer": "Результат автоматической проверки на дату. Не является юридическим заключением.",
"summary": {
"score": 41, "zone": "red",
"check_status": "ok", "check_failed_reason": null, "pages_checked": 34, "pages_unvisited": 0,
"counts": {"violation": 9, "risk": 4, "recommendation": 2, "manual": 7, "pass": 58, "na": 21},
"fines_total": {"min": 520000, "max": 1360000},
"fines_risk_extra": {"max": 300000},
"top_issues": ["CKI-02", "FRM-03", "LOC-01"],
"top_blocks": ["G-COOKIE-BEFORE", "FRM-03", "LOC-01"],
"rkn_bot_visible": ["CKI-02", "FRM-03"]
},
"blocks": [
{"id": "G-COOKIE-BEFORE", "group": "G-COOKIE-BEFORE", "kind": "violation",
"title": "Счётчики и реклама работают до согласия посетителя",
"check_ids": ["CKI-02", "CKI-09"], "priority": "P1", "urgency": "Срочно",
"services": ["Google Analytics", "Яндекс Метрика"]}
],
"by_law": [
{"law": "152-FZ", "title": "152-ФЗ «О персональных данных»", "violation": 6, "risk": 2, "fines": {"min": 450000, "max": 1000000}},
{"law": "ZOZPP", "title": "Закон о защите прав потребителей", "violation": 2, "risk": 1, "fines": {"min": 50000, "max": 300000}}
],
"findings": [
{
"check_id": "CKI-02",
"title": "Аналитика срабатывает до согласия на cookie",
"category": "Cookie и трекеры",
"status": "violation",
"kind": "violation",
"urgency": "Срочно",
"confidence": "technical",
"severity": 1,
"priority": "P1",
"requirement_plain": "Счётчики и трекеры нельзя запускать, пока посетитель не согласился.",
"legal": [
{"norm_id": "152-FZ", "article": "9", "part": "1", "point": null, "label": "152-ФЗ «О персональных данных», ст. 9, ч. 1", "text_excerpt": "…", "text_length": 650, "kb_ref": "152-FZ/9/1"},
{"norm_id": "KOAP-RF", "article": "13.11", "part": "2", "fine": {"min": 300000, "max": 700000}}
],
"evidence": {
"scenario": "no_action",
"page_url": "https://example.ru/",
"requests": ["https://mc.yandex.ru/watch/…", "https://www.google-analytics.com/g/collect?…"],
"screenshot_url": "/v1/scans/01927f40-…/artifacts/scr_7f2"
},
"notes": [],
"explanation": "Счётчики отправляют данные посетителя до того, как он выразил согласие.",
"practice": [{"case_id": "PR-001", "summary": "…", "outcome": "Штраф 50 000 ₽", "body": "Арбитражный суд города Москвы", "case_no": "А40-342666/25-93-2864", "date": "04.02.2026", "norm": "КоАП РФ, ст. 13.11, ч. 10", "amount_rub": 50000, "fine_replaced_by_warning": false, "source": "…"}],
"how_to_fix": ["Подключать счётчики только после нажатия «Принять»…", "Для Tilda: …"],
"developer_task": "Вынести инициализацию счётчиков в обработчик согласия…",
"effort_hours": 2,
"ai_used": false
},
{
"check_id": "DOC-12",
"title": "Перечислены cookie/аналитика/третьи лица-получатели",
"category": "Документы ПДн",
"status": "risk",
"confidence": "probable",
"severity": 2,
"priority": "P1",
"legal": [
{"norm_id": "152-FZ", "article": "9", "part": "1", "text_excerpt": "…", "kb_ref": "152-FZ/9/1"}
],
"evidence": {
"page_url": "https://example.ru/privacy",
"ai": {
"verdict": "partial",
"confidence": "medium",
"comment": "В политике названа Яндекс Метрика, но не названы Google Analytics и чат-виджет.",
"quote": "Мы используем сервис Яндекс Метрика для анализа посещаемости",
"norm_ref": "152-FZ/9/1"
}
},
"notes": ["ai_reviewed"],
"explanation": "Политика перечисляет не все сервисы, которые работают на сайте.",
"how_to_fix": ["Перечислить в политике все сервисы, которые работают на сайте."],
"ai_used": true
}
],
"manual_checks": [{"check_id": "ORG-03", "title": "…", "question": "Ведётся ли перечень систем и мест, где хранятся персональные данные?", "hint": "Это внутренний список: почта, CRM, таблицы, сайт, архивы.", "urgency": "Важно", "audience": null}],
"mismatches": [{"check_id": "DOC-15", "document": "Политика", "statement": "Мы не используем сервисы аналитики", "observed": "На сайте работает Яндекс Метрика", "conclusion": "Политика не соответствует фактической обработке"}],
"services": [{"name": "Google Analytics", "domain": "google-analytics.com", "country": "US", "foreign_entity": true, "category": "analytics", "before_consent": true}],
"cookies": {"no_action": [ … ], "accept": [ … ], "reject": [ … ]},
"cookie_no_reject": false,
"cms": ["WordPress"],
"profile_hint": {"code": "clinic", "title": "Клиника", "current_title": "Общий", "reason": "На страницах сайта много слов о лечении, врачах и пациентах."},
"plan": {"P1": ["CKI-02", "FRM-03"], "P2": [ … ], "P3": [ … ]},
"pages": [{"url": "https://example.ru/privacy", "kind": "policy", "http_status": 200}]
}
Значения полей:
| Поле | Значения |
|---|---|
status |
pass — требование выполнено; violation — нарушение; risk — спорная позиция или норма без устойчивой практики; manual — автоматически не проверить, нужен ответ человека; na — не применимо к сайту или профилю; error — проверку выполнить не удалось. Это вывод проверки; статус сверки нормы юристом на него не влияет |
kind |
Вид записи для человека (добавлено 23.10.2026): violation, risk, recommendation, manual, pass, na, error. Совпадает со status, кроме «Рекомендации»: проверка без нормы и штрафа (заголовки безопасности, флаги cookie) показывается как recommendation, хотя её status — violation или risk. «Рекомендация» не имеет штрафа и не влияет на оценку. Отчёты, собранные раньше, поля не имеют: считайте kind = status |
urgency |
«Срочно» (P1), «Важно» (P2), «Желательно» (P3 и все рекомендации); только у карточек вида violation, risk, recommendation. Срок «10 рабочих дней» — ст. 20 ч. 4 152-ФЗ (ответ оператора на запрос Роскомнадзора), «месяц» — рекомендация сервиса |
counts |
violation, risk, recommendation — число записей отчёта (blocks): смежные проверки (например, «счётчики до согласия» и «российские счётчики до согласия») слиты в одну запись, поэтому числа могут быть меньше числа карточек findings. manual — число вопросов в manual_checks (только те, что действительно нужно проверить человеку), pass, na, error — число карточек. Поле recommendation добавлено 23.10.2026 |
blocks |
Записи отчёта для человека: kind, title, check_ids (проверки, слитые в запись), priority, urgency, services (названия сервисов, на которых основан вывод). Порядок: нарушения, риски, рекомендации; внутри — по срочности |
legal[].text_excerpt |
Полный текст части нормы без обрезки (раньше — до 400 знаков). Нет текста в базе норм — null, и остаётся только ссылка (label). Если text_length больше 1500, в интерфейсе целиком его лучше не показывать, а вынести в приложение |
practice[] |
Только дела с судом (body) и номером дела (case_no) и результатом; confidence не заполняется. Нет подходящих дел — список пуст |
manual_checks[] |
Только вопросы, которые нельзя проверить с сайта и которые относятся к этому сайту; question, hint и urgency — короткий вопрос с ответом «да/нет», пояснение и срочность, если ответ «нет». Проверки, которые сервис не смог выполнить (не реализовано, сбой ИИ, недоступный реестр), в список не попадают; их карточки со статусом manual остаются в findings |
cookie_no_reject |
true — на сайте нет кнопки «Отклонить»: сценарий reject не проводится и в cookies отсутствует |
cms |
Система управления сайтом, если сканер её определил (WordPress, Tilda, 1С-Битрикс…); советы «Для Tilda/WordPress…» относятся только к ней |
profile_hint |
Подсказка профиля (с версии 1.3, поле добавлено, прежние не менялись): по признакам сканера сайт похож на другой профиль, чем выбранный. code, title — подходящий профиль, current_title — выбранный, reason — что нашёл сканер. null — подсказки нет. Профиль и набор проверок подсказка не меняет; в кратком отчёте самой подсказки нет (только нейтральная строка без названия профиля). Тексты reason берутся из данных отчёта (strings.profile_hint) |
confidence |
technical — установлено техническими средствами; probable — вероятно (в том числе по разбору текста ИИ с цитатой); manual — требует проверки человеком |
severity |
1 — критичная, 2 — высокая, 3 — средняя, 4 — низкая |
priority |
P1 — в течение 10 рабочих дней; P2 — в течение месяца; P3 — по возможности |
zone |
green (85–100), yellow (60–84), red (0–59) |
mismatches |
Расхождения «документ ↔ сайт»; заполняются, только если текст документа разбирал ИИ, иначе список пуст. check_id — проверка, из ответа которой получилось расхождение (DOC-10, DOC-12, DOC-15); document — документ; statement — дословная цитата утверждения из документа (наличие её в тексте проверяет программа); observed — что установил сканер (строку пишет программа по найденным сервисам и странам серверов); conclusion — пояснение ИИ |
notes |
Список кодов пометок карточки: почему проверка получила такой статус или чего сканер не смог. Может быть пустым. Коды и их смысл — в разделе 5.1 |
evidence.ai |
Разбор текста документа ИИ; есть только у карточек с ai_used: true. Поля и правила превращения вывода ИИ в статус — в разделе 5.1 |
Тексты, взятые с проверяемого сайта (цитаты, заголовки страниц, фрагменты документов), и тексты ИИ (evidence.ai.quote и evidence.ai.comment) передаются как есть. Если вы показываете их в своём интерфейсе, экранируйте HTML.
Скриншоты и другие файлы: GET /scans/{id}/artifacts/{artifact_id} — 302 на временную ссылку. Файлы хранятся 90 дней, JSON-отчёт — 365 дней.
5.1 Пометки notes и разбор ИИ evidence.ai
Пометки. В карточках findings, manual_checks и recommended поле notes — список кодов: почему проверка получила такой статус или чего сканер не смог. Показывайте пользователю смысл кода, а не сам код. Набор кодов пополняется: неизвестный код пропускайте, а не считайте ошибкой.
| Код | Что означает |
|---|---|
ai_reviewed |
Текст документа разобрал ИИ; цитату и ссылку на норму проверила программа, статус выставила программа |
ai_not_found |
ИИ не нашёл требуемого в тексте документа. Отсутствие цитатой не подтвердить, поэтому статус risk, а не violation |
ai_capped |
ИИ нашёл в тексте нарушение, но точность разбора ещё не проверена на наборе документов, размеченном юристом, поэтому показан risk. Проверьте цитату вручную |
ai_no_quote_needed |
Для проверок вида «лишних полей нет, данных детей нет» цитата невозможна: статус pass без цитаты |
ai_unclear, ai_low_confidence |
ИИ не смог однозначно оценить текст или остались сомнения (confidence: low); статус manual |
ai_no_quote, ai_bad_ref |
Цитата ИИ не найдена в тексте документа или ИИ сослался на норму, которой не было в запросе: вывод отброшен, статус manual |
ai_failed, ai_bad_schema |
Разбор не удался или ответ ИИ не удалось разобрать; статус manual |
ai_budget, ai_text_too_long, ai_task_missing |
Суточный лимит разбора исчерпан, документ слишком длинный для разбора или разбор для этой проверки не реализован; статус manual |
ai_unavailable |
Проверке нужен разбор текста, а ИИ выключен или недоступен; статус manual |
ai_forms_truncated, ai_text_truncated |
Форм больше, чем помещается в разбор, или длинный текст прочитан не целиком (только начало) |
partial_scan |
Сканирование было частичным: отсутствие чего-либо на сайте не доказано |
unverified_norm |
Норма ещё не сверена юристом с первоисточником: вместо violation показан risk |
cdn |
Сайт за сетью доставки контента: страна сервера может отличаться от страны IP |
no_document, document_not_loaded, short_text, script_link, no_consent_document |
Документ на сайте не найден, не открылся, очень короткий, открывается скриптом или отдельный документ согласия не найден |
no_pd_forms, no_checkout, no_publication |
Для проверки нет предмета: на сайте нет форм сбора персональных данных, оформления заказа или публикации сведений о людях; статус na |
photos_not_checked, staff_exception |
Фотографии программа не проверяет; сведения о работниках клиник и образовательных организаций публикуются в обязательном порядке (приказ № 118н, ПП № 1802) — проверьте вручную, нет ли лишнего |
requisites_mismatch, no_inn, not_found_in_egrul |
Реквизиты в документе и на сайте не совпадают, ИНН на сайте не найден или не найден в ЕГРЮЛ/ЕГРИП |
registry_unavailable, registry_disabled, individual_not_searchable |
Реестр операторов Роскомнадзора не ответил, поиск в нём отключён или оператор — физическое лицо (по ИНН не ищется) |
contract_form, consent_in_widget, scanner_does_not_fill_forms |
Форма договорная (заказ, запись, оплата); согласие во всплывающем окне, которое сканер не видит; пробной отправки формы не было |
service_country_unknown, country_unknown, location_unknown, not_found |
Страна сервиса или расположение серверов не определены; на сайте не найдено |
Разбор ИИ. У карточек, где текст документа разбирал ИИ (ai_used: true), в evidence есть блок ai:
| Поле | Значение |
|---|---|
verdict |
present — требование есть в тексте; partial — есть частично; absent — не найдено; contradicts — текст противоречит требованию; unclear — ИИ не смог оценить |
confidence |
Уверенность ИИ: high, medium, low |
comment |
Пояснение ИИ, до 300 знаков: описывает, что найдено на странице или в документе, и не содержит юридической оценки («нарушает требования» и подобных) |
quote |
Дословная цитата из документа, до 500 знаков. Есть только если программа нашла её в тексте документа |
norm_ref |
Норма в виде 152-FZ/9/1. Есть только если эта норма была в запросе к ИИ |
Статус карточки выставляет программа, а не ИИ, поэтому confidence карточки — probable. Правила:
presentс цитатой даётpass;partial—risk;absent—riskс пометкойai_not_found;contradictsдаётviolationтолько приconfidence: highи если для этой проверки разрешён такой статус; сейчас потолок статуса от ИИ —risk, поэтому приходитrisk(с пометкойai_capped, если понижен);unclear, низкая уверенность, нет цитаты или ссылка на чужую норму даютmanual. Блокaiв таких карточках показывает отброшенный вывод ИИ — не считайте его фактом.
6. Пример полного цикла
Python
import time, uuid, httpx
BASE = "https://pdn-152.ru/v1"
KEY = "ck_live_…"
client = httpx.Client(base_url=BASE, headers={"Authorization": f"Bearer {KEY}"}, timeout=30)
r = client.post("/scans",
json={"url": "https://example.ru", "profile": "shop", "org_type": "legal"},
headers={"Idempotency-Key": str(uuid.uuid4())})
r.raise_for_status()
scan_id = r.json()["id"]
while True:
s = client.get(f"/scans/{scan_id}").json()
if s["status"] in ("done", "partial", "error"):
break
time.sleep(7)
if s["status"] == "error":
print("Проверка не выполнена:", s["error"]["code"], "— кредит возвращён")
else:
report = client.get(f"/scans/{scan_id}/report", params={"status": "violation"}).json()
print("Оценка:", report["summary"]["score"])
for f in report["findings"]:
print(f["priority"], f["check_id"], f["title"])
JavaScript (Node.js 18+)
const BASE = "https://pdn-152.ru/v1";
const headers = { Authorization: `Bearer ${process.env.KEY}`, "Content-Type": "application/json" };
const created = await fetch(`${BASE}/scans`, {
method: "POST",
headers: { ...headers, "Idempotency-Key": crypto.randomUUID() },
body: JSON.stringify({ url: "https://example.ru", profile: "general" }),
}).then(r => r.json());
let scan;
do {
await new Promise(r => setTimeout(r, 7000));
scan = await fetch(`${BASE}/scans/${created.id}`, { headers }).then(r => r.json());
} while (!["done", "partial", "error"].includes(scan.status));
console.log(scan.summary);
PHP
$base = "https://pdn-152.ru/v1";
$ch = curl_init("$base/scans");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("KEY"), "Content-Type: application/json",
"Idempotency-Key: " . bin2hex(random_bytes(16))],
CURLOPT_POSTFIELDS => json_encode(["url" => "https://example.ru"]),
]);
$scan = json_decode(curl_exec($ch), true);
7. Вебхуки
Вместо опроса можно получать уведомления. Адреса настраиваются в кабинете («API и MCP» → «Вебхуки») или через API (право webhooks:manage):
| Запрос | Действие |
|---|---|
GET /webhooks |
список адресов |
POST /webhooks |
создать: {"url": "https://crm.example/hooks/152", "events": ["scan.completed", "scan.failed"]}; ответ 201, в нём один раз возвращается secret; события можно не указывать — тогда все четыре |
DELETE /webhooks/{id} |
удалить |
POST /webhooks/{id}/test |
отправить тестовое событие webhook.test (подписанное, как настоящее) и вернуть результат: delivered или failed, код ответа вашего сервера. Повторов нет |
События:
| Событие | Когда |
|---|---|
scan.completed |
проверка завершена со статусом done или partial |
scan.failed |
проверка завершена со статусом error, кредит возвращён |
credits.low |
на балансе осталось меньше 10% последнего купленного пакета или меньше 5 кредитов; приходит один раз, в момент, когда баланс опустился ниже порога |
credits.expiring |
пакет кредитов истекает не позже чем через 14 дней; один раз на пакет |
Тело запроса:
{
"id": "evt_01927f44…",
"type": "scan.completed",
"created_at": "2026-09-23T14:07:13Z",
"data": {"scan_id": "01927f40-…", "status": "done", "external_id": "client-1842", "summary": {"score": 41, "zone": "red", "check_status": "ok", "violations": 9}}
}
Отчёт в вебхук не вкладывается — получите его запросом GET /scans/{id}/report.
Подпись. Заголовок X-Signature: t=1790172433,v1=<hex>, где v1 = HMAC-SHA256(secret, "<t>.<тело запроса как есть>"). Проверяйте подпись и отклоняйте запросы с t старше 5 минут:
import hmac, hashlib, time
def verify(secret: str, header: str, body: bytes) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
if abs(time.time() - int(parts["t"])) > 300:
return False
expected = hmac.new(secret.encode(), f'{parts["t"]}.'.encode() + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts["v1"])
Доставка: ответ 2xx в течение 10 секунд считается успешным. Иначе повторы через 1 минуту, 5 минут, 30 минут, 2 часа, 6 часов, 24 часа. После 20 неудачных попыток доставки подряд (по всем событиям адреса) адрес отключается, владелец получает письмо. События по демонстрационным проверкам тестового ключа приходят с полем data.test: true. Возможна повторная доставка одного события — используйте id для защиты от дублей. Адреса вебхуков должны быть публичными (https, не внутренние сети).
8. Безопасность и рекомендации
- Храните ключ в переменных окружения или хранилище секретов, не в коде и не в браузере. API не предназначен для вызова из браузера: запросы с сайтов (CORS) не разрешены.
- Выпускайте отдельный ключ на каждую систему и ограничивайте права: для чтения отчётов достаточно
scans:read. - Ограничьте ключ списком IP-адресов ваших серверов.
- При подозрении на утечку отзовите ключ в кабинете — он перестанет работать сразу. При первом использовании ключа с нового IP владелец получает письмо.
- Проверяйте только сайты, которые вы вправе проверять (свои или клиентов с их согласия). Сервис открывает только публичные страницы и никогда не отправляет формы.
9. История изменений
| Дата | Версия | Изменения |
|---|---|---|
| — | v1 | первая публичная версия |
| 09.10.2026 | документ 1.2 | описаны поле notes с кодами пометок и блок evidence.ai (раздел 5.1); в пример полного отчёта добавлена карточка с разбором ИИ; окно reuse_recent — 4 часа вместо 6 |
10. Поддержка
Вопросы по API — через форму в кабинете. Указывайте request_id из ответа об ошибке.