Справочник Site API (seoasist-site-api/1)
Полная спецификация SEOAsist Site API: аутентификация и HMAC-подпись, четыре эндпоинта, типы правил и обзор готовых примеров для Next.js и PHP.
Открыть этот экран в панелиНа этой странице описан контракт SEOAsist Site API (спецификация seoasist-site-api/1), который реализует ваш сайт. Вкладка Руководство разработчика → Спецификация в панели и Подсказка по настройке содержат тот же контракт. Пошаговое руководство по подключению см. в статье Подключение самописного сайта с помощью SEOAsist Site API.
Общие правила
- Все эндпоинты находятся по одному базовому пути, например,
https://example.com/api/seoasist. - Всегда возвращайте ответ в формате JSON. Ответ не в формате JSON считается ошибкой.
- Никогда не перенаправляйте запросы (без кодов 30x). SEOAsist не следует по перенаправлениям, поэтому сохраняйте в панели итоговый URL API.
- В случае ошибки возвращайте 4xx/5xx с
{"error":"<короткое сообщение>"}. Это сообщение отображается в панели. - Проверяйте входные данные, ограничивайте размер тела запроса (~2 МБ) и логируйте вызовы.
- SEOAsist отменяет запрос по таймауту через 25 секунд. Запросы используют User-Agent
SEOAsist-SiteAPI/1.0 (+https://seoasist.com). - URL должен быть публично доступен; SEOAsist никогда не вызывает localhost или адреса private-сетей.
Аутентификация
Каждый запрос содержит три заголовка:
| Заголовок | Значение |
|---|---|
Authorization | Bearer <SEOASIST_SITE_TOKEN> |
X-SEOAsist-Timestamp | Unix timestamp (в секундах) |
X-SEOAsist-Signature | sha256=<hex>, где hex = HMAC-SHA256(ключ = токен, сообщение = <timestamp>.<raw body>) |
- Храните токен в переменной окружения
SEOASIST_SITE_TOKEN; никогда не прописывайте его жестко в коде. Он генерируется в панели при нажатии Подключить и начинается сsa_. - Сравнивайте токен и подпись за постоянное время (constant time).
- Для GET-запросов сырое тело (raw body) представляет собой пустую строку, поэтому подписываемое сообщение выглядит как
<timestamp>.. - Возвращайте
401 {"error":"unauthorized"}, если подпись неверна или timestamp старше 300 секунд.
const expected = "sha256=" + createHmac("sha256", token).update(`${ts}.${body}`).digest("hex");
GET /manifest
Этот эндпоинт вызывается при проверке подключения. Указывайте только те возможности, которые вы реализовали.
{"spec":"seoasist-site-api/1","platform":"nextjs","name":"My site","capabilities":["rules","content","files"]}
spec должен начинаться с seoasist-site-api/. Распознаваемые возможности (capabilities): rules, content и files; всё остальное игнорируется. В панели они отображаются как SEO-правила, Публикация контента и llms.txt / robots.
Есть еще два необязательных поля:
locales: языки, на которых вы можете публиковать контент (ISO 639-1), например,["az","ru","en","tr"]. SEOAsist отправляет статью на языке, отличном от основного языка сайта, только если этот язык указан здесь. Перед отклонением статьи из-за несоответствия языка манифест считывается еще раз.ruleKinds: объявляет поддержку опциональных типов правил. Единственный опциональный тип на сегодняшний день —link; при"ruleKinds":["link"]SEOAsist отправляет внутренние ссылки в ваш Site API вместо Pixel.
{"spec":"seoasist-site-api/1","platform":"nextjs","name":"My site","capabilities":["rules","content","files"],"locales":["tr","en"],"ruleKinds":["link"]}
PUT /rules (возможность: rules)
Тело запроса: {"rules": Rule[]}. Это полный текущий набор правил; замените всё, что было сохранено ранее. Пустой массив очищает все правила. Сохраняйте правила (в таблице БД или JSON-файле) и применяйте их на стороне сервера при рендеринге, чтобы поисковые роботы видели результат в HTML, а не в клиентском JavaScript.
type Rule = { id: string; kind: string; matchType: "exact" | "prefix" | "regex" | "all"; matchPath: string; payload: object };
Сопоставление использует путь запроса (pathname) без завершающего слэша (/ для главной страницы):
exact: путь полностью совпадаетprefix: путь начинается сmatchPathregex: проверка с помощью JavaScript RegExpall: каждая страница
Если совпадают несколько правил одного типа, побеждает наиболее специфичное: exact > prefix > regex > all.
| kind | payload | Как применять |
|---|---|---|
title | {"value"} | <title> |
meta_description | {"value"} | <meta name="description"> |
canonical | {"href"} | <link rel="canonical"> (абсолютный URL) |
robots | {"content"} | <meta name="robots"> (например, index,follow, noindex,follow) |
h1 | {"value","selector"?} | Заменить текст главного H1 страницы |
og | {"title"?,"description"?,"image"?,"type"?} | Метатеги og:* |
json_ld | {"json"} | <script type="application/ld+json"> (json — это объект или массив) |
hreflang | {"items":[{"lang","href"}]} | <link rel="alternate" hreflang> |
redirect | {"to","status"?} | HTTP-перенаправление перед рендерингом (по умолчанию 301) |
img_alt | {"items":[{"match","alt"}]} | Установить alt для элементов <img>, чьей src содержит match |
lang | {"value"} | <html lang> |
link | {"anchor","href"} | Внутренняя ссылка: обернуть первое вхождение anchor в основном тексте страницы (не внутри ссылки, заголовка, кода, nav/header/footer) в <a href="href">; ничего не делать, если страница уже ссылается на href. Применяется каждое совпадающее правило link, а не только самое специфичное. Отправляется только если ruleKinds в манифесте содержит link |
meta | {"name","content"} | <meta name="{name}" content="{content}"> в <head>. name используется только как тег подтверждения владения сайтом: msvalidate.01 (Bing), google-site-verification, yandex-verification (обычно matchPath /) |
Сохраняйте неизвестные типы и игнорируйте их. Ответ: {"ok":true,"count":<количество сохраненных>}.
SEOAsist отправляет все включенные правила сайта: добавленные на вкладке Pixel, примененные мета-предложения, правила, созданные задачами тарифных планов Autopilot (Otopilot), утвержденные внутренние ссылки и правила, сгенерированные функциями Авто-исправление / Исправить с помощью правил. Подключенный Site API с возможностью rules используется в качестве канала правил сайта.
POST /content (возможность: content)
Тело запроса:
{"externalId":null,"type":"post","title":"…","slug":"…","html":"<h2>…</h2>","excerpt":"…","metaTitle":"…","metaDescription":"…","status":"draft","featuredImageUrl":"https://…","tags":["…"],"updateUrl":null,"locale":"tr","translationOf":null}
- Указан
externalId: обновить этот элемент. - Указан
updateUrl(type: "page"): обновить существующую страницу по этому URL (ее основной контент, заголовок и метаданные). - В противном случае: создать новую статью в блоге, используя существующую систему контента сайта (DB/CMS/MDX) и ее стандартный шаблон URL.
status: "draft"не должен быть публично виден.html— это доверенный, уже отформатированный HTML статьи (заголовки, списки, таблицы, FAQ). Сохраняйте дизайн и верстку сайта.tagsсодержит не более 5 тегов.locale: язык текста (ISO 639-1, например,az,tr). На мультиязычном сайте публикуйте статью под собственным URL этого языка (языковой префикс / маршрут), никогда не под другим языком.translationOf: если статья является переводом, этоid, который вы вернули ранее для оригинальной статьи. Сохраните её как языковую версию этой статьи (переключатель языков должен открывать её, а не ошибку 404) и свяжите версии с помощью<link rel="alternate" hreflang>на каждой из них. Оригинал всегда отправляется первым; когда владелец повторно публикует материал, каждая версия отправляется снова со своим собственнымexternalId.featuredImageUrl: абсолютный URL изображения статьи, одинаковый для каждой языковой версии. При создании и при каждом обновлении, если он отличается от сохраненного, скачайте его и замените изображение статьи (URL может не иметь расширения файла; используйте Content-Type из ответа).- Для статей в блоге
htmlможет содержать структурированные данные статьи (например, BlogPosting и FAQPage) в конце в виде<script type="application/ld+json">. Не удаляйте их; публикуйте на странице как есть.
Ответ: {"id":"<постоянный id>","url":"<абсолютный публичный URL>","status":"draft"|"publish"}. SEOAsist сохраняет id и отправляет его обратно в качестве externalId при повторной отправке того же элемента. Если статья блога без externalId совпадает со статьей, опубликованной ранее на том же сайте с тем же slug и языком, SEOAsist использует id этой статьи, чтобы на сайте не открывалась вторая копия.
PUT /files (возможность: files)
Тело запроса: {"path":"llms.txt"|"robots-ai.txt","content":string}
llms.txt: отдавайте его по адресу/llms.txtкакtext/plain(заменяя предыдущий).robots-ai.txt: блок правил для ИИ-краулеров. Отдавайте/robots.txtс собственными правилами сайта плюс добавленный этот блок (заменяя предыдущий блок).
Ответ: {"ok":true,"url":"<публичный url>"}.
Готовые примеры
Диалоговое окно Руководство разработчика содержит код для копирования и вставки:
| Вкладка | Содержимое |
|---|---|
| Next.js | lib/seoasist.ts (хранилище на базе JSON-файла и seoRules(pathname), выбирающая наиболее специфичное правило для каждого типа), app/api/seoasist/[...path]/route.ts (четыре эндпоинта с проверкой HMAC) и пример использования, который применяет правила в generateMetadata и отдаёт /llms.txt. |
| PHP | seoasist-api.php: правило перенаправления .htaccess для /api/seoasist/*, хранилище data/seoasist.json и проверки подписи с помощью hash_hmac. |
| Тест | Подписанная команда curl для /manifest. |
Эндпоинт /content в примерах представляет собой заглушку (TODO); вам необходимо сохранить статью в свой собственный блог/CMS и вернуть ее реальный URL.
Тестовая команда:
TOKEN="<SEOASIST_SITE_TOKEN>"
TS=$(date +%s)
SIG=$(printf "%s." "$TS" | openssl dgst -sha256 -hmac "$TOKEN" | sed 's/^.* //')
curl -s "https://example.com/api/seoasist/manifest" \
-H "Authorization: Bearer $TOKEN" \
-H "X-SEOAsist-Timestamp: $TS" \
-H "X-SEOAsist-Signature: sha256=$SIG"
Apache может не передавать заголовок Authorization в PHP. В таком случае добавьте SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 в .htaccess.
Сообщения об ошибках, отображаемые в панели
| Сообщение | Значение |
|---|---|
token rejected | Сайт ответил 401/403 без поля error в теле ответа. |
endpoint not found (is the Site API installed at this URL?) | 404; неверный URL или API не установлен. |
redirect to … (use the final API URL) | Сайт вернул перенаправление. |
non-JSON response | Ответ был не в формате JSON. |
timeout / unreachable: … | Нет ответа в течение 25 секунд или сбой подключения. |
unexpected manifest (spec "…") | /manifest не вернул валидный spec. |
Если ваш сайт возвращает {"error":"…"} в теле ответа, панель отображает это сообщение как есть. Решения проблем см. в статье Проблемы с подключением Site API.
Похожие статьи
- Подключение самописного сайта с помощью SEOAsist Site APIДля сайтов не на WordPress или Shopify: установите и подключите Site API, чтобы исправления SEO, статьи в блог и файл llms.txt применялись на вашем сайте автоматически на стороне сервера.
- Сбой проверки подключения Site APIНайдите и устраните причину ошибки «Test failed»: токен, подпись, неверный URL, редиректы, таймауты или сброс заголовка Authorization в Apache.
- REST API v1: ключи API, аутентификация и эндпоинтыКак создать ключ API, передавать заголовок Authorization, лимиты запросов и полный список эндпоинтов v1 с параметрами, форматом ответа и кодами ошибок.