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.comRSS- и 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. Коды ответа по документации протокола и справочнику Яндекса:

КодЗначениеЧто делать
200URL полученыничего; это факт приёма, не индексации
202приняты, ключ ещё проверяетсядля нового ключа подождать; у Яндекса постоянный 202 значит, что файл ключа не найден
400неверный формат запросапроверить JSON и кодирование URL
403ключ не найден или не совпадает с файломоткрыть <KEY>.txt по HTTPS
405метод не поддерживается (у Яндекса)использовать GET для одного URL или POST для пакета
422URL не принадлежат 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-шаг обновить забыли, и он проработал так почти четыре месяца. Ошибок в нём три, и все видны при чтении кода.

  1. head -100 берёт первые 100 адресов sitemap, а не изменённые. На 6 ноября 2026 года в sitemap блога 336 URL. Они идут по алфавиту пути, и первая русская страница стоит на 168-м месте. Шаг не отправил ни одного из 168 русских адресов, а для Яндекса русские страницы главные. Новая английская статья попадала в отправку, только если её slug начинался с букв от a до примерно j.
  2. Неизменённые страницы уходили при каждом деплое. Это прямо противоречит рекомендациям протокола и Яндекса и тратит квоту обхода.
  3. Ошибка ответа была не видна. 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 → BingBing Webmaster Tools, отчёт по URL, полученным через IndexNowURL видны как полученные
IndexNow → ЯндексЯндекс Вебмастер (по справке, результаты IndexNow видны там)страница загружена роботом
Переобход ЯндексаGET recrawl/queue/{task-id}, страница «Переобход страниц»DONE
Индекс ЯндексаВебмастер: страницы в поиске, история индексированияURL в поиске
GoogleSearch 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.

Чек-лист автоматизации

  1. Сгенерировать ключ IndexNow (8-128 символов), положить <KEY>.txt в корень сайта и проверить, что файл отдаётся с кодом 200.
  2. Получать список изменённых URL из git diff между прошлым и новым продовым коммитом, а не из sitemap.
  3. Сопоставить пути контента с URL всех локалей; изменения шаблонов и конфигов сборки разворачивать во весь sitemap.
  4. Пропускать в отправку только URL своего хоста по HTTPS.
  5. Считать успехом 200 и 202, остальные коды показывать ошибкой шага, не ломая деплой.
  6. В GitHub Actions задать fetch-depth: 0 и базой брать коммит последнего успешного деплоя (запасные варианты: github.event.before, затем HEAD~1).
  7. Новые страницы дополнительно ставить на переобход через API Вебмастера, предварительно прочитав recrawl/quota.
  8. Хранить OAuth-токен в секретах, записать дату его выпуска и обновлять раз в 6 месяцев.
  9. Перед отправкой проверять, что CDN отдаёт новый HTML.
  10. Через сутки проверить приём в Bing Webmaster Tools и статусы задач переобхода, через неделю - страницы в поиске.

Если публикация статьи у вас уже автоматизирована, шаг уведомления встаёт в конец пайплайна, после деплоя; пример такого пайплайна разобран в статье про контент-движок на AI.

Источники

Часто задаваемые вопросы

Поддерживает ли Google IndexNow?
Нет. Google не входит в число участников IndexNow ни на indexnow.org, ни в FAQ протокола. Для Google остаются sitemap с корректным lastmod, внутренние ссылки и ручной запрос индексирования в Search Console. Пинг sitemap Google отключил: анонс вышел в июне 2023 года.
Нужно ли отправлять URL в Bing и Яндекс отдельно?
По FAQ indexnow.org отправка на любой endpoint участника передаётся всем остальным, поэтому одного запроса на api.indexnow.org достаточно. В справке Яндекса такая передача прямо не описана. Если важно не зависеть от общего шлюза, можно дополнительно отправлять тот же список на yandex.com/indexnow.
Сколько URL можно отправить через IndexNow и API Вебмастера?
IndexNow принимает до 10 000 URL в одном POST-запросе, суточные пороги каждый поисковик задаёт сам и не публикует. Переобход в API Вебмастера ограничен суточной квотой, которую возвращает метод recrawl/quota. У futurecraft.pro это 150 URL в день; для другого сайта квота может быть другой.
Через сколько страница появится в поиске после отправки?
Срок не гарантирован ни IndexNow, ни API Вебмастера. Справка Яндекса для переобхода называет от 3 до 7 дней на индексирование. Код 200 или 202 и даже статус задачи DONE означают только, что запрос принят или робот загрузил страницу, а не что она уже в поиске.
Нужен ли IndexNow, если на сайте есть sitemap?
Да, это разные сигналы. Sitemap остаётся полным списком страниц, по которому робот обходит сайт по своему расписанию. IndexNow сообщает о конкретном изменении сразу после деплоя. Яндекс прямо рекомендует совмещать способы: sitemap, обход по счётчику Метрики и IndexNow.
Можно ли через IndexNow сообщить об удалении страницы?
Да. И протокол, и справка Яндекса разрешают отправлять URL, которые теперь отдают 404 или 410, а также адреса с редиректом 301 или 302. Так поисковик быстрее узнает, что устаревшую страницу пора убрать из выдачи.
Что делать, если API Яндекс Вебмастера не отвечает?
Не останавливать деплой: шаг отправки должен завершаться с предупреждением, а не с ошибкой. Нужны таймауты, пара повторов и лог ответа. Если API недоступен долго, URL можно отправить вручную в интерфейсе Вебмастера на странице «Переобход страниц».