Все материалы
Продуктовая аналитикагайдстарт

UTM-метки: что это, как сделать и не сломать отчёт по каналам

UTM-метки простыми словами: что писать в utm_source, utm_medium и utm_campaign, правила именования, примеры ссылок для Директа и рассылок и разбор меток в SQL.

КейсПрактика14 октября 2026 г.17 мин

В конце месяца маркетолог просит у аналитика простую цифру: сколько регистраций принесла летняя кампания в Директе. В отчёте по источникам у Яндекса четыре строки: yandex / cpc, Yandex / CPC, ya / cpc и yandex%20 / cpc, а ещё один переход из той же кампании записан как источник cpc с каналом yandex. Ответ зависит от того, кто и как сложит эти строки. UTM-метки нужны, чтобы такой вопрос решался одной группировкой. Ниже — что писать в каждый из пяти параметров, как договориться об именах, готовые ссылки для Директа, рассылок и партнёров и как разобрать метки в SQL, чтобы одна заглавная буква не делила канал на части.

Коротко

  • UTM-метки — параметры в конце ссылки (?utm_source=…&utm_medium=…), по которым система аналитики понимает, откуда пришёл посетитель. Страница от них не меняется.
  • Стандартных параметров пять: utm_source (откуда), utm_medium (тип трафика), utm_campaign (кампания), utm_content (объявление или ссылка), utm_term (ключевая фраза). Первые три ставятся всегда.
  • Значения чувствительны к регистру: и Google Analytics, и Яндекс Метрика считают Yandex и yandex разными источниками. Пишите строчной латиницей, слова соединяйте подчёркиванием.
  • Для аналитика метка — ключ для GROUP BY. Без общего словаря значений отчёт по каналам приходится чинить руками каждый месяц.
  • В данных продукта метку сохраняют при первом заходе и не перезаписывают; в учебной базе «Маяк» это готовый столбец users.channel.
  • Перед запуском кампании кликните по размеченной ссылке и проверьте, что параметры дожили до адресной строки после всех перенаправлений.

Что такое UTM-метки и зачем они аналитику

UTM-метка — это пара «параметр=значение» в адресе ссылки после знака вопроса: https://mayak.example/?utm_source=yandex&utm_medium=cpc&utm_campaign=summer_sale. Открывается та же страница, что и без хвоста, но счётчик на сайте — Яндекс Метрика, Google Analytics или ваш собственный сборщик событий — читает параметры и записывает визит с этими значениями. Название восходит к Urchin — системе веб-аналитики, которую Google купил в 2005 году и превратил в Google Analytics.

Зачем метки, если счётчик и так видит переходы с поиска и других сайтов? Без меток источник определяется по рефереру — адресу страницы, откуда пришёл человек. Реферер не скажет, какая из трёх кампаний в Директе сработала, не отличит две кнопки в одном письме, а у переходов из почтовых программ и мессенджеров его часто нет совсем — такие визиты оседают в прямых заходах.

Для аналитика метка — ключ, по которому потом группируют регистрации, оплаты и расходы. Стоимость привлечения клиента, доля рекламных расходов, удержание по каналам — все эти расчёты начинаются с group by source, medium. Если ключ грязный, ошибка расходится по всем производным метрикам и при этом не бросается в глаза: строки отчёта просто разъезжаются по разным значениям.

Пять параметров: что писать в utm_source, utm_medium, utm_campaign, utm_content и utm_term

Названия и смысл параметров одинаковы в Google Analytics и Яндекс Метрике. Справка Google Analytics советует всегда ставить source, medium и campaign; справка Директа называет обязательными те же три, а content и term — необязательными. Метрика формально мягче: utm_source в ней обязателен для рекламных систем и соцсетей, utm_medium — для рассылок, мессенджеров и QR-кодов. Для аналитика правило проще: три первых параметра ставятся всегда, иначе в отчёте появляются пустые значения — (not set) в терминах Google Analytics.

