Позиции сайта через API: как выгрузить данные из Яндекс Вебмастера
Позиции по запросам бесплатно отдаёт API Яндекс Вебмастера. Подводные камни: составной host_id, query_indicator повторяющимся параметром и разные limit.
- Позиции живут в двух семействах эндпоинтов: search-queries (сводка и история) и query-analytics (разбивка по дням)
- host_id — не домен, а строка вида https:example.com:443, её нужно получить отдельным запросом
- Без параметра query_indicator сводка приходит с пустым indicators — самая частая ошибка интеграции
- Массовая проверка позиций — скрипт примерно на 90 строк: до 3000 запросов с пагинацией по 500 и сохранением в CSV
- Тот же набор данных доступен без кода — через MCP-подключение AI к Вебмастеру
Содержание (10)
Если позиции нужны не глазами, а в таблице, дашборде или в скрипте — их отдаёт API Яндекс Вебмастера. Бесплатно, по тысячам запросов сайта, без ограничения «до 100 ключей в тарифе». Ниже — какие эндпоинты за это отвечают, чем они отличаются друг от друга, где у них лимиты и на чём чаще всего спотыкается интеграция.
Про то, как читать эти же цифры руками в интерфейсе и когда всё-таки нужен внешний съёмщик, есть отдельная статья — мониторинг позиций в Яндексе. Здесь — техническая сторона.
Что именно отдаёт API
Сразу важное ограничение, чтобы не строить неверных ожиданий. Вебмастер не отдаёт «позицию сайта по запросу в Москве на мобильных в 14:30». Он отдаёт среднюю позицию показа — усреднение по всем реальным показам вашего сайта живым пользователям за период. Это другая метрика, чем у сервисов съёма, и в ряде задач она честнее: она взвешена по фактическому спросу, а не по одному замеру из одной точки.
Вместе с позицией доступны показы, клики и CTR — то есть полная связка, по которой видно не только «где стоим», но и «приносит ли это трафик».
Авторизация и host_id
Работа с API начинается с двух вещей, которые почти всегда съедают первый час.
Токен. Авторизация — OAuth Яндекса, заголовком Authorization: OAuth <token>. Приложение регистрируется в Яндекс OAuth, нужное право — доступ к Вебмастеру.
Идентификаторы. В URL каждого запроса участвуют user_id и host_id, и оба нужно получить отдельными вызовами:
GET https://api.webmaster.yandex.net/v4/user
GET https://api.webmaster.yandex.net/v4/user/{user-id}/hosts
Здесь первая ловушка: host_id — это не домен. Он выглядит как https:example.com:443 — схема, домен и порт через двоеточия, без слешей. В путь URL его нужно вставлять в URL-кодированном виде. Подставить туда просто example.com — самая частая ошибка на старте.
Эндпоинт 1: сводка по запросам
GET /v4/user/{user-id}/hosts/{host-id}/search-queries/popular
Возвращает список запросов, по которым сайт показывался, отсортированный по показам или кликам. Ключевые параметры:
order_by—TOTAL_SHOWSилиTOTAL_CLICKS, обязательный;query_indicator— какие метрики приложить к каждому запросу:TOTAL_SHOWS,TOTAL_CLICKS,AVG_SHOW_POSITION,AVG_CLICK_POSITION;date_from,date_to— период;device_type_indicator—ALL,DESKTOP,MOBILE,TABLET,MOBILE_AND_TABLET;offset,limit— пагинация, до 500 записей за вызов.
Главная ловушка этого метода. Параметр query_indicator необязательный, и если его не передать, сервер спокойно вернёт 200 и список запросов — но с пустым indicators: {} у каждого. Ни позиций, ни показов. Выглядит как «API не отдаёт метрики», на деле — не запросили.
Второй нюанс: индикаторы передаются повторяющимся параметром, а не строкой через запятую:
?order_by=TOTAL_SHOWS
&query_indicator=TOTAL_SHOWS
&query_indicator=TOTAL_CLICKS
&query_indicator=AVG_SHOW_POSITION
Если ваш HTTP-клиент склеивает массив в query_indicator=A,B,C, метрики снова не придут. Мы сами наступили на эту граблю в коде Seely: схема инструмента объявляла параметр строкой, а клиент ждал массив — в итоге индикаторы молча не доходили до API, и запросы возвращались без цифр. Ошибка починена, но она хорошо показывает, насколько тихо этот метод деградирует.
Эндпоинт 2: разбивка по дням
POST /v4/user/{user-id}/hosts/{host-id}/query-analytics/list
Метод другого назначения: он отдаёт статистику по дням за последние две недели, и не только по запросам, но и по URL. Тело запроса задаёт:
text_indicator—QUERY(группировать по запросам) илиURL(по страницам);limit— до 20 записей за вызов, это самый жёсткий потолок из всех методов;region_ids— фильтр по регионам;filters— текстовые и статистические фильтры (например, только запросы с позицией лучше 10);sort_by_date— сортировка.
В ответе на каждую запись приходит массив statistics — по одной точке на день и поле: IMPRESSIONS, CLICKS, CTR, POSITION, DEMAND. Плюс поле popular_complementary_indicator: для запроса это самый частый URL, для URL — самый частый запрос. Именно оно позволяет одним вызовом понять, какая страница отвечает за какой запрос — то есть увидеть каннибализацию, не сводя данные вручную.
DEMAND — отдельная ценность: это оценка общего спроса по запросу, а не только ваших показов. По нему видно, вы просели или просел сам спрос.
Эндпоинт 3: история
GET /v4/user/{user-id}/hosts/{host-id}/search-queries/all/history
GET /v4/user/{user-id}/hosts/{host-id}/search-queries/{query-id}/history
Первый — динамика суммарных показателей по всем запросам, второй — по конкретному запросу (query_id берётся из ответа сводки). Оба принимают query_indicator теми же значениями и с тем же правилом повторяющегося параметра.
Это основной инструмент, когда нужно отделить апдейт алгоритма от собственной ошибки: если по всем запросам провал в один день — это апдейт, если по одному — правки на странице.
Как собрать выгрузку целиком
Порядок, который работает:
- Получить
user_idиhost_id— один раз, дальше кэшировать. - Забрать сводку через
search-queries/popularс нужными индикаторами, страницами по 500 записей черезoffset, пока ответ не станет короче лимита. - Для запросов, которые интересны, дозапросить дневную динамику через
query-analytics/listпачками по 20. - Сохранить срез с датой снятия: API отдаёт скользящее окно, «как было три месяца назад» задним числом уже не восстановить.
Последний пункт важнее, чем кажется. Вебмастер хранит ограниченное окно истории, поэтому собственный архив снимков — единственный способ через год ответить на вопрос «а что было прошлой весной».
Есть и общий лимит частоты обращений на приложение, поэтому в цикле по страницам нужна пауза и обработка 429, а не «выгрузить всё в 50 параллельных потоков».
Массовая проверка позиций: скрипт на API Вебмастера
Ниже — рабочий скрипт на Python, который делает массовую проверку позиций по запросам сайта (до 3000 самых популярных) за последние 28 дней и сохраняет результат в CSV. Он использует только метод search-queries/popular и учитывает все ловушки из разделов выше: составной host_id в URL-кодировке, query_indicator повторяющимся параметром, пагинацию по 500 записей и повтор при 429.
Что понадобится:
- Python 3.9+ и библиотека
requests(pip install requests); - OAuth-токен Яндекса с правом доступа к Вебмастеру в переменной окружения
YANDEX_WEBMASTER_TOKEN; - подтверждённые права на сайт в Вебмастере.
import csv
import os
import sys
import time
from datetime import date, timedelta
from urllib.parse import quote
import requests
API = "https://api.webmaster.yandex.net/v4"
TOKEN = os.environ["YANDEX_WEBMASTER_TOKEN"] # OAuth-токен с доступом к Вебмастеру
DAYS = 28
DEVICE = "ALL" # ALL, DESKTOP, MOBILE, TABLET, MOBILE_AND_TABLET
PAGE = 500 # максимум limit для search-queries/popular
session = requests.Session()
session.headers["Authorization"] = f"OAuth {TOKEN}"
def get(url, params=None):
"""GET с повтором при 429 (превышение квоты) и 5xx."""
for attempt in range(5):
r = session.get(url, params=params, timeout=30)
if r.status_code == 429 or r.status_code >= 500:
time.sleep(5 * 2 ** attempt)
continue
r.raise_for_status()
return r.json()
raise RuntimeError(f"API не ответило после 5 попыток: {url}")
def fmt(value):
return round(value, 1) if value else ""
user_id = get(f"{API}/user")["user_id"]
hosts = get(f"{API}/user/{user_id}/hosts")["hosts"]
if len(sys.argv) < 2:
print("Передайте host_id первым аргументом. Доступные сайты:")
for h in hosts:
print(" ", h["host_id"], h.get("unicode_host_url", ""))
sys.exit(1)
host_id = sys.argv[1] # вида https:example.com:443
date_to = date.today() - timedelta(days=1)
date_from = date_to - timedelta(days=DAYS - 1)
url = f"{API}/user/{user_id}/hosts/{quote(host_id, safe='')}/search-queries/popular"
rows, offset = [], 0
while True:
# список кортежей -> query_indicator уйдёт повторяющимся параметром
params = [
("order_by", "TOTAL_SHOWS"),
("query_indicator", "TOTAL_SHOWS"),
("query_indicator", "TOTAL_CLICKS"),
("query_indicator", "AVG_SHOW_POSITION"),
("query_indicator", "AVG_CLICK_POSITION"),
("device_type_indicator", DEVICE),
("date_from", date_from.isoformat()),
("date_to", date_to.isoformat()),
("offset", offset),
("limit", PAGE),
]
data = get(url, params)
batch = data.get("queries", [])
for q in batch:
ind = q.get("indicators", {})
shows = ind.get("TOTAL_SHOWS") or 0
clicks = ind.get("TOTAL_CLICKS") or 0
rows.append({
"query": q["query_text"],
"shows": int(shows),
"clicks": int(clicks),
"ctr_%": round(clicks / shows * 100, 2) if shows else 0,
"avg_show_position": fmt(ind.get("AVG_SHOW_POSITION")),
"avg_click_position": fmt(ind.get("AVG_CLICK_POSITION")),
})
offset += len(batch)
if len(batch) < PAGE or offset >= data.get("count", 0):
break
time.sleep(1) # бережём квоту приложения
filename = f"positions_{host_id.split(':')[1]}_{date.today().isoformat()}.csv"
with open(filename, "w", newline="", encoding="utf-8-sig") as f:
writer = csv.DictWriter(f, fieldnames=rows[0].keys() if rows else ["query"], delimiter=";")
writer.writeheader()
writer.writerows(rows)
print(f"{len(rows)} запросов за {date_from} — {date_to} сохранено в {filename}")
Запуск в два шага:
export YANDEX_WEBMASTER_TOKEN=y0_...
python positions.py # выведет список host_id ваших сайтов
python positions.py https:example.com:443 # выгрузит позиции в CSV
На выходе — файл вида positions_example.com_2026-09-29.csv с колонками: запрос, показы, клики, CTR, средняя позиция показа и средняя позиция клика. Разделитель — точка с запятой, кодировка с BOM, так что файл сразу открывается в Excel без «кракозябр».
Как доработать под свои задачи:
- Разбивка по устройствам. Поменяйте
DEVICEнаMOBILE_AND_TABLETилиDESKTOPи запустите дважды — получите позиции отдельно для мобильной и десктопной выдачи. - Архив срезов. Имя файла содержит дату, поэтому скрипт можно повесить на cron раз в неделю и копить собственную историю, которую Вебмастер не хранит.
- Сравнение периодов. Запустите с
DAYS = 28сейчас и сравните с файлом четырёхнедельной давности — рост и падение позиций по каждому запросу видны простым ВПР илиpandas.merge. - Связка «запрос — страница». Если нужно понять, какой URL отвечает за запрос, дозапросите
query-analytics/listсtext_indicator=QUERY— полеpopular_complementary_indicatorотдаст основную страницу.
Важная оговорка: по документации Яндекса метод отдаёт ТОП-3000 поисковых запросов сайта, то есть максимум шесть страниц по 500. Для большинства небольших и средних сайтов это практически вся значимая семантика. Если запросов больше, редкие хвостовые в выгрузку не попадут — для них используйте query-analytics/list с фильтрами или сужайте период и устройство.
Не хотите писать код ради выгрузки? Подключите Вебмастер к ChatGPT или Claude — те же эндпоинты, но задача формулируется текстом.
Как подключить за 5 минутТот же результат без кода
Всё описанное выше — обвязка вокруг довольно простых HTTP-запросов: авторизация, идентификаторы, пагинация, повторяющиеся параметры. Эту обвязку можно не писать.
MCP-сервер делает ровно её: держит токен, знает про составной host_id, сам разбивает выгрузку на страницы и отдаёт нейросети готовые данные. На вашей стороне остаётся формулировка задачи:
Выгрузи из Вебмастера запросы за последние 28 дней с показами, кликами и средней позицией. Найди те, где позиция в топ-10, но CTR ниже 3%. Для каждого покажи, какая страница отвечает за запрос.
Внутри это несколько вызовов тех же самых методов — get_webm_popular_queries за сводкой, get_webm_query_analytics_list за связкой «запрос — URL». Разница в том, что пагинацию, индикаторы и формат ответа разбирает сервер, а не вы.
Когда своя интеграция всё-таки нужна: если данные ложатся в собственную БД, если нужен ежедневный снапшот по расписанию или если поверх строится клиентский дашборд. Для разовой аналитики и еженедельных разборов писать код смысла мало.
Итого
- Позиции, показы и клики по запросам Яндекс отдаёт бесплатно через API Вебмастера v4.
search-queries/popular— сводка до 500 записей,query-analytics/list— дневная разбивка до 20,history— динамика.- Массовая проверка позиций — один скрипт с пагинацией и выгрузкой в CSV (до 3000 запросов), без платных съёмщиков.
- Три вещи ломают интеграцию чаще всего: домен вместо составного
host_id, отсутствующийquery_indicatorи склейка индикаторов в один параметр. - Позиция здесь — усреднение по реальным показам, а не съём из конкретного города.
- Историю нужно архивировать самому: API отдаёт скользящее окно.
Что делать с полученными цифрами дальше — в статье про вывод запросов из топ-20 в топ-10, а про регулярную отчётность на этих данных — в материале об автоматизации SEO-отчётов.
Частые вопросы
Да, это API Яндекс Вебмастера версии v4. Позиции, показы, клики и CTR по запросам отдают эндпоинты search-queries (сводка и история) и query-analytics (разбивка по дням). Доступ бесплатный, сводка отдаёт до 3000 самых популярных запросов, авторизация — по OAuth-токену Яндекса.
Потому что не передан параметр query_indicator. Он необязательный, и без него сервер отдаёт только тексты запросов без метрик. Указывать его нужно повторяющимся параметром: query_indicator=TOTAL_SHOWS&query_indicator=AVG_SHOW_POSITION, а не одной строкой через запятую.
Съёмщик отправляет запрос в поиск из конкретного региона с конкретного устройства и фиксирует место сайта. API отдаёт среднюю позицию по реальным показам вашим посетителям — по всем регионам и устройствам сразу. Для отчёта по городам нужен съёмщик, для понимания реальной картины — API.
Они разные у разных методов: сводка популярных запросов отдаёт до 500 записей за вызов, query-analytics — до 20 за вызов, выборка URL в поиске — до 100. Плюс общий лимит частоты обращений на приложение. Поэтому большие выгрузки строятся через offset и пагинацию.
Через метод search-queries/popular API Вебмастера: он отдаёт до 500 запросов за вызов со средней позицией, показами и кликами, дальше страницы перебираются через offset. Скрипт на Python с сохранением в CSV укладывается примерно в 90 строк и работает бесплатно, без лимита «ключей в тарифе». Метод отдаёт до 3000 самых популярных запросов сайта.
Да. MCP-сервер выступает посредником: он вызывает те же эндпоинты Вебмастера, а вы формулируете задачу текстом в ChatGPT или Claude. Токен и пагинация остаются на стороне сервера, вы получаете готовую таблицу.
Частично. У query-analytics есть параметр region_ids, он фильтрует статистику по регионам. Но это по-прежнему усреднение по показам внутри региона, а не позиция в выдаче конкретного города в конкретную минуту.