▤ Руководства

API: руководство (версия 126)

Содержание

Обзор правил, общих для всех методов. Описание каждого метода с примерами — в справочнике «API_REFERENCE»; машиночитаемое — GET /api/v1/openapi.json; возможности для программ — GET /api/v1/capabilities (оба без ключа).

Быстрый старт

  1. Кабинет → «API и вебхуки» → «Ключи» → создайте ключ с правом read. Он показывается один раз.
  2. Проверьте доступ: curl -H "X-Api-Key: smk_…" https://seo.example/api/v1/me.
  3. Добавьте сайт (write): curl -X POST -H "X-Api-Key: …" -H "Content-Type: application/json" -d '{"host":"example.com"}' https://seo.example/api/v1/sites.
  4. Запустите проверку (run): POST /analyze — вернётся задание (код 202), ход смотрите в GET /jobs/{id}.
  5. Прочитайте итог: GET /overview, GET /issues/{host}, GET /plan, GET /changes/{host}.

Ключи и права

Ключей может быть несколько; у каждого:

  • права: read (чтение), run (запуск проверок — тяжёлые запросы), write (сайты, задачи, профиль, вебхуки);
  • срок действия (бессрочный, 30/90 дней, год) и отзыв в любой момент;
  • разрешённые адреса — IPv4/IPv6 и сети (203.0.113.0/24); пусто — любые;
  • свой лимит запросов в час (по умолчанию 120, SEOMASTER_API_RATE).

Ключ показывается один раз; в базе только SHA-256. Передавайте его в X-Api-Key или Authorization: Bearer; в адресе нельзя. Для CLI и MCP-сервера заведите отдельный ключ с минимальным набором прав: так проще отозвать и видно в журнале.

Запросы и ответы

Всё в JSON (UTF-8). Тело запросов — Content-Type: application/json. Имена полей стабильны: новые поля могут добавляться, существующие не меняют смысл и тип, поэтому клиент должен игнорировать незнакомые поля. Ошибка всегда одного вида: {"error": "текст", "code": "код"} (в том числе при сбое сервера и обслуживании). Текст предназначен людям и может меняться, программе нужен code.

Каждый ответ содержит X-Request-Id (укажите его в обращении в поддержку), X-API-Version и заголовки лимита.

Ошибки

Код в поле code стабилен; HTTP-статус и признак «повторять можно» ниже.

КодHTTPПовторЗначение
missing401нетНе передан ключ. Добавьте заголовок X-Api-Key или Authorization: Bearer.
invalid401нетКлюч не найден. Проверьте, что он скопирован целиком.
revoked401нетКлюч отозван. Выпустите новый в кабинете.
expired401нетСрок действия ключа истёк. Выпустите новый.
ip403нетЗапрос с адреса, которого нет в списке разрешённых для ключа.
blocked403нетАккаунт недоступен.
closed503даСервис закрыт на тестирование: доступ только для администратора.
scope403нетУ ключа нет нужного права (read, run или write).
limit429даИсчерпан лимит запросов ключа или лимит тарифа. Повторите после паузы из заголовка Retry-After.
not_found404нетСайт, задача, задание или данные не найдены (или принадлежат другому аккаунту).
not_ready409даДанных ещё нет: проверка не выполнялась или не завершена. Запустите проверку или дождитесь задания.
method405нетДля этого адреса такой HTTP-метод не поддерживается.
bad_request400нетЗапрос составлен неверно: смотрите текст ошибки.
bad_host400нетНе удалось распознать домен.
bad_status400нетНедопустимое значение статуса.
bad_param400нетОдин из параметров запроса имеет недопустимое значение.
unsupported422нетОперация для этого объекта невозможна (например, задачу этого типа нельзя перепроверить автоматически).
idem_conflict422нетIdempotency-Key уже использован с другим запросом. Возьмите новый ключ.
idem_busy409даЗапрос с этим Idempotency-Key ещё выполняется. Повторите через секунду.
tool_off503нетУтилита отключена администратором. Выберите другую из каталога или повторите позже.
service_off503нетВнешний сервис, на котором работает раздел, отключён администратором для всех пользователей. Используйте другие данные или повторите позже.
tool_failed422нетУтилита не смогла обработать эти данные или адрес (текст ошибки — в error): исправьте ввод.
maintenance503даСервис на обслуживании. Повторите позже (Retry-After).
server500даВнутренняя ошибка. Повторите позже и сообщите X-Request-Id, если повторяется.

