
Понадобилась мне семантика — не в смысле «один раз глянуть Wordstat в браузере», а программно, чтобы прогонять по 50-100 фраз в день и складывать результаты в свою базу. Контекст: веду контент-маркетинг для агентства разработки чат-ботов BotKraft, статьи под Яндекс Нейро. Веб-Wordstat для такого объёма не вариант — копировать вручную из таблички полдня. Direct API — слишком дорогой вход: нужен рекламный аккаунт, отдельный OAuth, у меня этого не было и заводить ради одного метода не хотелось.
Случайно полез в новые сервисы Yandex Cloud AI Studio (там сейчас живёт YandexGPT) и обнаружил, что Wordstat теперь есть в Search API v2 — отдельным сервисом без зависимости от Direct. Доступ — обычный API-ключ из AI Studio, тот же что и для YandexGPT. По сути в один клик получаешь ещё и доступ к семантике.
Подключал, по дороге собрал коллекцию граблей. Этим и поделюсь.
Где это вообще лежит
Не путаем три разных сервиса с похожим названием:
-
Search API for sites — старый сервис «поиск по сайту», возвращает SERP по запросам, оплата за выдачу
-
Search API v2 (новый) — то что в AI Studio, включает Web Search, Image Search, Generative Search и Wordstat
-
Yandex Direct API → Reports.Wordstat — старый путь через рекламный аккаунт
Меня интересует второе. Endpoint root для Wordstat: https://searchapi.api.cloud.yandex.net/v2/wordstat/...
Под капотом — gRPC c REST-обёрткой через google.api.http annotations. Полная proto-спецификация в публичном yandex-cloud/cloudapi — рекомендую смотреть туда, а не в гипертекстовую документацию: в proto всё точнее и без неоднозначностей.
Четыре метода:
|
Метод |
Path |
Назначение |
|---|---|---|
|
|
|
топ похожих запросов с частотностью за 30 дней + ассоциации |
|
|
|
частотность по времени (день/неделя/месяц) |
|
|
|
география запроса по регионам/городам |
|
|
|
справочник всех регионов с ID |
Ничего сверхъестественного. Operators (!слово, +слово, [слово]) и сравнение нескольких фраз в одном запросе — отсутствуют, об этом ниже.
Авторизация и история про folder
Авторизация — заголовок Authorization: Api-Key <ключ>. Ключ создаётся в AI Studio привязанным к Service Account, тот живёт в каком-то folder. Folder ID — обязательное поле folderId в JSON-теле каждого запроса. Без него — ошибка INVALID_ARGUMENT.
И вот тут первая засада: где взять folder ID? В UI AI Studio это, конечно, есть, но прятано за пару кликов в консоли Yandex Cloud → Folders. Документация AI Studio (на момент моего тыка) folder в инструкциях явно не упоминала — будто все априори знают.
Я узнал свой folder через крайне дурацкий путь. Сначала пытался вызвать YandexGPT с подставленным gpt://test/yandexgpt/latest (просто чтобы проверить что ключ работает). API ответил:
Specified folder ID 'test' does not match with service account folder ID 'b1g0gj00k72p1kr6dsc5'
Спасибо ребятам, что зашили имя реального folder в текст ошибки — буквально лайфхак на ровном месте. Тот же folder подошёл и для Wordstat-эндпоинтов.
Если у вас тоже не сразу очевидно где folder — можно либо тыкать в Cloud Console, либо вот так через текст ошибки. Гарантированно даст актуальный.
topRequests — основной метод
Этот метод я использую в 80% случаев. Он возвращает не просто частотность одной фразы, а топ похожих формулировок (results) и семантически близкие запросы (associations). Последнее — золото, об этом отдельно.
Минимальный запрос:
curl -X POST 'https://searchapi.api.cloud.yandex.net/v2/wordstat/topRequests' \ -H "Authorization: Api-Key $YANDEX_AI_API_KEY" \ -H 'Content-Type: application/json' \ -d '{ "phrase": "чат бот для бизнеса", "numPhrases": 20, "regions": ["213"], "folderId": "b1g0gj00k72p1kr6dsc5" }'
Параметры:
-
phrase— основной запрос (до 400 символов, обязательно) -
numPhrases— сколько результатов вернуть, 1-2000, по умолчанию 20 -
regions— массив региональных ID (["213"]— Москва,["2"]— Санкт-Петербург). Опционально -
devices— массив["DEVICE_ALL"|"DEVICE_DESKTOP"|"DEVICE_PHONE"|"DEVICE_TABLET"]. Опционально -
folderId— обязательно
Ответ:
{ "totalCount": "21500", "results": [ {"phrase": "чат боты для бизнеса", "count": "1100"}, {"phrase": "чат бот для бизнеса макс", "count": "145"}, {"phrase": "ии чат бот для бизнеса", "count": "102"} ], "associations": [ {"phrase": "чатбот", "count": "7371"}, {"phrase": "чатбот нейросеть", "count": "372"} ]}
Почему count — строки, а не числа? Это особенность gRPC→JSON преобразования: protobuf int64 сериализуется в JSON как string, чтобы не терять точность в больших значениях (а JS-number — int53). Чисто гигиенически — оборачивайте int(...) перед арифметикой, иначе словите сюрприз на сравнениях.
Зачем смотреть на associations
Реальная история. Я готовил статью «Чат-бот для бизнеса: как выбрать», уверенный что основной запрос — чат бот для бизнеса. Запросил topRequests, получил totalCount: 21500 и top-1 «чат боты для бизнеса» — 1100 в месяц. Не густо.
Дальше посмотрел в associations. Первая позиция — слово «чатбот» одно. Просто слитное написание. Частотность — 7371. В семь раз больше моего «правильного» запроса.
Ну и кто из нас был «прав». Маркетолог с многолетней практикой Wordstat это бы заметил глазами — там всё это на одном экране. Я как технарь без regular-практики решил: «возьму то что напрашивается». А API заставил меня посмотреть на associations отдельным полем — и уберёг от очевидного, но проигнорированного бага.
Если вы внедряете Wordstat в автоматический пайплайн (а зачем ещё API?) — обязательно дёргайте обе колонки и сравнивайте totalCount ассоциаций с основным запросом. На свой H1 я в итоге поставил «чатбот», а не «чат бот для бизнеса». Прикинул бы вы такое глазами на 50 фразах в день?
dynamics — графики во времени
Возвращает частотность по периодам. Полезно для оценки сезонности: запускать ли ниши в межсезонье, какой месяц брать за baseline.
curl -X POST 'https://searchapi.api.cloud.yandex.net/v2/wordstat/dynamics' \ -H "Authorization: Api-Key $YANDEX_AI_API_KEY" \ -H 'Content-Type: application/json' \ -d '{ "phrase": "чат бот для бизнеса", "period": "PERIOD_MONTHLY", "fromDate": "2026-01-01T00:00:00Z", "toDate": "2026-03-31T23:59:59Z", "folderId": "b1g0gj00k72p1kr6dsc5" }'
period — PERIOD_DAILY / PERIOD_WEEKLY / PERIOD_MONTHLY. С префиксом PERIOD_ — это enum в proto, и API строго проверяет. Без префикса — INVALID_ARGUMENT.
Даты — RFC3339 timestamps. И вот тут второй грабль: для PERIOD_MONTHLY поле toDate ОБЯЗАНО быть последним днём месяца. Поставил 2026-04-01T00:00:00Z — получил:
{ "code": 3, "message": "rpc error: code = InvalidArgument desc = The to field value should be the last day of the month", "details": [...]}
Аналогично — для PERIOD_WEEKLY нужен последний день недели. В proto этого ограничения нет, написано просто google.protobuf.Timestamp. Узнаёшь из ответа сервера. Если делаете календарную автоматизацию — закладывайте функцию last_day_of_period(date, period).
Отдаёт response в виде:
{ "results": [ {"date": "2026-01-31T00:00:00Z", "count": "1050", "share": 0.0000087}, {"date": "2026-02-28T00:00:00Z", "count": "1180", "share": 0.0000094}, {"date": "2026-03-31T00:00:00Z", "count": "1100", "share": 0.0000091} ]}
share — доля запроса от всех Yandex-запросов в этом периоде. Полезно для нормировки если сравниваете тренды разных ниш.
regions — география запросов
Чтобы понимать, где запускать рекламу или какой регион таргетить контентом.
curl -X POST 'https://searchapi.api.cloud.yandex.net/v2/wordstat/regions' \ -H "Authorization: Api-Key $YANDEX_AI_API_KEY" \ -H 'Content-Type: application/json' \ -d '{ "phrase": "чат бот для бизнеса", "region": "REGION_REGIONS", "folderId": "b1g0gj00k72p1kr6dsc5" }'
region — три варианта: REGION_ALL (всё подряд), REGION_CITIES (только города), REGION_REGIONS (только субъекты РФ).
В каждом элементе ответа интересно поле affinityIndex:
{ "region": "1", "count": "235", "share": 0.0000109, "affinityIndex": 120.4}
affinityIndex — отношение доли запроса в этом регионе к доле по стране. Больше 100 — «горячее» среднего, меньше 100 — слабее. Я использую так: при выборе ниши для лендинга смотрю топ-10 регионов с affinityIndex > 130 — там запрос реально греется, можно ехать с офером.
Минус: region в ответе — это ID, а не название. Чтобы превратить в человекочитаемое — нужен справочник из четвёртого метода.
getRegionsTree — справочник регионов
Самый скучный, но без него географические данные мало понятны. Возвращает дерево всех регионов Yandex с ID и label (название). Я его дёргаю один раз и кэширую в локальный JSON, дальше — просто маппинг id→name.
curl -X POST 'https://searchapi.api.cloud.yandex.net/v2/wordstat/getRegionsTree' \ -H "Authorization: Api-Key $YANDEX_AI_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"folderId": "b1g0gj00k72p1kr6dsc5"}'
В ответе — рекурсивная структура regions: [{id, label, children: [...]}]. Размер JSON — около 200 KB, в нём пара тысяч регионов вплоть до районов городов.
Минимальный Python wrapper
Свой воркшоп в 60 строк, без сторонних библиотек (только stdlib):
import jsonimport osimport sslimport sysimport urllib.requestimport urllib.errorKEY = os.environ["YANDEX_AI_API_KEY"]FOLDER = os.environ.get("YANDEX_FOLDER_ID", "b1g0gj00k72p1kr6dsc5")API_BASE = "https://searchapi.api.cloud.yandex.net/v2/wordstat"# macOS Python: системный CA-bundle часто не подтянут - отключаем строгую проверку.# В проде - ставьте certifi и нормальный context, тут для локальных скриптов сойдёт.SSL_CTX = ssl.create_default_context()SSL_CTX.check_hostname = FalseSSL_CTX.verify_mode = ssl.CERT_NONEdef call(path: str, body: dict) -> dict: body.setdefault("folderId", FOLDER) req = urllib.request.Request( f"{API_BASE}/{path}", data=json.dumps(body).encode("utf-8"), headers={ "Authorization": f"Api-Key {KEY}", "Content-Type": "application/json", }, method="POST", ) try: with urllib.request.urlopen(req, timeout=30, context=SSL_CTX) as resp: return json.loads(resp.read().decode("utf-8")) except urllib.error.HTTPError as e: return {"_http_error": e.code, "_body": e.read().decode("utf-8", errors="replace")}def top(phrase: str, num: int = 20) -> None: res = call("topRequests", {"phrase": phrase, "numPhrases": num}) if "_http_error" in res: sys.exit(f"HTTP {res['_http_error']}: {res['_body'][:200]}") print(f"\n«{phrase}» - всего {res['totalCount']} запросов/мес\n") print("ТОП:") for r in res.get("results", []): print(f" {int(r['count']):>8} {r['phrase']}") if res.get("associations"): print("\nАССОЦИАЦИИ:") for r in res["associations"]: print(f" {int(r['count']):>8} {r['phrase']}")if __name__ == "__main__": top(sys.argv[1] if len(sys.argv) > 1 else "чат бот для бизнеса", num=10)
Запускаешь python3 wordstat.py "ваша фраза" — видишь топ + ассоциации. На основе этого скелета у меня выросли отдельные команды dynamics, regions, batch (последняя — прогон по списку из файла, чтобы проверить SEO-семантику разом).
Я специально ушёл от requests и взял голый urllib: меньше зависимостей, легче ставить в CI, никаких внезапных breaking changes от сторонней либы.
Подводные камни (сводно)
-
Folder ID нигде явно не упомянут в инструкциях — узнаётся через текст ошибки YandexGPT (см. выше). В перспективе наверняка добавят в quickstart, но сейчас так
-
PERIOD_MONTHLYтребуетtoDate— последний день месяца. Аналогично для weekly. В proto не описано, узнаёте от сервера -
macOS Python без сертификатов:
ssl.SSLCertVerificationError. Решение —ssl.CERT_NONEдля локалки или установитьcertifiдля прода -
countприходит строкой (proto-quirk для int64). Не забывайтеint(...)перед операциями -
associationsможет отсутствовать на узких нишевых запросах — пустой массив, неnull. Не assume что ключ всегда есть с данными
Чего НЕТ vs веб-Wordstat
До переезда с веб-Wordstat на API стоит понимать что в API чего-то не будет:
-
Операторы (
!слово,+слово,[слово]) — не поддерживаются. Передать вphraseможно, но точная фразовая частотность не гарантируется -
Сравнение нескольких фраз в одном запросе — нет. Только по одной за вызов
-
Левая колонка / правая колонка отдельно — API возвращает только results + associations, без полного списка ассоциаций без частот
-
История запросов пользователя — нет (что логично)
Если вам нужна тонкая работа с операторами — открываете wordstat-2.yandex.ru в браузере. Если массовая проверка тысячи фраз для контентного пайплайна — этот API.
Куда я это вписал
Конкретно у меня — в пайплайн проверки H1 / подзаголовков / FAQ. Перед публикацией статьи запускаю wordstat.py top "<черновой H1>" — смотрю что там в associations. Часто переписываю H1 под более популярную формулировку. Время — секунды, выгода — часы недописанного контента, который никто не нашёл бы.
Из побочного: автоматизировал проверку «не пишем ли мы про мёртвую тему». Если totalCount < 200/мес — статья слишком узкая, или бы взять смежный широкий запрос.
Цены и квоты
Приятный сюрприз: Wordstat API в составе AI Studio Search API на момент написания бесплатен. По общей политике AI Studio без оплаты идут API-вызовы (в том числе Wordstat и embedding-сервисы), хранилище файлов и поисковые индексы; платить вы будете только за токены LLM-моделей через Model Gallery (YandexGPT и подобные). Wordstat сюда не попадает.
Актуальный статус всё-таки лучше глянуть на странице тарифов AI Studio — Yandex иногда меняет правила, особенно когда сервис выходит из беты.
Rate limit, конечно, есть — стандартный для Yandex Cloud, где-то 5-10 RPS. Я не упирался при разумном использовании. Для batch-обходов на сотни ключей закладываю time.sleep(0.2) между вызовами просто из вежливости и чтобы не словить 429.
Когда брать, когда нет
Брать, если:
-
Регулярно нужна семантика (10+ запросов в день)
-
Уже есть Yandex Cloud-аккаунт с AI Studio (зачастую под YandexGPT)
-
Не хочется заводить рекламный аккаунт ради Wordstat
Не брать, если:
-
Запросов на Wordstat — 5 в неделю. Веб-интерфейс быстрее
-
Нужны операторы и тонкая работа с ассоциациями
-
Уже глубоко используете Direct API и Wordstat там устраивает
Полезные ссылки
В целом — практичный API за разумные деньги. Главное переживите грабли с folder ID и месячными датами, дальше всё прозрачно.
ссылка на оригинал статьи https://habr.com/ru/articles/1030276/