📊Яндекс.Метрика API — 6 запросов, которые интерфейс показывает хуже

Curl-примеры для health-check счётчика, фильтра ботов, комбинации источник × landing, среза по часам и когорт удержания. С подводными камнями.

редакция seodb#яндекс-метрика#api#автоматизация

Интерфейс Метрики удобен для беглого взгляда: «сколько сегодня зашло, откуда». Как только надо ответить на вопрос сложнее одного измерения — начинается переключение между отчётами, пересечение сегментов через клики, экспорт в CSV, ручная склейка в pandas. Всё то же самое API отдаёт одной строкой curl, за 200 мс, в JSON, готовое к автоматизации.

Ниже — шесть запросов, которые я реально гоняю каждую неделю на своих проектах. Порядок примерно от простого к менее очевидному. Все примеры под OAuth-токен с scope metrika:read, получить его — минут пятнадцать. ${CID} — id счётчика, ${TOKEN} — OAuth-токен.

1. Health-check счётчика — код на месте, но статус мутный

Первое, с чего начинаю после установки нового счётчика или когда что-то ведёт себя странно:

curl -sS -H "Authorization: OAuth ${TOKEN}" \
  "https://api-metrika.yandex.net/management/v1/counter/${CID}" \
  | jq '.counter | {code_status, activity_status, has_data: (.features // [])}'

В интерфейсе этого поля нет вообще — «Проверить код счётчика» есть, а code_status как значение не показывается. Через API — три состояния: CS_OK (всё чисто), CS_ERR_* (счётчик не найден или не отвечает как надо), CS_ERR_UNKNOWN (данные приходят, но проверка не смогла подтвердить).

Реальный случай — код стоит через SSR, визиты идут (проверил в реалтайме), а code_status: CS_ERR_UNKNOWN держится третий день. Причина оказалась банальной: проверочный бот Яндекса ходит по URL без query-string, а роутер отдавал ему шаблон 404 с закомментированным кодом счётчика. Интерфейс на это никак не жаловался. API — сразу.

Быстрый чек-лист когда CS_ERR_*:

  1. curl -sS https://mysite/ | grep -c mc.yandex.ru — счётчик реально в HTML отдаётся?
  2. Открыть Метрику → Настройки → Счётчик → «Проверить». Ручной триггер обновит поле.
  3. Если счётчик через ssr:true — убедиться, что серверный рендер не режет <script> тега.

2. Отсечь ботов, которых интерфейс всё равно тащит

В отчётах интерфейса галка «Роботы» по умолчанию исключает Yandex/Google-owned боты, но пропускает мимо кучу мелкой шелухи, которая триггерит счётчик через JS. На молодом домене это может быть половина визитов.

curl -sSG -H "Authorization: OAuth ${TOKEN}" \
  "https://api-metrika.yandex.net/stat/v1/data" \
  --data-urlencode "ids=${CID}" \
  --data-urlencode "metrics=ym:s:visits,ym:s:users,ym:s:bounceRate" \
  --data-urlencode "dimensions=ym:s:isRobot" \
  --data-urlencode "date1=7daysAgo" --data-urlencode "date2=today"

Ответ разбивает трафик на Yes / No / null. Если Yes больше 20% — стоит скачать список визитов с isRobot=='Yes' и посмотреть, кто это. Часто там прячутся мониторинги (UptimeRobot, Better Uptime), собственные health-checks, сайт-чекеры конкурентов.

Дальше все остальные метрики я снимаю уже с фильтром:

--data-urlencode "filters=ym:s:isRobot=='No'"

Разница в bounce rate между «весь трафик» и «только не-боты» на моих собственных проектах — 5-15 процентных пунктов. То есть если в интерфейсе показывает 68% — реальный человеческий bounce ближе к 55%. Это не мелочь для отчёта клиенту.

3. Источник × landing — за один запрос, без переключения отчётов

Классический вопрос: «какой источник приводит людей на конкретную статью». В интерфейсе — открыть отчёт «Источники», кликнуть на статью, увидеть источники только этой статьи. Хочешь понять картину по всему блогу — кликай в каждую по очереди.

API принимает два измерения сразу:

curl -sSG -H "Authorization: OAuth ${TOKEN}" \
  "https://api-metrika.yandex.net/stat/v1/data" \
  --data-urlencode "ids=${CID}" \
  --data-urlencode "metrics=ym:s:visits" \
  --data-urlencode "dimensions=ym:s:startURLPath,ym:s:trafficSource" \
  --data-urlencode "filters=ym:s:startURLPath=~'/blog/'" \
  --data-urlencode "date1=30daysAgo" --data-urlencode "date2=today" \
  --data-urlencode "limit=200" \
  --data-urlencode "sort=-ym:s:visits"

На выходе — плоская таблица «URL блога / источник / визитов». Дальше это pivot-ится за 3 строки на pandas или в Excel. Раз в неделю запускаю и вижу: какие статьи пришли из поиска, какие тащит Telegram, какие — реферальные ссылки с профильных сайтов. Инсайт по-хорошему один и тот же: у 80% контента 90% визитов из одного канала. Понимание, из какого — задаёт стратегию, что пушить дальше.

Оператор =~ в фильтре — это regex-match. Полный синтаксис фильтров есть в документации, но для 90% случаев хватает ==, !=, =~, =@ (contains).

4. По часам за день — увидеть аномалию, которую суточная гранулярность прячет

В интерфейсе минимальная гранулярность в отчёте — день. Если проект получил ботовую атаку, накрутку поведенческих или просто аномальный всплеск — суточный график покажет «был большой день», без понимания когда именно и как долго.