Пять стандартных UTM-параметров
ПараметрНа какой вопрос отвечаетЧто писатьПримеры
utm_sourceОткуда пришёл человекплощадка или отправитель: рекламная система, соцсеть, рассылка, партнёрyandex, google, vk, newsletter, partner_blog
utm_mediumКакой это тип трафиказначение из короткого словаря каналовcpc, email, paid_social, partner, qr
utm_campaignКакая кампанияакция, продукт или промокод; одно имя на всю кампанию во всех источникахsummer_sale, august_digest, pro_launch_202609
utm_contentКакое объявление или какая ссылкавариант креатива или место ссылки, чтобы различить две кнопки в одном письмеad_101, button_top, video_15s
utm_termПо какой фразе показали объявлениеключевая фраза платного поиска; в Директе подставляется автоматически{keyword}, report_tool

Чем utm_source отличается от utm_medium

Это самая частая путаница в разметке. Source — конкретное место: yandex, vk, newsletter. Medium — категория, к которой это место относится: cpc, email, paid_social. Проверка простая: если завтра появится вторая рекламная система, у неё будет другой source, но тот же medium.

Отсюда и роль в отчётах. Отчёт по каналам строится по medium, отчёт по площадкам внутри канала — по source, сравнение кампаний — по campaign. Перепутанные параметры не просто дают неверную подпись: визит уходит в чужой канал, и в итоговой таблице его уже не найти.

В Google Analytics есть и дополнительные параметры, например utm_id — идентификатор кампании для загрузки расходов. Если вы не загружаете расходы в Google Analytics, хватает пяти стандартных.

Как называть значения: правила разметки для всей команды

Значения в метках чувствительны к регистру. Справка Google Analytics приводит в пример google и Google как два разных источника, справка Метрики — vkontakte и Vkontakte. Обе советуют нижний регистр, справка Директа добавляет латиницу и разделители _ или -. Кириллица в адресе кодируется в UTF-8: слово «обзор» превращается в %D0%BE%D0%B1%D0%B7%D0%BE%D1%80, ссылка удлиняется, а в выгрузке остаётся нечитаемая строка.

Правила стоит записать в одном месте — в таблице, из которой маркетологи собирают ссылки, — а не держать в голове у одного человека. Минимальный набор такой:

Конвенция именования UTM-меток
ПравилоКак писатьКак не писатьПочему
Регистрyandex, summer_saleYandex, Summer_Saleсистемы аналитики считают их разными значениями
Разделитель словодин на всю команду: summer_salesummer sale, summer-sale вперемешку с summer_saleпробел кодируется в %20 или +, смешение разделителей раскалывает строку
Алфавитлатиница по одному правилу транслитерации: obzorобзоркириллица кодируется и в выгрузке становится нечитаемой
Пробелы по краямyandexyandex (скопировано из таблицы)невидимый хвост превращается в yandex%20
Даты в названииpro_launch_202609запуск 01.09формат ггггмм сортируется и не содержит точек и пробелов
Значения mediumтолько из словаряcpc, CPC, ppc, context для одного каналапо medium строится отчёт по каналам
  • cpc — оплата за клик в поисковой и контекстной рекламе: Директ, Google Ads.
  • paid_social — платная реклама в соцсетях.
  • email — письма и рассылки, включая триггерные.
  • partner — размещения у партнёров и блогеров по договорённости.
  • referral — реферальная программа «приведи друга».
  • banner — медийные размещения с оплатой за показы.
  • qr — офлайн: листовки, упаковка, стенды на конференциях.

Словарь medium и стандартные каналы GA4

Словарь выше — пример для небольшого продукта, а не стандарт: важнее, чтобы он был один и его соблюдали. Если отчёты смотрят в GA4, сверьте словарь с его стандартной группировкой каналов, иначе часть трафика уйдёт в «прочее». Письмо попадает в канал Email, только если source или medium равен email, e-mail, e_mail или e mail. Платный поиск требует источник из списка поисковых систем и medium, подходящий под шаблон ^(.*cp.*|ppc|retargeting|paid.*)$.

Тонкость: группировка каналов в GA4 регистр не различает, а сами значения source и medium в отчётах — различают. Поэтому Email попадёт в канал Email, но в таблице по источникам будет отдельной строкой рядом с email. Нормализовать значения всё равно придётся.

Как сделать UTM-метку: примеры готовых ссылок

Метку собирают вручную, в генераторе ссылок (у Google есть Campaign URL Builder) или в общей таблице, где формула склеивает адрес из выпадающих списков. Таблица надёжнее: словарь medium зашит в список, и опечатке негде появиться.

