~/wiki / seo / yandex-metrica-goals-api-automation

Цели Яндекс.Метрики через API: автоматизация вместо ручной настройки

Основной чат

Чат для вайбкодеров: новости, гайды, поиск исполнителей, маркетплейс и разбор реальных кейсов.

$ cd раздел/ $ join vibe dev
Цели Яндекс.Метрики через API: автоматизация вместо ручной настройки - обложка

Обычный путь настройки целей в Яндекс.Метрике выглядит так: зайти в интерфейс, нажать «Добавить цель», ввести название, выбрать тип, сохранить. Повторить 15 раз. Потом при переезде на другой счётчик — повторить всё снова. Если в команде несколько проектов — повторить для каждого.

Есть способ лучше. Яндекс.Метрика предоставляет Management API, через которое можно создавать цели программно — скриптом, за секунды, без интерфейса. Один раз написал скрипт, добавил в package.json, и теперь синхронизация целей запускается одной командой.

Разбираем как это работает и как сделать самому.


Зачем автоматизировать цели

Воспроизводимость. Список целей живёт в коде рядом с проектом. Новый разработчик клонирует репозиторий, запускает скрипт — и счётчик настроен. Никаких «а кто помнит что мы отслеживали?».

Пересинхронизация. Если счётчик слетел, нужно завести новый или скопировать цели на другой счётчик — запускаете скрипт с другим COUNTER_ID и готово.

Версионирование. Список целей в git-репозитории — видно когда добавили новую цель, кто и зачем.

Защита от дублей. Скрипт сначала запрашивает текущие цели и создаёт только те, которых ещё нет.


Как это работает: схема

plaintext
npm run metrica:goals
        │
        ▼
Скрипт читает YANDEX_METRICA_TOKEN и COUNTER_ID из .env
        │
        ▼
GET /management/v1/counter/{id}/goals  ← текущие цели в Метрике
        │
        ▼
Сравнение с нужным списком целей в коде
        │
        ├── Цель уже есть → пропустить
        └── Цели нет → POST /management/v1/counter/{id}/goals/  ← создать

Скрипт никогда не удаляет существующие цели — только добавляет новые. Безопасно запускать повторно сколько угодно раз.


Шаг 1. Получите OAuth-токен Яндекса

Для работы с Management API нужен OAuth-токен. Это не логин/пароль, а специальный ключ для API.

Зарегистрируйте приложение

  1. Откройте oauth.yandex.ru
  2. Нажмите «Зарегистрировать новое приложение»
  3. Название — любое, например «Metrica Goals Sync»
  4. Платформа — «Веб-сервисы»
  5. Redirect URI — https://oauth.yandex.ru/verification_code
  6. Доступы — найдите раздел «Яндекс.Метрика», включите metrika:write

После регистрации получите client_id.

Получите токен

Откройте в браузере:

plaintext
https://oauth.yandex.ru/authorize?response_type=token&client_id=ВАШ_CLIENT_ID

Авторизуйтесь, разрешите доступ — в адресной строке появится токен вида y0_AgAAAA.... Скопируйте его.

Сохраните в .env

bash
# .env (не коммитить в git!)
YANDEX_METRICA_TOKEN=y0_AgAAAA...
YANDEX_METRICA_COUNTER_ID=ВАШ_НОМЕР_СЧЁТЧИКА

Номер счётчика виден в URL когда вы открываете счётчик в интерфейсе Метрики.


Шаг 2. Напишите скрипт синхронизации

javascript
// scripts/yandex-metrica-goals.mjs

const TOKEN = process.env.YANDEX_METRICA_TOKEN;
const COUNTER_ID = process.env.YANDEX_METRICA_COUNTER_ID;

if (!TOKEN || !COUNTER_ID) {
  console.error('❌ Укажите YANDEX_METRICA_TOKEN и YANDEX_METRICA_COUNTER_ID');
  process.exit(1);
}