Правило для клиента: повторять только если «Повтор — да», с паузой из Retry-After (если нет — 1, 2, 4… секунд, не более 5 попыток); остальные ошибки исправляйте в запросе.

Лимиты и заголовки

X-RateLimit-Limit и X-RateLimit-Remaining — лимит ключа в час и остаток; при исчерпании — 429 limit и Retry-After. Запуск проверок дополнительно ограничен тарифом (дневное число проверок и повторных проверок задач); обход сайта через API считается как три проверки. Остаток по ключу — GET /usage.

Постраничность

Списки принимают limit (по умолчанию 50, максимум 200) и offset. Ответ содержит объект page: limit, offset, total, next_offset (null — страниц больше нет). Листайте, пока next_offset не станет null.

Условные запросы (ETag)

Ответы на GET содержат ETag. Повторите запрос с If-None-Match: <значение> — если данные не изменились, придёт 304 без тела и лимит не потратится впустую. Удобно для опроса GET /overview, GET /plan и GET /events.

Идемпотентность

Для POST, PUT, PATCH, DELETE передайте заголовок Idempotency-Key (1–64 символа: буквы, цифры, . _ : -). Повтор с тем же ключом и тем же запросом в течение суток вернёт сохранённый ответ с заголовком Idempotent-Replay: true и ничего не выполнит второй раз — безопасно при обрыве связи. Тот же ключ с другим запросом — idem_conflict; пока первый запрос выполняется — idem_busy. Ответы 5xx не запоминаются. Секрет вебхука в сохранённый ответ не попадает: при повторе вместо него пояснение, создайте секрет заново через POST /webhooks/{id}/rotate-secret.

Фоновые задания

Долгие операции (POST /analyze, POST /crawl/{host}) отвечают 202 и объектом задания. Опрашивайте GET /jobs/{id}: состояние queued, running, done или error; пока задание идёт, поле poll_after_s подсказывает паузу (3 секунды); по завершении — ссылки на результат. GET /jobs — последние задания. Задания видны только владельцу ключа.

Вебхуки

Несколько адресов на пользователя; для каждого — набор событий (events, uptime, watchdog, page, crawl, rank, security, digest), фильтр по сайтам (поддомены подходят), секрет. Через API: GET|POST /webhooks, DELETE /webhooks/{id}, POST /webhooks/{id}/rotate-secret, POST /webhooks/{id}/test, GET /webhooks/{id}/deliveries. Лимит адресов — по тарифу (webhooks), ключей — api_keys.

Тело — исходное уведомление плюс event, delivery, sent_at. Заголовки: X-SeoMaster-Event, X-SeoMaster-Delivery, X-SeoMaster-Timestamp, X-SeoMaster-Signature-V2: t=<время>,v1=<HMAC-SHA256("время.тело", секрет)>; прежний X-SeoMaster-Signature: sha256=<HMAC от тела> сохранён. Получатель проверяет подпись и отбрасывает запросы старше 5 минут.

Проверка подписи (PHP):

[$t, $v1] = array_map(fn($p) => explode('=', $p, 2)[1], explode(',', $_SERVER['HTTP_X_SEOMASTER_SIGNATURE_V2']));
$ok = abs(time() - (int)$t) < 300 && hash_equals(hash_hmac('sha256', $t . '.' . file_get_contents('php://input'), $secret), $v1);

Доставка: ответ 2xx за 8 секунд. Иначе повторы через 1 мин, 5 мин, 30 мин, 2 ч, 12 ч; после шестой неудачи доставка «потеряна» (ошибки 4xx, кроме 408/409/425/429, — после третьей), её можно отправить вручную. 15 неудач подряд отключают адрес. Адреса проверяются на SSRF при каждой отправке. Первая попытка — сразу, повторы — bin/webhooks.php (cron каждую минуту).

Версии и совместимость

Версия контракта — в info.version схемы OpenAPI, в GET /capabilities и в заголовке X-API-Version (сейчас 1.3; номер версии сервиса — отдельно, x-service-version). Внутри /api/v1 изменения только добавляющие: новые методы, поля и необязательные параметры. Несовместимые изменения выйдут под /api/v2, а /api/v1 будет работать не меньше 12 месяцев после объявления. Ошибки определяйте по code, а не по тексту.

Для CLI и MCP-сервера