Синтаксис короткий. Первый параметр идёт после ?, остальные — через &. Если в адресе уже есть параметр, например ?ref=1, метки добавляют через &, второй знак вопроса не нужен. Якорь # ставится в самом конце, после меток: всё, что после решётки, браузер на сервер не передаёт.

codeПримеры ссылок с UTM-метками для разных каналов
# Директ, поисковая кампания: значения в фигурных скобках подставит Директ
https://mayak.example/?utm_source=yandex&utm_medium=cpc&utm_campaign={campaign_id}&utm_content={ad_id}&utm_term={keyword}

# Рассылка: две кнопки в одном письме различает utm_content
https://mayak.example/pricing?utm_source=newsletter&utm_medium=email&utm_campaign=august_digest&utm_content=button_top
https://mayak.example/pricing?utm_source=newsletter&utm_medium=email&utm_campaign=august_digest&utm_content=button_footer

# Реклама во VK
https://mayak.example/?utm_source=vk&utm_medium=paid_social&utm_campaign=summer_sale&utm_content=video_15s

# Обзор у партнёра
https://mayak.example/?utm_source=partner_blog&utm_medium=partner&utm_campaign=obzor_202608

# Листовка на конференции, ссылка зашита в QR-код
https://mayak.example/?utm_source=conf_leaflet&utm_medium=qr&utm_campaign=analytics_conf_202609

# В адресе уже есть параметр, якорь стоит последним
https://mayak.example/?ref=1&utm_source=newsletter&utm_medium=email&utm_campaign=august_digest#pricing

UTM-метки для Яндекс Директа: динамические параметры

В Директе значения не обязательно вписывать руками. Справка Директа описывает динамические параметры — шаблоны в фигурных скобках, вместо которых при клике подставляются настоящие значения. В первой ссылке из примеров {campaign_id} станет номером кампании, {ad_id} — номером объявления, {keyword} — фразой, по которой его показали.

Номер кампании в utm_campaign надёжнее названия. Название переименуют посреди месяца, и одна кампания разъедется на две строки, а номер останется прежним. Названия подтягивают из выгрузки Директа по номеру. Если нужна читаемость прямо в Метрике, есть {campaign_name_lat}, но риск переименования остаётся.

Две особенности из справки Директа прямо касаются отчётов. Первая: при клике utm_source=yandex иногда сокращается до utm_source=ya, поэтому в словаре нормализации нужна строка ya → yandex. Вторая: кириллица в ссылке кодируется, и если адрес вырастет больше 4096 байт, значения передадутся только для меток yclid и openstat. Сам Директ передаёт yclid — идентификатор клика для Метрики; UTM-метки нужны продуктовой базе, CRM и системам, которые yclid не читают.

Динамические параметры Директа, полезные в UTM-метках
ПараметрЧто подставится
{campaign_id}номер кампании
{campaign_name_lat}название кампании транслитом; {campaign_name} — как есть
{gbid}номер группы объявлений
{ad_id}номер объявления
{keyword}ключевая фраза, по которой показано объявление
{source_type}search на поиске, context в сетях
{source}площадка в сетях; на поиске — none
{device_type}desktop, mobile или tablet
{region_name}регион показа

Как метка попадает в данные продукта

Метрика хранит метку у визита и, по справке, берёт её из первого просмотра в визите. Продуктовой базе нужно другое — канал у пользователя, чтобы считать по нему регистрации, оплаты и удержание. Обычная схема: при первом заходе параметры из адреса сохраняются в cookie или локальном хранилище браузера, при регистрации записываются в строку пользователя и дальше не меняются. Это первое касание — канал, который впервые привёл человека, а не тот, с которого он пришёл перед оплатой. Как выбор правила меняет итоговые цифры по каналам, разобрано в статье о моделях атрибуции.

В учебной базе «Маяк» такой столбец уже есть — users.channel. Сырых адресов в ней нет, поэтому разбор показан на учебной вставке: двенадцать адресов страниц входа, какими их видит сборщик событий. В них собраны типичные ошибки: заглавные буквы, сокращение ya, пробел в конце значения, письмо без utm_medium, перепутанные source и medium, кириллица в названии кампании.