Endpoint /stat/v1/data/bytime даёт разбивку по часам:

curl -sSG -H "Authorization: OAuth ${TOKEN}" \
  "https://api-metrika.yandex.net/stat/v1/data/bytime" \
  --data-urlencode "ids=${CID}" \
  --data-urlencode "metrics=ym:s:visits,ym:s:bounceRate,ym:s:pageDepth" \
  --data-urlencode "date1=yesterday" --data-urlencode "date2=today" \
  --data-urlencode "group=hour" \
  --data-urlencode "filters=ym:s:isRobot=='No'"

В ответе time_intervals — массив часов, data[].metrics — 24 значения на метрику. Загоняешь в матплотлиб или jq — сразу видно: пик в 4 утра при среднем bounce 100% и глубине 1.0 — это точно не люди, кто-то долбит по одному URL. Пик в 21:00 из Telegram-паблика с bounce 40% и глубиной 2.5 — это нормальный трафик.

group=hour работает на диапазонах до 7 дней. Хочешь дальше — group=day, group=week, group=month.

5. Глубина сессии по landing — сегментируем «залипательные» страницы

Метрика в интерфейсе показывает глубину усреднённо на весь сайт. Полезнее — глубина по стартовой странице: кто из landing-ов реально ведёт вглубь, а кто — тупик.

curl -sSG -H "Authorization: OAuth ${TOKEN}" \
  "https://api-metrika.yandex.net/stat/v1/data" \
  --data-urlencode "ids=${CID}" \
  --data-urlencode "metrics=ym:s:visits,ym:s:pageDepth,ym:s:bounceRate,ym:s:avgVisitDurationSeconds" \
  --data-urlencode "dimensions=ym:s:startURLPath" \
  --data-urlencode "filters=ym:s:isRobot=='No' AND ym:s:visits>5" \
  --data-urlencode "date1=30daysAgo" --data-urlencode "date2=today" \
  --data-urlencode "sort=-ym:s:pageDepth" \
  --data-urlencode "limit=50"

Фильтр ym:s:visits>5 отсекает URL-ы с одним-двумя случайными визитами, где статистика ничего не значит. Сортировка по глубине — сверху страницы, с которых человек уходит дальше по сайту (значит, там нормальный внутренний перелинк или контент цепляет). Внизу — тупики: заходит, посмотрел, ушёл.

Использую это как приоритезацию: страницы снизу списка либо получают дополнительные внутренние ссылки в теле, либо переупаковываются под другой intent.

6. Когорты удержания — вернулись ли люди

Классический продуктовый вопрос, который в интерфейсе Метрики спрятан в отчёт «Периодичность визитов» и там подан странно. Через API — cohort-таблица за один запрос:

curl -sSG -H "Authorization: OAuth ${TOKEN}" \
  "https://api-metrika.yandex.net/stat/v1/data" \
  --data-urlencode "ids=${CID}" \
  --data-urlencode "metrics=ym:s:visits,ym:s:users" \
  --data-urlencode "dimensions=ym:s:firstVisitDate,ym:s:date" \
  --data-urlencode "date1=30daysAgo" --data-urlencode "date2=today" \
  --data-urlencode "filters=ym:s:isRobot=='No'" \
  --data-urlencode "limit=1000"

На выходе пары «дата первого визита / дата возврата». Дальше pivot и retention-таблица собирается за 5 строк pandas — по диагонали идёт когорта, по вертикали смотришь, какой процент вернулся на день N.

Для контентного сайта retention плохой в 90% случаев — там нет причины возвращаться каждый день. Но retention по неделям (ym:s:firstVisitWeek, ym:s:week) для блога с регулярной публикацией — уже нормальная метрика. Если каждая пятая когорта возвращается через неделю — контент читают серией, а не как разовый заход из поиска.

Что стоит помнить, когда сядешь автоматизировать

Три вещи, на которых я обжигался.

Первое — семплирование. На больших счётчиках Метрика семплирует ответ и возвращает sampled: true, sample_share: 0.1. Если пропустил и посчитал абсолютные числа — расчёт мимо на порядок. Всегда проверять sample_share в ответе и либо запрашивать accuracy=full (медленнее), либо умножать на 1 / sample_share.

Второе — атрибуция. По умолчанию attribution=CROSS_DEVICE_LAST_SIGNIFICANT (последний значимый источник, склеенный между устройствами). Для многих задач нужен Automatic или First (первое касание). Забыл параметр — получил цифры, которые не бьются с интерфейсом, потому что там пользователь мог выбрать другую атрибуцию.

Третье — лимиты. 200 запросов в минуту, 60 в 10 секунд, 5 параллельных на счётчик. Для большого сайта с десятками URL — писать sleep 0.5 между запросами или использовать batching через dimensions, а не итерировать по URL руками.

Что делать сегодня

Если счётчик уже стоит — получи OAuth-токен и прогони первый запрос из этого поста. code_status покажет, всё ли чисто с установкой. Дальше — второй запрос, чтобы увидеть долю ботов и решить, нужно ли ставить фильтр по умолчанию во все последующие запросы.

Если строишь регулярный отчёт клиенту — оберни 6 запросов в Python-скрипт с еженедельным крон-триггером, сохраняй JSON в S3 или Postgres, поверх этого строй дашборд. Три часа работы — и клиент получает автоматический еженедельный snapshot, а ты — экономишь час каждый понедельник.

Метрика — это не только красивая карта кликов в интерфейсе. Через API это база данных с историей визитов, из которой можно вытащить что угодно, если знать, о чём спрашивать.