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

Yandex targets. Metrics through APIs: Automation instead of manual setup

Main chat

A chat for vibe coders: news, guides, live cases, marketplace, and finding executors.

$ cd section/ $ join vibe dev
Yandex targets. Metrics through APIs: Automation instead of manual setup - обложка

The usual way to set goals in Yandex. Metrice looks like this: go to the interface, click "Add goal", enter the name, select the type, save. Repeat 15 times. Then when you move to another counter, repeat everything again. If there are several projects in the team, repeat them for everyone.

There's a better way. Yandex. Metrics provides a Management API through which you can create goals programmatically - scripted, in seconds, without an interface. Once wrote a script, added to package.json, and now the synchronization of goals is run by one command.

We understand how it works and how to do it yourself.


Why Automate Goals

Reproducible. The list of goals lives in the code next to the project. The new developer clones the repository, runs the script, and the counter is configured. “Who remembers what we were tracking?”.

Resynchronization. If the counter is off, you need to start a new one or copy the targets to another counter - run the script with another COUNTER_ID and is ready.

Versioning. The list of goals in the git repository is visible when you add a new goal, who and why.

Duplicate protection. The script first requests current targets and creates only those that do not yet exist.


How it works: diagram

plaintext
npm run metrica:goals
♥
¶
The script reads YANDEX METRICA TOKEN and COUNTER ID from .env
♥
¶
GET /management/v1/counter/{id}/goals ← Current Metric Goals
♥
¶
Comparison with the desired list of goals in the code
♥
The goal is already to miss out.
← POST /management/v1/counter/{id}/goals/

Script **never deletes existing targets - only adds new ones. It is safe to run again as many times as you want.


Step 1. Get OAuth-token Yandex

To work with the Management API, you need an OAuth token. This is not a login / password, but a special key for the API.

Register the application

  1. Open oauth.yandex.ru
  2. Click "Register a new application"
  3. It’s called “Metrica Goals Sync.”
  4. Platform — “Web services”
  5. Redirect URI - https://oauth.yandex.ru/verification_code
  6. Accesses - find the section "Yandex.Metrica", turn on metrika:write

After registration, you will receive client_id.

Get a token

Open in your browser:

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

Log in, allow access - a token of the type y0_AgAAAA... will appear in the address bar. Copy it.

Save it to .env

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

The counter number is visible in the URL when you open the counter in the Metrics interface.


Step 2. Write a synchronization script

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);
});

Step 3. Add a command to package.json

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

Now the synchronization starts like this:

bash
npm run metrica:goals

Step 4. Send events from pages

Targets in Metric are only "traps." For them to work, you need to send events from the site through ym().

Make sure the meter is installed

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>

Sending an event

javascript
// Syntax
ym (CoUNTER ID, 'reachGoal', 'goal name')

Examples: The meaning of 'goal name' should be the same
// with conditions[0].value in your script
ym(12345678, 'reachGoal', 'article open');
ym(12345678, 'reachGoal', 'cta click');
ym(12345678, 'reachGoal', 'search');

Real examples of integration

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);
}

Full scheme of work

plaintext
The user opened the article
♥
¶
useEffect → ym(id, 'reachGoal', 'article open')
♥
¶
Yandex. Metrica counted the goal of "opening the article"
♥
¶
Seen in Metrics reports → Conversions → Goals

All that connects the client code to the Metric settings is the 'article_open' string. That is why it is important that the value in conditions[0].value in the script is the same as what you transmit to reachGoal().


Target types in the API

action is a JavaScript event via reachGoal. But the API supports other types:

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

For url and request, the condition type will be different:

javascript
// Target for URL
{
name: "A page of gratitude,"
type: 'url',
conditions: [{type: 'contain', value: '/thank-you'}],
}

// Purpose of downloading the file
{
name: "Price list downloaded,"
type: 'request',
conditions: [{type: 'contain', value: 'price-list.pdf'}],
}

Launch with clear variables

If .env is not configured or you need to run for another counter:

bash
# Export variables from .env and run
set -a && source.env && set +a
YANDEX METRICA COUNTER ID=other counter npm run metrica:goals

# Or transfer variables directly.
YANDEX METRICA TOKEN=YANDEX METRICA COUNTER ID=123456 npm run metrica:goals

What to add to .gitignore

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

The yandex-metrica-goals.mjs script itself - you can and should commit: there are no secrets in it, only logic and a list of goals.


Outcome

Automating goals through the API solves three problems at once: reproducibility (a list of goals in the code), restart security (no duplicates, no deletions), and speed (seconds instead of manually configuring through the interface).

The scheme is simple: the script knows what goals should be, asks the Metric what are, makes a difference. The site sends events via ym(id, 'reachGoal', 'название'). The metric counts conversions.

Once configured – and no longer think about manually setting goals when changing the counter or adding a new project.

$ cd ../ ← back to SEO and promotion