Для извлечения подходит split_part — он одинаково работает в DuckDB и PostgreSQL. Сначала из адреса вырезается строка запроса: всё между ? и #. Её обрамляют амперсандами, чтобы искать &utm_source=, а не просто utm_source=: иначе совпал бы и параметр с именем вроде xutm_source. Если параметра нет, split_part возвращает пустую строку, и nullif превращает её в NULL. Вариант на регулярных выражениях и различия функций между движками — в статье про регулярные выражения в SQL, сам split_part — в статье про строковые функции.

Результат запроса: метки как есть, без чистки
visit_idutm_sourceutm_mediumutm_campaignutm_contentutm_term
1yandexcpcsummer_salead_101report_tool
2yandexcpcsummer_salead_102NULL
3YandexCPCSummer_SaleNULLNULL
4yacpcsummer_saleNULLNULL
5yandex%20cpcsummer_saleNULLNULL
6newsletteremailaugust_digestNULLNULL
7newsletterNULLaugust_digestNULLNULL
8cpcyandexsummer_saleNULLNULL
9vkpaid_socialsummer_saleNULLNULL
10partner_blogpartner%D0%BE%D0%B1%D0%B7%D0%BE%D1%80NULLNULL
11NULLNULLNULLNULLNULL
12newsletteremailaugust_digestbutton_topNULL
Учебная вставка: пять UTM-параметров из адреса страницы входа
with raw_visits(visit_id, landing_url) as (
  values
    (1,  'https://mayak.example/?utm_source=yandex&utm_medium=cpc&utm_campaign=summer_sale&utm_content=ad_101&utm_term=report_tool'),
    (2,  'https://mayak.example/pricing?utm_source=yandex&utm_medium=cpc&utm_campaign=summer_sale&utm_content=ad_102'),
    (3,  'https://mayak.example/?utm_source=Yandex&utm_medium=CPC&utm_campaign=Summer_Sale'),
    (4,  'https://mayak.example/?utm_source=ya&utm_medium=cpc&utm_campaign=summer_sale'),
    (5,  'https://mayak.example/?utm_source=yandex%20&utm_medium=cpc&utm_campaign=summer_sale'),
    (6,  'https://mayak.example/?utm_source=newsletter&utm_medium=email&utm_campaign=august_digest'),
    (7,  'https://mayak.example/?utm_source=newsletter&utm_campaign=august_digest'),
    (8,  'https://mayak.example/?utm_source=cpc&utm_medium=yandex&utm_campaign=summer_sale'),
    (9,  'https://mayak.example/?utm_source=vk&utm_medium=paid_social&utm_campaign=summer_sale#pricing'),
    (10, 'https://mayak.example/?utm_source=partner_blog&utm_medium=partner&utm_campaign=%D0%BE%D0%B1%D0%B7%D0%BE%D1%80'),
    (11, 'https://mayak.example/blog/sql-regexp/'),
    (12, 'https://mayak.example/?utm_source=newsletter&utm_medium=email&utm_campaign=august_digest&utm_content=button_top')
),
query_strings as (
  -- всё между «?» и «#», обрамлённое амперсандами
  select visit_id,
         '&' || split_part(split_part(landing_url, '#', 1), '?', 2) || '&' as qs
  from raw_visits
)
select visit_id,
       nullif(split_part(split_part(qs, '&utm_source=', 2), '&', 1), '')   as utm_source,
       nullif(split_part(split_part(qs, '&utm_medium=', 2), '&', 1), '')   as utm_medium,
       nullif(split_part(split_part(qs, '&utm_campaign=', 2), '&', 1), '') as utm_campaign,
       nullif(split_part(split_part(qs, '&utm_content=', 2), '&', 1), '')  as utm_content,
       nullif(split_part(split_part(qs, '&utm_term=', 2), '&', 1), '')     as utm_term
from query_strings
order by visit_id;

Как одна заглавная буква делит канал на несколько строк

Теперь сгруппируем те же визиты по source и medium — так, как это делает любой отчёт по источникам. Пустые значения подписаны (not set), как в Google Analytics.

