IndexNow и API Яндекс Вебмастера: автоматизируем индексацию
Что такое IndexNow?
IndexNow - открытый протокол, по которому сайт сам сообщает поисковым системам о новых, изменённых и удалённых URL одним HTTP-запросом с ключом проверки владельца. Его поддерживают Bing, Яндекс, Naver, Seznam.cz и Yep; Google в протоколе не участвует. IndexNow ускоряет обнаружение изменений, но индексацию не гарантирует.
TL;DR
- -IndexNow принимают Bing, Яндекс, Naver, Seznam.cz и Yep; Google протокол не поддерживает, для него остаются sitemap с точным lastmod и Search Console.
- -Для Яндекса есть второй канал: переобход через API Вебмастера (POST recrawl/queue). Квота суточная и своя у каждого сайта: у futurecraft.pro она 150 URL в день, у другого сайта может отличаться.
- -Отправлять стоит только изменённые URL. Разность git между прошлым и новым продовым коммитом даёт точный список: для коммита с одной статьёй на двух языках это ровно 2 URL.
- -Код 200 или 202 от IndexNow значит только «запрос принят». Получение URL проверяют в Bing Webmaster Tools, а у переобхода Яндекса есть статус задачи: IN_PROGRESS, DONE или FAILED.
- -Прежний CI-шаг этого блога отправлял первые 100 адресов из sitemap: ни одной русской страницы и не обязательно новую статью. Исправленный шаг берёт URL из git diff и подсвечивает ошибку ответа, не ломая деплой.
После деплоя статического сайта поисковики узнают о новой странице, когда робот снова обойдёт sitemap или найдёт на неё ссылку. Ускорить это можно двумя способами. IndexNow - открытый протокол, по которому сайт одним HTTP-запросом с ключом владельца сообщает поисковым системам о новых, изменённых и удалённых URL. Его поддерживают Bing, Яндекс, Naver, Seznam.cz и Yep; Google в протоколе не участвует, и индексацию IndexNow не гарантирует.
Второй способ работает только для Яндекса: переобход через API Яндекс Вебмастера. Он ставит конкретный URL в приоритетную очередь робота, но в пределах суточной квоты. У futurecraft.pro она 150 URL в день; у вашего сайта может быть другой, поэтому её читают через API, а не берут из статей.
Статья о том, как собрать из этого пайплайн после деплоя: что куда отправлять, скрипт, который берёт изменённые URL из git, шаг в GitHub Actions, переобход через API Вебмастера и проверка результата. Весь код взят из CI этого блога, включая разбор собственной ошибки: до ноября 2026 года CI отправлял в IndexNow не изменённые страницы, а первые 100 адресов из sitemap.
Какие каналы существуют и что куда отправлять
Каналов шесть, и документированы не все. В таблице состояние на ноябрь 2026 года.
| Канал | Кто получает | Что передаёт | Лимит | Документирован |
|---|---|---|---|---|
IndexNow (api.indexnow.org или endpoint поисковика) | Bing, Яндекс, Naver, Seznam.cz, Yep | новые, изменённые и удалённые URL | до 10 000 URL в запросе; суточные пороги у каждого поисковика свои | да |
| API Вебмастера: переобход страниц | Яндекс | один URL за запрос | суточная квота из recrawl/quota (у нас 150) | да |
| API Вебмастера: приоритетный переобход Sitemap | Яндекс | файл sitemap целиком | 10 раз в месяц | да, API v4.1 |
Sitemap с lastmod | все поисковики | полный список URL | - | да |
| Обход по счётчику Метрики | Яндекс | страницы, которые посещают пользователи | - | да |
| PubSubHubbub (WebSub) | хаб pubsubhubbub.appspot.com | RSS- и Atom-ленты | - | хаб работает; влияние на поиск Google не описано |
Два механизма в таблицу не вошли. Пинг sitemap Google отключён, подробности в разделе про Google. Пинг Яндекса webmaster.yandex.ru/ping?sitemap=... отвечает HTTP 200, но ни в справке, ни в документации API его нет, и эффект ничем не подтверждён. Рекомендовать его нельзя, хотя наш CI его по-прежнему вызывает.
Рабочая схема для статического сайта: sitemap с точным lastmod как источник правды, IndexNow с изменёнными URL после каждого деплоя, переобход через API Вебмастера для новых статей. Яндекс в справке о новых и изменённых страницах сам советует совмещать способы.
IndexNow: как устроен протокол
Ключ и файл проверки
Ключ подтверждает, что запрос относится к вашему сайту. По требованиям Яндекса это строка от 8 до 128 символов из a-z, A-Z, 0-9 и -. Файл <KEY>.txt с ключом внутри, в кодировке UTF-8, кладут в корень сайта. Если файл лежит в другом каталоге, его адрес передают параметром keyLocation, и тогда ключ покрывает только URL из этого каталога.
KEY=$(openssl rand -hex 16) # 32 символа: 0-9 и a-f
printf '%s' "$KEY" > "public/${KEY}.txt" # Astro и Vite копируют public/ в корень сайта
# после деплоя: ожидаем 200 и text/plain
curl -s -o /dev/null -w '%{http_code} %{content_type}\n' "https://example.com/${KEY}.txt"
Яндекс сверяет ключ при каждом запросе. Файл публичный по устройству протокола, так что ключ подтверждает связь с сайтом, а не аутентифицирует отправителя. Для смены ключа нужен новый файл и новый ключ в запросах.
Запрос и коды ответа
Один URL отправляют GET-запросом, URL кодируют по RFC 3986:
curl -s -w '\nHTTP %{http_code}\n' \
"https://api.indexnow.org/indexnow?url=https%3A%2F%2Fexample.com%2Fblog%2Fpost%2F&key=<KEY>"
Пакет уходит POST-запросом с JSON, до 10 000 URL за раз (документация протокола):
{
"host": "example.com",
"key": "<KEY>",
"keyLocation": "https://example.com/<KEY>.txt",
"urlList": [
"https://example.com/blog/post/",
"https://example.com/ru/blog/post/"
]
}
Заголовок запроса: Content-Type: application/json; charset=utf-8. Коды ответа по документации протокола и справочнику Яндекса:
| Код | Значение | Что делать |
|---|---|---|
| 200 | URL получены | ничего; это факт приёма, не индексации |
| 202 | приняты, ключ ещё проверяется | для нового ключа подождать; у Яндекса постоянный 202 значит, что файл ключа не найден |
| 400 | неверный формат запроса | проверить JSON и кодирование URL |
| 403 | ключ не найден или не совпадает с файлом | открыть <KEY>.txt по HTTPS |
| 405 | метод не поддерживается (у Яндекса) | использовать GET для одного URL или POST для пакета |
| 422 | URL не принадлежат host, ошибка в keyLocation, больше 10 000 URL | проверить хост и список |
| 429 | слишком много запросов | сократить частоту; у Яндекса лимит считается по IP |
Успех для скрипта - 200 и 202. Всё остальное должно выглядеть как ошибка, иначе отказ в приёме никто не заметит.
Шлюзы и интервал повторной отправки
У каждого участника свой endpoint, плюс общий https://api.indexnow.org/indexnow. По FAQ indexnow.org отправка на любой из них передаётся всем участникам, а решение об индексации каждый поисковик принимает сам. Яндекс принимает запросы на https://yandex.com/indexnow, но в своей справке передачу другим участникам прямо не описывает.
С интервалами источники расходятся. indexnow.org советует ждать не меньше 5 минут перед повторной отправкой того же URL, а после ответа 429 - не меньше 10 минут. Яндекс рекомендует паузу 10 минут. Безопаснее ориентироваться на 10: при обычном деплое один и тот же URL всё равно не отправляется чаще.
Что отправлять и когда
Отправлять нужно новые, изменённые и удалённые страницы, и ничего больше. На вопрос «Можно ли отправить URL всех страниц сайта?» справка Яндекса отвечает: только новые, изменившиеся или удалённые, а для учёта всех изменений есть sitemap и обход по счётчику Метрики. Bing добавляет, что каждый обход расходует квоту обхода сайта.
Удалённые страницы отправлять можно и полезно. URL с ответом 404 или 410 и адреса с редиректом 301 или 302 протокол принимает, так поисковик быстрее уберёт устаревшее из выдачи.
Для статического сайта «изменённая страница» вычисляется из git. Если в коммите поменялся файл статьи, изменилась её страница. Если поменялся шаблон, компонент или конфиг сборки, изменилась разметка всех страниц. Отправлять весь sitemap из-за правки футера - компромисс: indexnow.org просит не сообщать о косметических изменениях, а скрипт не умеет отличить косметику от смысла. В нашем скрипте выбран осторожный вариант: шаблон поменялся, значит, отправляем всё.
Скрипт: отправка изменённых URL из git
scripts/notify-indexnow.mjs в репозитории блога работает без зависимостей, на Node.js 20. Он принимает --base-ref=<git ref> (с какого коммита считать изменения) и --dry-run (напечатать список, ничего не отправляя). Сначала скрипт берёт список изменённых файлов и превращает пути статей в URL:
function changedFiles() {
git(['rev-parse', '--verify', baseRef]);
const output = git(['diff', '--name-only', `${baseRef}...HEAD`]);
return output ? output.split('\n').filter(Boolean) : [];
}
function articleUrl(file) {
const match = file.match(/^src\/content\/blog\/(en|ru)\/(.+)\.mdx?$/);
if (!match) return null;
const [, locale, slug] = match;
const localePrefix = locale === 'ru' ? '/ru' : '';
return `${siteOrigin}${localePrefix}/blog/${slug}/`;
}
git() здесь - обёртка над execFileSync('git', args). Аргументы уходят массивом, без shell, а baseRef, начинающийся с - или содержащий пробелы, скрипт отклоняет до вызова git.
Дальше два правила расширяют список. Изменения в общих файлах добавляют все URL из собранного sitemap в dist/. Изменения данных витрины качества (страницы с метриками проектов, их данные CI обновляет автоматически) добавляют только страницы, которые эти данные рендерят:
const dataPages = {
'src/data/quality/': ['/engineering/', '/ru/engineering/', '/kronos/', '/ru/kronos/'],
};
function affectsEveryPage(file) {
return [
'astro.config.mjs', 'src/components/', 'src/content/config.',
'src/data/categories.', 'src/i18n/', 'src/layouts/', 'src/pages/',
].some((prefix) => file === prefix || file.startsWith(prefix));
}
Перед отправкой каждый URL проходит проверку: только https: и только хост сайта. Ошибка в маппинге не отправит в поисковик чужой адрес. Ответ, отличный от 200 и 202, превращается в исключение с первыми 500 символами тела:
const response = await fetch('https://api.indexnow.org/indexnow', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ host, key, keyLocation: `${siteOrigin}/${key}.txt`, urlList }),
});
if (![200, 202].includes(response.status)) {
const body = (await response.text()).slice(0, 500);
throw new Error(`IndexNow returned HTTP ${response.status}: ${body}`);
}
Так выглядит --dry-run на коммите, который публикует одну статью в двух локалях:
$ node scripts/notify-indexnow.mjs --base-ref=<PREV_COMMIT> --dry-run
IndexNow: 2 changed URLs since <PREV_COMMIT>.
https://futurecraft.pro/blog/claude-code-russia/
https://futurecraft.pro/ru/blog/claude-code-russia/
На merge-коммите пакетного релиза из девяти статей тот же прогон вернул 18 URL. Картинки в public/og/ и llms.txt тоже менялись в этом релизе, но страниц не порождают, и в список не попали.
У разностного подхода три особенности, которые легко упустить:
- База должна быть прошлым продовым коммитом. Если взять
origin/mainпри деплое из ветки, список окажется неполным или пустым. В CI базу даётgithub.event.before, при ручном деплое её задают переменнойINDEXNOW_BASE_REF. - Переименование статьи теряло старый URL.
git diff --name-onlyпо умолчанию распознаёт переименования и печатает только новый путь, поэтому адрес, который теперь отдаёт 404, в список не попадал. Скрипт вызывает diff с флагом--no-renames: в выводе оба пути, и поисковик узнаёт о старом адресе. - Удалённая статья даёт URL, который уже отдаёт 404. Это ожидаемо: протокол принимает такие адреса.
Шаг в CI: было и стало
До ноября 2026 года IndexNow в CI этого блога вызывался так (фрагмент, ключ заменён):
- name: Notify search engines (IndexNow, Yandex, PubSubHubbub)
continue-on-error: true
run: |
SITEMAP_URL="https://${SITE}/sitemap-index.xml"
INDEXES=$(curl -s "$SITEMAP_URL" | grep -o '<loc>[^<]*</loc>' | sed 's/<\/*loc>//g')
PAGES=""
for sm in $INDEXES; do
PAGES="${PAGES}$(curl -s "$sm" | grep -o '<loc>[^<]*</loc>' | sed 's/<\/*loc>//g')"$'\n'
done
URL_JSON=$(echo "$PAGES" | grep -v '^$' | head -100 | jq -R . | jq -s .)
curl -s -X POST "https://api.indexnow.org/indexnow" \
-H "Content-Type: application/json" \
-d "{\"host\":\"${SITE}\",\"key\":\"<KEY>\",\"keyLocation\":\"https://${SITE}/<KEY>.txt\",\"urlList\":${URL_JSON}}" \
-w "\nIndexNow: HTTP %{http_code}\n"
Шаг появился в июле 2026 года вместе с автодеплоем. Ручной deploy.sh ещё в августе перешёл на разностный скрипт, а CI-шаг обновить забыли, и он проработал так почти четыре месяца. Ошибок в нём три, и все видны при чтении кода.
head -100берёт первые 100 адресов sitemap, а не изменённые. На 6 ноября 2026 года в sitemap блога 336 URL. Они идут по алфавиту пути, и первая русская страница стоит на 168-м месте. Шаг не отправил ни одного из 168 русских адресов, а для Яндекса русские страницы главные. Новая английская статья попадала в отправку, только если её slug начинался с букв отaдо примерноj.- Неизменённые страницы уходили при каждом деплое. Это прямо противоречит рекомендациям протокола и Яндекса и тратит квоту обхода.
- Ошибка ответа была не видна.
curlпечатал код, шаг всегда завершался успешно. Ответ 403 из-за недоступного файла ключа или 429 можно было обнаружить, только перечитывая логи.
Исправленный шаг вызывает разностный скрипт. Базой служит не прошлый пуш, а коммит последнего успешного деплоя: его возвращает API GitHub Actions.
permissions:
contents: read
actions: read # чтобы узнать последний успешный деплой
# ...
- uses: actions/checkout@v4
with:
fetch-depth: 0 # нужна история, чтобы сравнить с продовым коммитом
- name: Notify IndexNow (changed URLs only)
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
INDEXNOW_KEY: <KEY>
PUSH_BEFORE: ${{ github.event.before }}
run: |
BASE=$(gh api "repos/${GITHUB_REPOSITORY}/actions/workflows/deploy.yml/runs?branch=main&status=success&per_page=1" \
--jq '.workflow_runs[0].head_sha // empty' || true)
git cat-file -e "${BASE}^{commit}" 2>/dev/null || BASE="$PUSH_BEFORE"
git cat-file -e "${BASE}^{commit}" 2>/dev/null || BASE="$(git rev-parse HEAD~1)"
node scripts/notify-indexnow.mjs --base-ref="$BASE"
Почему не github.event.before. Это SHA ветки main до пуша, и в первой версии исправления базой был он. Но workflow использует concurrency с cancel-in-progress: true: если новый пуш отменит деплой предыдущего, следующий запуск посчитал бы разность от отменённого коммита, и его URL в IndexNow не ушли бы. При ручном запуске (workflow_dispatch) и вызове из другого workflow (workflow_call) before нет вовсе. Последний успешный деплой закрывает все три случая, а before и HEAD~1 остались запасными вариантами. Если workflow вызывается из другого, право actions: read нужно выдать и вызывающему: вложенный workflow не получит больше прав, чем у него. По умолчанию actions/checkout скачивает один коммит, отсюда fetch-depth: 0.
continue-on-error: true осталось, но смысл поменялся. Раньше шаг не мог упасть. Теперь скрипт бросает исключение на любом ответе, кроме 200 и 202: шаг отмечается ошибкой, а деплой считается успешным. Сайт уже выложен, и откатывать его из-за отказа поисковика не нужно. Пинг Яндекса и PubSubHubbub вынесены в отдельный шаг, чтобы их результат не смешивался с IndexNow.
До мержа исправление проверено прогоном --dry-run на диффе одной статьи: ровно 2 URL, английский и русский. Первый боевой запуск пришёлся на пачку из четырёх мержей подряд. Три промежуточных деплоя отменились, последний прошёл, и шаг взял базой последний успешный деплой до пачки:
Base: last successful deploy (2a9fee1…).
IndexNow: 344 changed URLs since 2a9fee1….
IndexNow: HTTP 200
344 адреса здесь не ошибка. В пачке был PR, который менял файлы в src/pages/, а такие изменения затрагивают все страницы, и скрипт отправляет весь sitemap. В том числе 168 русских URL, которые старый шаг с head -100 не отправлял ни разу. Обычный деплой одной статьи даёт 2 адреса.
Если CI в этой части нужен с нуля, YAML для GitHub Actions удобно собирать по промптам генератора CI/CD.
API Вебмастера: переобход приоритетных URL
IndexNow сообщает об изменении всем участникам сразу. Переобход через API Вебмастера ставит конкретный URL в приоритетную очередь робота Яндекса, и по каждому URL можно отследить статус. Обратная сторона: нужен OAuth-токен, а число URL ограничено суточной квотой.
OAuth-токен и права
По инструкции Яндекса приложение регистрируют на oauth.yandex.ru/client/new: платформа «Веб-сервисы», redirect URI https://oauth.yandex.ru/verification_code, права webmaster:hostinfo и webmaster:verify. Токен получают по ссылке https://oauth.yandex.ru/authorize?response_type=token&client_id=<CLIENT_ID>.
Токен действует 6 месяцев, потом его нужно получить заново по той же ссылке. Запросы без токена получают 401. В примерах справочника API токен передаётся заголовком Authorization: OAuth <TOKEN>. Хранить его нужно в секретах CI или в локальном файле окружения вне репозитория, но не в коде.
user-id и host-id
Все методы переобхода требуют два идентификатора. Их получают один раз:
AUTH="Authorization: OAuth ${YWM_TOKEN}"
curl -s -H "$AUTH" https://api.webmaster.yandex.net/v4/user | jq .user_id
curl -s -H "$AUTH" "https://api.webmaster.yandex.net/v4/user/${YWM_USER_ID}/hosts" \
| jq -r '.hosts[] | "\(.host_id) \(.unicode_host_url) verified=\(.verified)"'
host_id имеет вид https:example.com:443. Переобход работает только для подтверждённого сайта, иначе API вернёт 404 HOST_NOT_VERIFIED.
Отправка и квота
Метод переобхода: POST /v4/user/{user-id}/hosts/{host-id}/recrawl/queue с телом {"url": "..."}. В ответ приходит 202 с task_id и quota_remainder, остатком суточной квоты. Ошибки: 400 INVALID_URL, 403 INVALID_USER_ID, 404 HOST_NOT_VERIFIED, 409 URL_ALREADY_ADDED и 429 QUOTA_EXCEEDED (в теле daily_quota и exceeded_until, время сброса квоты).
Числа квоты в документации нет, в примерах ответов демо-значения. Метод recrawl/quota возвращает реальное значение для конкретного сайта. Для futurecraft.pro 6 ноября 2026 года ответ был таким:
{ "daily_quota": 150, "quota_remainder": 150 }
Это наблюдение для одного хоста, а не гарантия: у другого сайта квота может быть другой. Скрипт ниже сначала читает остаток и отправляет URL, пока он не кончится. Передавать URL лучше в порядке важности: новые статьи, затем обновлённые, затем остальное.
#!/usr/bin/env bash
# recrawl.sh URL...: отправить URL на переобход в пределах суточной квоты
set -uo pipefail
API="https://api.webmaster.yandex.net/v4/user/${YWM_USER_ID}/hosts/${YWM_HOST_ID}"
AUTH="Authorization: OAuth ${YWM_TOKEN}"
left=$(curl -sf --max-time 20 --retry 2 -H "$AUTH" "$API/recrawl/quota" \
| jq -er .quota_remainder) || { echo "Квоту не получить (сеть или токен), пропускаю"; exit 0; }
for url in "$@"; do
[ "$left" -gt 0 ] || { echo "Квота на сегодня исчерпана"; break; }
code=$(curl -s -o resp.json -w '%{http_code}' --max-time 20 -X POST \
-H "$AUTH" -H "Content-Type: application/json" \
-d "$(jq -nc --arg u "$url" '{url: $u}')" "$API/recrawl/queue")
case "$code" in
202) left=$(jq -r .quota_remainder resp.json)
echo "OK $url task=$(jq -r .task_id resp.json) left=$left" ;;
409) echo "Уже в очереди: $url" ;;
429) echo "Квота исчерпана: $(cat resp.json)"; break ;;
*) echo "HTTP $code для $url: $(cat resp.json)" ;;
esac
done
Скрипт написан по документации и проверен shellcheck; ветка недоступного API (неверный токен, выход с кодом 0) проверена прогоном. Запросы на отправку при подготовке статьи не выполнялись, чтобы не тратить квоту. Сам метод recrawl/queue проверен раньше: с тем же заголовком он вернул 202 и task_id.
Есть отдельный механизм для крупных изменений: приоритетный переобход Sitemap в API v4.1. Робот загружает файл sitemap сразу, не дожидаясь планового обхода. Лимит: 10 раз в месяц, остаток показывает GET /v4.1/user/{user-id}/hosts/{host-id}/sitemaps/recrawl. Подходит для релиза, после которого поменялись десятки страниц, но в ежедневный деплой его встраивать не стоит.
Статус задач
GET .../recrawl/queue/{task-id} возвращает state, у которого три значения:
IN_PROGRESS- задача в очереди;DONE- робот загрузил URL;FAILED- загрузить не удалось; проверьте доступность страницы и скорость ответа.
DONE не означает, что страница уже в поиске. Справка Яндекса называет для переобхода срок индексирования от 3 до 7 дней. Список всех задач отдаёт GET .../recrawl/queue: у futurecraft.pro на 6 ноября 2026 года в нём 79 задач, и у всех, что вернул API, статус DONE.
Путь эндпоинта лучше сверять с документацией, а не с памятью. MCP-клиент, через который мы работали с Вебмастером, обращался к /recrawl/tasks и получал «Resource not found». Рабочий путь - /recrawl/queue, он и указан в текущей документации.
Google: что остаётся без IndexNow
Google в IndexNow не участвует: ни на главной indexnow.org, ни в FAQ протокола его нет. Остальные способы ускорить обнаружение страниц для Google тоже сузились:
- Пинг sitemap отключён. Google объявил об этом в июне 2023 года с отсрочкой в 6 месяцев; запросы на старый endpoint возвращают 404. Вместо него Google просит корректный
lastmodв sitemap, ссылку на sitemap в robots.txt и отправку через Search Console. - Indexing API не для блогов. По документации он предназначен только для страниц с разметкой
JobPostingилиBroadcastEventвнутриVideoObject. Скрипт для Indexing API в этом блоге был, в августе 2026 года его удалили. - Запрос индексирования остался ручным. «Проверка URL» в Search Console позволяет запросить индексирование страницы, но только в интерфейсе и в пределах квоты. URL Inspection API статус только читает.
- PubSubHubbub работает как хаб. CI блога отправляет
hub.mode=publishдля RSS-лент наpubsubhubbub.appspot.com. Хаб отвечает, но документации Search Central о том, что Google Поиск использует WebSub для индексации обычных страниц, найти не удалось. Влияние на индексацию не измерено.
Для Google основная работа - точный lastmod, внутренние ссылки на новые страницы и sitemap в Search Console. IndexNow на Google не влияет.
Как проверить результат
Ответ 200 от IndexNow означает только «URL получены». Проверять нужно на стороне поисковика, и у каждого канала свой способ:
| Канал | Где проверить | Что означает успех |
|---|---|---|
| IndexNow → Bing | Bing Webmaster Tools, отчёт по URL, полученным через IndexNow | URL видны как полученные |
| IndexNow → Яндекс | Яндекс Вебмастер (по справке, результаты IndexNow видны там) | страница загружена роботом |
| Переобход Яндекса | GET recrawl/queue/{task-id}, страница «Переобход страниц» | DONE |
| Индекс Яндекса | Вебмастер: страницы в поиске, история индексирования | URL в поиске |
| Search Console: «Проверка URL», отчёт «Страницы» | «URL есть в индексе Google» |
Пример из практики: в конце сентября 2026 года после SEO-релиза в IndexNow ушло 127 URL, и Bing Webmaster Tools показал их полученными примерно через 13 часов. Это время до подтверждения приёма, а не до индексации: скорость попадания в индекс мы не измеряли.
Перед любой отправкой проверьте, что HTML на продакшене уже обновился. Поисковик получит сигнал, придёт за страницей и увидит то, что отдаст CDN:
curl -sI https://example.com/ru/blog/post/ | grep -i -E '^(age|cache-control|cf-cache-status|last-modified):'
Большой age при cf-cache-status: HIT означает, что CDN отдаёт старую копию.
Эксплуатационные нюансы из практики
API Вебмастера бывает недоступен. 11 октября 2026 года не работал весь сервис: интерфейс показывал «Сервис временно недоступен», API не отвечал. С 11 по 22 октября api.webmaster.yandex.net не отвечал с рабочей машины по таймауту соединения; похоже на сетевую блокировку, а не на проблему с токеном. 6 ноября вызовы снова проходят. Отсюда правило: шаг переобхода не должен ломать деплой, нужны таймауты, пара повторов и лог. При долгой недоступности источник правды - интерфейс Вебмастера в браузере.
CDN отдаёт старый HTML. 22 октября 2026 года одна из точек присутствия Cloudflare отдавала уже снятую страницу с возрастом около 27 часов: зона кэшировала HTML с s-maxage=604800, то есть на неделю. Уведомление поисковика не гарантирует, что робот получит свежую версию. После деплоя нужно проверить заголовки и при необходимости сбросить кэш; для этого у токена CI должны быть права на purge.
CI может не стартовать. В октябре 2026 года workflow этого блога завершался с startup_failure: истекла карта для оплаты GitHub. Релиз выкатили вручную через deploy.sh, а IndexNow и PubSubHubbub отправили руками. Уведомление не должно жить только в CI. В блоге deploy.sh вызывает тот же notify-indexnow.mjs с базой из INDEXNOW_BASE_REF и отказывается деплоить что-либо, кроме чистого origin/main.
Токен истекает через 6 месяцев. Дату выпуска удобно записать рядом с секретом и поставить напоминание. Признак истёкшего токена - 401 от API, его полезно выводить отдельно от сетевых ошибок. В скрипте выше обе ситуации пока сведены в одно сообщение.
Не все окна данных длинные. Часть отчётов Вебмастера в API отдаёт ограниченное окно: мониторинг поисковых запросов, например, показывает данные только за последние две недели. Для проверки результата надёжнее статусы задач переобхода и история индексирования.
Смежные API Яндекса: Wordstat и Метрика
Эти API не про индексацию, но в SEO-пайплайне обычно живут рядом.
Wordstat API теперь входит в Yandex Search API в AI Studio (документация). Методы: GetTop (популярные запросы со словом за 30 дней), GetDynamics (динамика по месяцам, неделям или дням), GetRegionsDistribution и GetRegionsTree. Работает синхронно, для запросов нужна роль search-api.webSearch.user, квоты и тарифы описаны в отдельных разделах документации. Как использовать данные о запросах при планировании контента, разобрано в статье про исследование ключевых слов с AI.
API отчётов Метрики (справка) отдаёт агрегированную статистику по группировкам и метрикам в JSON или CSV. Для индексации полезна косвенно: показывает, пошёл ли поисковый трафик на новую страницу.
llms.txt и Markdown-версии страниц тоже помогают машинам находить контент, но поисковиков об изменениях не уведомляют. Это отдельная тема, она разобрана в гайде по llms.txt. Как сигналы индексации соотносятся с цитированием в AI-поиске Bing, показано в кейсе про 3 500 цитирований в Copilot.
Чек-лист автоматизации
- Сгенерировать ключ IndexNow (8-128 символов), положить
<KEY>.txtв корень сайта и проверить, что файл отдаётся с кодом 200. - Получать список изменённых URL из
git diffмежду прошлым и новым продовым коммитом, а не из sitemap. - Сопоставить пути контента с URL всех локалей; изменения шаблонов и конфигов сборки разворачивать во весь sitemap.
- Пропускать в отправку только URL своего хоста по HTTPS.
- Считать успехом 200 и 202, остальные коды показывать ошибкой шага, не ломая деплой.
- В GitHub Actions задать
fetch-depth: 0и базой брать коммит последнего успешного деплоя (запасные варианты:github.event.before, затемHEAD~1). - Новые страницы дополнительно ставить на переобход через API Вебмастера, предварительно прочитав
recrawl/quota. - Хранить OAuth-токен в секретах, записать дату его выпуска и обновлять раз в 6 месяцев.
- Перед отправкой проверять, что CDN отдаёт новый HTML.
- Через сутки проверить приём в Bing Webmaster Tools и статусы задач переобхода, через неделю - страницы в поиске.
Если публикация статьи у вас уже автоматизирована, шаг уведомления встаёт в конец пайплайна, после деплоя; пример такого пайплайна разобран в статье про контент-движок на AI.
Источники
- IndexNow: документация протокола, FAQ
- Bing: IndexNow, начало работы
- Яндекс, справка Вебмастера: поддержка IndexNow, справочник запросов IndexNow, ключ IndexNow, новые и изменённые страницы
- API Яндекс Вебмастера: авторизация, отправка на переобход, квота, статус задачи, приоритетный переобход Sitemap
- Google: прекращение пинга sitemap, Indexing API
- Yandex AI Studio: Wordstat API; Метрика: API отчётов