API проектировалось так, чтобы командная строка и MCP-сервер были тонкой оболочкой над ним:

  • единые имена: у каждого метода есть команда CLI (x-cli, например seomaster site issues) и инструмент MCP (x-mcp, например get_top_issues); они указаны в справочнике, в OpenAPI и в GET /capabilities;
  • самоописание: GET /capabilities без ключа отдаёт версию, права, лимиты, соглашения, каталог ошибок, события вебхуков и список методов — CLI и MCP строят по нему справку и схемы инструментов, не храня копию;
  • признаки операций: x-readonly (только чтение — MCP readOnlyHint), x-async (возвращает задание), право x-scope; методы удаления помечаются как разрушающие, а PUT/DELETE — идемпотентные;
  • безопасное выполнение: у каждого изменяющего вызова есть Idempotency-Key; опасные правки профиля требуют явного confirm; долгие операции — задания с опросом;
  • единая форма ошибок и стабильные коды: CLI возвращает код выхода (0 — успех, 1 — ошибка запроса, 2 — ключ или права, 3 — лимит или недоступность, 4 — сеть), MCP — сообщение с code и признаком повтора;
  • настройка: адрес сервиса и ключ — переменные SEOMASTER_URL и SEOMASTER_KEY (или файл настроек); ключ не печатается в журнал.

MCP-сервер работает с версии 126: адрес /mcp, инструменты строятся из этого же реестра методов, описание — в руководстве «MCP». CLI seomaster работает с версии 127 (bin/seomaster, руководство «CLI» — docs/CLI.md): команды строятся по GET /capabilities; имена команд и инструментов закреплены и не будут переименованы.

Что нового

  • 129: вход OAuth 2.1 для MCP (/oauth/register, /oauth/authorize, /oauth/token, /oauth/revoke, метаданные /.well-known/oauth-protected-resource и /.well-known/oauth-authorization-server, обязательный PKCE). Токен доступа — обычный ключ smk_… с выбранными правами, работает и в REST API; контракт не менялся.
  • 128: контракт 1.3. Утилиты сервиса (86 штук) как методы: GET /utilities, GET /utilities/{id}, POST /utilities/{id} (команды seomaster utils …, инструменты MCP list_utilities, get_utility, run_utility); сводки GET /today и GET /incidents. Коды ошибок tool_off и tool_failed. MCP: 31 готовый сценарий вместо 7. CLI: вывод блоков утилит, ввод из файла и стандартного ввода.
  • 127: CLI seomaster. В GET /capabilities у каждого метода добавлены params и body (параметры и тело запроса); остальное без изменений.
  • 126: контракт 1.2. Новые методы данных: GET /performance/{host}, /queries/{host}, /opportunities/{host}, /positions/{host}, /backlinks/{host}, /effects/{host}, /work/{host}. MCP-сервер POST /mcp. Прежние методы чтения вынесены из маршрута в класс (поведение не изменилось).
  • 125: контракт 1.1. Методы: GET /overview, /plan, /issues/{host}, /changes/{host} (новые, исправленные и вернувшиеся ошибки), /indexing/{host}, /market/{host}, профиль сайта (GET|PUT|DELETE /profile/{host}), GET /explain, POST /tasks/{id}/recheck, POST /analyze и POST /crawl/{host} (задания), GET /jobs, POST /webhooks, DELETE /webhooks/{id}, POST /webhooks/{id}/rotate-secret, GET /capabilities. Общее: каталог ошибок, постраничность, Idempotency-Key, ETag, X-API-Version, JSON-ошибки при сбое и обслуживании, имена CLI и MCP.
  • 117: data_state, data_confidence, data_age, data_reason в GET /providers.
  • 64–72: диагноз, динамика, трафик, конкуренты, соцсети, JS, реестр провайдеров; ключи с правами, лимит, журнал, вебхуки с подписью V2.

Ограничения

Поля plain и guide в GET /explain — на русском (подсказка hint на двух языках, параметр lang). Отчёты, история и обход по домену общие для всех пользователей и читаются любым ключом; обзор, план, индексация, рынок, профиль и задания — только владельца. GET /market/{host} требует подключённых данных Ahrefs или Semrush. POST /analyze и POST /crawl/{host} выполняются воркером (bin/worker.php) либо при обращении; ход — в GET /jobs/{id}. Перепроверка задач ограничена суточным лимитом тарифа.