Результат запроса: десять строк на двенадцать визитов
utm_sourceutm_mediumvisits
newsletteremail2
yandexcpc2
(not set)(not set)1
YandexCPC1
cpcyandex1
newsletter(not set)1
partner_blogpartner1
vkpaid_social1
yacpc1
yandex%20cpc1
Учебная вставка: отчёт по источникам на сырых метках
with raw_visits(visit_id, landing_url) as (
  values
    (1,  'https://mayak.example/?utm_source=yandex&utm_medium=cpc&utm_campaign=summer_sale&utm_content=ad_101&utm_term=report_tool'),
    (2,  'https://mayak.example/pricing?utm_source=yandex&utm_medium=cpc&utm_campaign=summer_sale&utm_content=ad_102'),
    (3,  'https://mayak.example/?utm_source=Yandex&utm_medium=CPC&utm_campaign=Summer_Sale'),
    (4,  'https://mayak.example/?utm_source=ya&utm_medium=cpc&utm_campaign=summer_sale'),
    (5,  'https://mayak.example/?utm_source=yandex%20&utm_medium=cpc&utm_campaign=summer_sale'),
    (6,  'https://mayak.example/?utm_source=newsletter&utm_medium=email&utm_campaign=august_digest'),
    (7,  'https://mayak.example/?utm_source=newsletter&utm_campaign=august_digest'),
    (8,  'https://mayak.example/?utm_source=cpc&utm_medium=yandex&utm_campaign=summer_sale'),
    (9,  'https://mayak.example/?utm_source=vk&utm_medium=paid_social&utm_campaign=summer_sale#pricing'),
    (10, 'https://mayak.example/?utm_source=partner_blog&utm_medium=partner&utm_campaign=%D0%BE%D0%B1%D0%B7%D0%BE%D1%80'),
    (11, 'https://mayak.example/blog/sql-regexp/'),
    (12, 'https://mayak.example/?utm_source=newsletter&utm_medium=email&utm_campaign=august_digest&utm_content=button_top')
),
query_strings as (
  select visit_id,
         '&' || split_part(split_part(landing_url, '#', 1), '?', 2) || '&' as qs
  from raw_visits
),
tags as (
  select visit_id,
         nullif(split_part(split_part(qs, '&utm_source=', 2), '&', 1), '') as utm_source,
         nullif(split_part(split_part(qs, '&utm_medium=', 2), '&', 1), '') as utm_medium
  from query_strings
)
select coalesce(utm_source, '(not set)') as utm_source,
       coalesce(utm_medium, '(not set)') as utm_medium,
       count(*) as visits
from tags
group by 1, 2
order by visits desc, utm_source, utm_medium;

Что видно в сыром отчёте

Пять переходов из одной кампании в Директе разошлись по четырём строкам: yandex / cpc — два, а Yandex / CPC, ya / cpc и yandex%20 / cpc — по одному. Кто посмотрит только на верхнюю строку, увидит два визита вместо пяти и занизит платный поиск в 2,5 раза.

Восьмой визит хуже: перепутанные параметры дали источник cpc и канал yandex. В отчёте по каналам он окажется в отдельной строке, которую никто не свяжет с Директом. Седьмой — письмо без utm_medium — выпадает из канала email. При этом общее число визитов верное, и сверка «итог совпал с Метрикой» ошибку не ловит. Похожие ловушки, когда метрика честно посчитана, но врёт из-за данных, собраны в статье о том, почему метрика обманывает.

Как нормализовать метки и свести их к каналам

Чистка идёт в три шага. Первый — механический: replace убирает закодированный пробел %20, trim — обычные пробелы по краям, lower приводит к нижнему регистру. Второй — словарь: известные синонимы сводятся к одному значению, ya становится yandex. Третий — правило канала: из пары source и medium получается одно значение для отчёта.

Важно, куда попадает то, что правила не узнали. В запросе ниже это отдельная строка «не распознано» с номерами визитов, а не тихое «прочее». Письмо без medium и визит с перепутанными параметрами видны сразу, и их можно исправить в источнике — в шаблоне рассылки и в настройках объявления. Отнести utm_source=newsletter к email без medium запрос может, но такое решение стоит записать в словарь явно, а не прятать внутрь case.