const BASE_URL = `https://api-metrika.yandex.net/management/v1/counter/${COUNTER_ID}`;

const HEADERS = {
  'Authorization': `OAuth ${TOKEN}`,
  'Content-Type': 'application/json',
};

// ─── Список нужных целей ──────────────────────────────────────────────────────
//
// type: 'action' — JavaScript-событие через ym(id, 'reachGoal', 'goal_name')
// Поле conditions[0].value — это 'goal_name' в вашем reachGoal()
//
const DESIRED_GOALS = [
  {
    name: 'Открытие статьи',
    type: 'action',
    conditions: [{ type: 'exact', value: 'article_open' }],
  },
  {
    name: 'Клик по CTA',
    type: 'action',
    conditions: [{ type: 'exact', value: 'cta_click' }],
  },
  {
    name: 'Поиск по сайту',
    type: 'action',
    conditions: [{ type: 'exact', value: 'search' }],
  },
  {
    name: 'Переход в Telegram',
    type: 'action',
    conditions: [{ type: 'exact', value: 'telegram_click' }],
  },
  {
    name: 'Добавление в закладки',
    type: 'action',
    conditions: [{ type: 'exact', value: 'bookmark_add' }],
  },
  // Добавляйте свои цели сюда
];

// ─── Вспомогательные функции ──────────────────────────────────────────────────

async function getExistingGoals() {
  const res = await fetch(`${BASE_URL}/goals`, { headers: HEADERS });
  if (!res.ok) {
    const err = await res.text();
    throw new Error(`Ошибка получения целей: ${res.status} ${err}`);
  }
  const data = await res.json();
  return data.goals || [];
}

async function createGoal(goal) {
  const res = await fetch(`${BASE_URL}/goals/`, {
    method: 'POST',
    headers: HEADERS,
    body: JSON.stringify({ goal }),
  });
  if (!res.ok) {
    const err = await res.text();
    throw new Error(`Ошибка создания цели "${goal.name}": ${res.status} ${err}`);
  }
  return res.json();
}

// ─── Основная логика ──────────────────────────────────────────────────────────

async function syncGoals() {
  console.log(`\n🔄 Синхронизация целей для счётчика ${COUNTER_ID}\n`);

  // 1. Получаем текущие цели
  const existingGoals = await getExistingGoals();
  console.log(`📊 Текущих целей в Метрике: ${existingGoals.length}`);

  // Собираем Set названий для быстрого поиска
  const existingNames = new Set(existingGoals.map(g => g.name));

  // 2. Создаём только отсутствующие
  let created = 0;
  let skipped = 0;

  for (const goal of DESIRED_GOALS) {
    if (existingNames.has(goal.name)) {
      console.log(`  ⏭️  Пропущена (уже есть): ${goal.name}`);
      skipped++;
      continue;
    }

    try {
      await createGoal(goal);
      console.log(`  ✅ Создана: ${goal.name}`);
      created++;
    } catch (err) {
      console.error(`  ❌ ${err.message}`);
    }

    // Небольшая пауза чтобы не спамить API
    await new Promise(r => setTimeout(r, 300));
  }

  console.log(`\n✨ Готово! Создано: ${created}, пропущено: ${skipped}\n`);
}

syncGoals().catch(err => {
  console.error('Критическая ошибка:', err.message);
  process.exit(1);
});

Шаг 3. Добавьте команду в package.json

json
{
  "scripts": {
    "metrica:goals": "node scripts/yandex-metrica-goals.mjs"
  }
}

Теперь синхронизация запускается так:

bash
npm run metrica:goals

Шаг 4. Отправляйте события со страниц

Цели в Метрике — это только «ловушки». Чтобы они срабатывали, нужно отправлять события с сайта через ym().

Убедитесь что счётчик установлен