Результат запроса: шесть групп вместо десяти строк
channelvisitsvisit_ids
paid_search51, 2, 3, 4, 5
email26, 12
не распознано27, 8
paid_social19
partner110
без меток111
Учебная вставка: нормализация меток и словарь каналов
with raw_visits(visit_id, landing_url) as (
  values
    (1,  'https://mayak.example/?utm_source=yandex&utm_medium=cpc&utm_campaign=summer_sale&utm_content=ad_101&utm_term=report_tool'),
    (2,  'https://mayak.example/pricing?utm_source=yandex&utm_medium=cpc&utm_campaign=summer_sale&utm_content=ad_102'),
    (3,  'https://mayak.example/?utm_source=Yandex&utm_medium=CPC&utm_campaign=Summer_Sale'),
    (4,  'https://mayak.example/?utm_source=ya&utm_medium=cpc&utm_campaign=summer_sale'),
    (5,  'https://mayak.example/?utm_source=yandex%20&utm_medium=cpc&utm_campaign=summer_sale'),
    (6,  'https://mayak.example/?utm_source=newsletter&utm_medium=email&utm_campaign=august_digest'),
    (7,  'https://mayak.example/?utm_source=newsletter&utm_campaign=august_digest'),
    (8,  'https://mayak.example/?utm_source=cpc&utm_medium=yandex&utm_campaign=summer_sale'),
    (9,  'https://mayak.example/?utm_source=vk&utm_medium=paid_social&utm_campaign=summer_sale#pricing'),
    (10, 'https://mayak.example/?utm_source=partner_blog&utm_medium=partner&utm_campaign=%D0%BE%D0%B1%D0%B7%D0%BE%D1%80'),
    (11, 'https://mayak.example/blog/sql-regexp/'),
    (12, 'https://mayak.example/?utm_source=newsletter&utm_medium=email&utm_campaign=august_digest&utm_content=button_top')
),
query_strings as (
  select visit_id,
         '&' || split_part(split_part(landing_url, '#', 1), '?', 2) || '&' as qs
  from raw_visits
),
tags as (
  select visit_id,
         nullif(split_part(split_part(qs, '&utm_source=', 2), '&', 1), '') as utm_source,
         nullif(split_part(split_part(qs, '&utm_medium=', 2), '&', 1), '') as utm_medium
  from query_strings
),
clean as (
  -- шаг 1: закодированный пробел, пробелы по краям, регистр
  select visit_id,
         lower(trim(replace(utm_source, '%20', ' '))) as source,
         lower(trim(replace(utm_medium, '%20', ' '))) as medium
  from tags
),
dictionary as (
  -- шаг 2: синонимы из словаря
  select visit_id,
         case when source = 'ya' then 'yandex' else source end as source,
         medium
  from clean
)
-- шаг 3: правило канала
select case
         when source is null and medium is null then 'без меток'
         when medium = 'cpc' and source in ('yandex', 'google') then 'paid_search'
         when medium = 'email' then 'email'
         when medium = 'paid_social' then 'paid_social'
         when medium = 'partner' then 'partner'
         else 'не распознано'
       end as channel,
       count(*) as visits,
       string_agg(cast(visit_id as varchar), ', ' order by visit_id) as visit_ids
from dictionary
group by 1
order by visits desc, channel;

Что остаётся после чистки и как то же сделать в Python

Платный поиск собрал все пять своих визитов. Без меток остался один заход на статью блога: это органика или прямой заход, и различить их можно только по рефереру, которого в адресе нет. Десятый визит попал в партнёров, но название кампании так и осталось закодированным: split_part ничего не декодирует, а в PostgreSQL встроенной функции для этого нет (в DuckDB есть url_decode). Кириллицу проще не пускать в метки, чем чинить потом в базе.

В Python разбор адреса короче: urllib.parse сам делит строку запроса на параметры и декодирует кириллицу. Тот же словарь даёт те же шесть групп, а кампания десятого визита читается как «обзор». Учтите: parse_qs превращает + в пробел, а SQL-разбор выше — нет.

pythonТо же в Python: разбор адресов через urllib.parse и словарь каналов
from collections import Counter
from urllib.parse import urlsplit, parse_qs

urls = [
    'https://mayak.example/?utm_source=yandex&utm_medium=cpc&utm_campaign=summer_sale&utm_content=ad_101&utm_term=report_tool',
    'https://mayak.example/pricing?utm_source=yandex&utm_medium=cpc&utm_campaign=summer_sale&utm_content=ad_102',
    'https://mayak.example/?utm_source=Yandex&utm_medium=CPC&utm_campaign=Summer_Sale',
    'https://mayak.example/?utm_source=ya&utm_medium=cpc&utm_campaign=summer_sale',
    'https://mayak.example/?utm_source=yandex%20&utm_medium=cpc&utm_campaign=summer_sale',
    'https://mayak.example/?utm_source=newsletter&utm_medium=email&utm_campaign=august_digest',
    'https://mayak.example/?utm_source=newsletter&utm_campaign=august_digest',
    'https://mayak.example/?utm_source=cpc&utm_medium=yandex&utm_campaign=summer_sale',
    'https://mayak.example/?utm_source=vk&utm_medium=paid_social&utm_campaign=summer_sale#pricing',
    'https://mayak.example/?utm_source=partner_blog&utm_medium=partner&utm_campaign=%D0%BE%D0%B1%D0%B7%D0%BE%D1%80',
    'https://mayak.example/blog/sql-regexp/',
    'https://mayak.example/?utm_source=newsletter&utm_medium=email&utm_campaign=august_digest&utm_content=button_top',
]


def tag(params, name):
    # parse_qs декодирует %20 и кириллицу; пустое значение — None
    value = params.get(name, [''])[0].strip().lower()
    return value or None


def channel(source, medium):
    source = 'yandex' if source == 'ya' else source
    if source is None and medium is None:
        return 'без меток'
    if medium == 'cpc' and source in ('yandex', 'google'):
        return 'paid_search'
    if medium in ('email', 'paid_social', 'partner'):
        return medium
    return 'не распознано'


counts = Counter()
for url in urls:
    params = parse_qs(urlsplit(url).query)
    counts[channel(tag(params, 'utm_source'), tag(params, 'utm_medium'))] += 1

print(counts.most_common())
print(tag(parse_qs(urlsplit(urls[9]).query), 'utm_campaign'))
# [('paid_search', 5), ('email', 2), ('не распознано', 2), ('paid_social', 1), ('partner', 1), ('без меток', 1)]
# обзор

Каналы в учебной базе: как выглядит чистый результат

В таблице users «Маяка» разметка уже сведена к четырём каналам — так выглядит результат, когда словарь соблюдается. Перед расчётом по каналам проверьте это запросом: сколько различных значений, нет ли заглавных букв и пробелов, когда каждое значение появилось впервые.

Результат запроса на базе «Маяк»
channelusersshare_pctfirst_signupis_clean
organic174737.92026-06-01true
referral103722.52026-06-01true
paid_search101522.02026-07-01true
partner81417.62026-06-01true
Распределение регистраций по каналам и проверка значений channel
select channel,
       count(*) as users,
       round(cast(100.0 * count(*) / sum(count(*)) over () as numeric(18, 6)), 1) as share_pct,
       min(signup_date) as first_signup,
       channel = lower(trim(channel)) as is_clean
from users
group by channel
order by users desc;

Как читать распределение каналов

Значений ровно четыре, все в нижнем регистре и без пробелов. Органика даёт 37,9% регистраций, рекомендации — 22,5%, платный поиск — 22,0%, партнёры — 17,6%. Столбец first_signup показывает то, что легко пропустить: платный поиск появился только 1 июля, остальные каналы — с 1 июня. Доля за все три месяца его занижает: с 1 июля на платный поиск приходится 1 015 регистраций из 3 571, то есть 28,4%.

Отсюда правило: каналы сравнивают на одинаковом окне, а не за всё время. Как довести такое сравнение от регистраций до выручки и порога цены привлечения, показано в разборе оценки рекламного канала в SQL.

Частые ошибки в UTM-разметке

Метки на внутренних ссылках сайта. Баннер на главной ведёт на страницу тарифов по ссылке с utm_source=main_banner. Счётчик видит в адресе новую метку и может принять клик за новый переход: в зависимости от системы он начнёт новый визит или станет последним касанием перед покупкой. Сборщик событий, который пишет «последнюю увиденную метку», перезапишет канал пользователя, и регистрации, которые привёл Директ, уйдут на ваш собственный баннер. Внутренние переходы отмечают событием клика или отдельным параметром, имя которого не начинается с utm_.