html
<!-- В <head> каждой страницы -->
<script>
  (function(m,e,t,r,i,k,a){
    m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
    // ... стандартный код счётчика Метрики
  })(window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
  
  ym(ВАШ_COUNTER_ID, "init", { clickmap: true, trackLinks: true });
</script>

Отправка события

javascript
// Синтаксис
ym(COUNTER_ID, 'reachGoal', 'goal_name');

// Примеры — значение 'goal_name' должно совпадать
// с conditions[0].value в вашем скрипте
ym(12345678, 'reachGoal', 'article_open');
ym(12345678, 'reachGoal', 'cta_click');
ym(12345678, 'reachGoal', 'search');

Реальные примеры интеграции

javascript
// Открытие статьи — при монтировании компонента
useEffect(() => {
  ym(COUNTER_ID, 'reachGoal', 'article_open');
}, []);

// Клик по CTA-кнопке
function handleCtaClick() {
  ym(COUNTER_ID, 'reachGoal', 'cta_click');
  // ... остальная логика
}

// Поиск — при отправке формы
function handleSearch(query) {
  if (query.length > 2) {
    ym(COUNTER_ID, 'reachGoal', 'search');
  }
}

// Переход в Telegram
function handleTelegramLink() {
  ym(COUNTER_ID, 'reachGoal', 'telegram_click');
}

// Добавление в закладки
function handleBookmark(articleId) {
  ym(COUNTER_ID, 'reachGoal', 'bookmark_add');
  saveBookmark(articleId);
}

Полная схема работы

plaintext
Пользователь открыл статью
       │
       ▼
useEffect → ym(id, 'reachGoal', 'article_open')
       │
       ▼
Яндекс.Метрика засчитала цель "Открытие статьи"
       │
       ▼
Видно в отчётах Метрики → Конверсии → Цели

Всё что связывает клиентский код с настройками Метрики — строка 'article_open'. Именно поэтому важно чтобы значение в conditions[0].value в скрипте совпадало с тем что вы передаёте в reachGoal().


Типы целей в API

В скрипте используется тип action — это JavaScript-событие через reachGoal. Но API поддерживает и другие типы:

Тип Когда использовать Пример
action JS-событие через reachGoal Клик по кнопке
url Посещение URL Страница /thank-you
request Запрос к URL Файл .pdf скачан
step Составная цель (шаги) Воронка оформления заказа

Для url и request тип условия будет другим:

javascript
// Цель по URL
{
  name: 'Страница благодарности',
  type: 'url',
  conditions: [{ type: 'contain', value: '/thank-you' }],
}

// Цель по скачиванию файла
{
  name: 'Скачан прайс-лист',
  type: 'request',
  conditions: [{ type: 'contain', value: 'price-list.pdf' }],
}

Запуск с явными переменными

Если .env не настроен или нужно запустить для другого счётчика:

bash
# Экспортировать переменные из .env и запустить
set -a && source .env && set +a
YANDEX_METRICA_COUNTER_ID=другой_счётчик npm run metrica:goals

# Или передать переменные напрямую
YANDEX_METRICA_TOKEN=токен YANDEX_METRICA_COUNTER_ID=123456 npm run metrica:goals

Что добавить в .gitignore

gitignore
# Токены и секреты — никогда в репозиторий
.env
.env.local
.env.*.local

Сам скрипт yandex-metrica-goals.mjs — коммитить можно и нужно: в нём нет секретов, только логика и список целей.


Итог

Автоматизация целей через API решает три проблемы сразу: воспроизводимость (список целей в коде), безопасность повторного запуска (нет дублей, нет удалений) и скорость (секунды вместо ручной настройки через интерфейс).

Схема простая: скрипт знает какие цели должны быть, спрашивает Метрику какие есть, создаёт разницу. Сайт отправляет события через ym(id, 'reachGoal', 'название'). Метрика засчитывает конверсии.

Один раз настроили — и больше не думаете о ручной настройке целей при смене счётчика или добавлении нового проекта.

$ cd ../ ← назад к SEO и продвижение