Разный регистр и синонимы. Yandex, yandex и ya — три источника для любой системы.

Пробелы и кириллица. Значение, скопированное из таблицы с пробелом, превращается в %20 или +. Кириллица кодируется в UTF-8, удлиняет ссылку и в выгрузке остаётся нечитаемой, а у Директа при длине адреса больше 4096 байт UTM-значения не передаются вовсе.

Метка теряется при перенаправлении. Ссылка ведёт на адрес с http:// или без www, сервер перенаправляет на основной адрес и отбрасывает строку запроса. Сокращатели ссылок и промежуточные страницы приложений иногда делают то же. Визит записывается как прямой заход. Для JavaScript-перенаправлений Метрика предлагает передавать исходный адрес в параметре utm_referrer. Проверка одна: кликнуть по готовой ссылке и посмотреть адресную строку после загрузки.

**Рассылки без utm_medium=email.** Письмо с utm_source=newsletter без medium не попадёт в канал email ни в вашем словаре, ни в группировке GA4, а в Метрике medium для рассылок обязателен. Без меток вовсе переход из почтовой программы часто выглядит как прямой заход.

Путаница source и medium. utm_source=cpc&utm_medium=yandex или utm_medium=yandex_direct — визит уходит из канала платного поиска. Правило из раздела выше: source — место, medium — категория из словаря.

Как проверять разметку: перед запуском и раз в неделю

Разметка ломается постепенно: новый подрядчик, новая площадка, срочная рассылка, собранная вручную.

  • Перед запуском кликнуть по каждой ссылке, убедиться, что после всех перенаправлений параметры на месте, и найти свой визит в отчёте «Метки UTM» Метрики.
  • Собирать ссылки только из общей таблицы со словарём medium. Ссылка, набранная руками в письме, — главный источник хвостов.
  • Раз в неделю выводить все пары source и medium, не попавшие в словарь, с числом визитов. Новое значение — либо опечатка, которую чинят в ссылке, либо новый источник, который вносят в словарь.
  • Хранить сырые значения меток рядом с нормализованными. Если правило словаря поменяется, канал можно пересчитать задним числом.

Частые вопросы

Что такое UTM-метки простыми словами?

Это подписи в конце ссылки, по которым система аналитики понимает, из какой рекламы, письма или публикации пришёл человек. На содержание страницы они не влияют.

Какие UTM-метки обязательны?

Справка Google Analytics и справка Директа называют обязательными utm_source, utm_medium и utm_campaign. Метрика требует их выборочно, в зависимости от типа источника, но для чистого отчёта по каналам три параметра нужны всегда.

Нужны ли UTM-метки в Директе, если Метрика понимает yclid?

Для отчётов внутри Метрики хватает yclid. Метки нужны всему остальному: продуктовой базе, CRM, сквозной аналитике и другим счётчикам, которые yclid не читают. Динамические параметры заполняют их автоматически.

Можно ли писать значения меток по-русски?

Технически можно: кириллица закодируется в UTF-8. Но ссылка станет длинной, значения в выгрузке — нечитаемыми, а справка Директа советует латиницу. Выберите одно правило транслитерации и держитесь его.

Не повредят ли метки поисковому продвижению?

Адреса с метками — копии одной страницы. Чтобы поисковики не считали их отдельными страницами, укажите канонический адрес (rel=canonical), а для Яндекса — директиву Clean-param в robots.txt.

Итог

UTM-метка нужна не для красоты ссылки, а чтобы через месяц трафик и деньги группировались по каналам без ручной склейки. Для этого хватает пяти параметров, словаря значений medium, нижнего регистра и латиницы, правила первого касания в данных продукта и еженедельной проверки новых значений. Всё, что не попало в словарь, выводится отдельной строкой и чинится в источнике.

Дальше метки превращаются в метрики: стоимость привлечения, удержание и выручку по каналам. Считать их на базе продукта, от регистраций до оплат, учат в Симуляторе SQL-аналитика.

Продолжить чтение
Вся библиотека