UTM без хаосу: як перевіряти рекламні посилання в Google Sheets до запуску кампанії
UTM-помилки рідко видно в момент запуску кампанії. Реклама вже крутиться, лист уже пішов у базу, партнер уже поставив посилання, а проблема проявляється пізніше: трафік розбився на facebook і Facebook, email-розсилка прийшла як referral, частина кліків потрапила в (not set), а landing page виявився недоступним.

Це не проблема аналітики. Це проблема pre-launch контролю. Якщо команда регулярно запускає PPC, email, affiliate або промо-кампанії, UTM треба перевіряти до старту, а не розбирати після того, як бюджет уже витрачений. Найпростіший робочий шар для такого контролю - Google Sheets з правилами, довідниками й Apps Script.
Google Analytics описує UTM-параметри як спосіб передати campaign data через destination URL. Для стабільної атрибуції важливі щонайменше utmsource, utmmedium і utm_campaign, а значення параметрів чутливі до регістру. Це означає, що Meta і meta можуть роз'їхатися в різні значення у звітах. Тому UTM governance починається не з красивого builder, а з правил, які не дають випустити неправильне посилання.
Які помилки треба ловити до запуску
Для першої версії не потрібна складна система approval. Достатньо перевіряти помилки, які реально псують дані або запуск.
Типові проблеми:
- порожній
utmsource,utmmediumабоutm_campaign; - різний регістр:
Google,google,GOOGLE; - зайві пробіли в назвах кампаній;
- medium не з довідника:
paid,socialpaid,ppcзамість затвердженогоcpcабоpaidsocial; - однаковий final URL для різних рядків без відмінності в
utm_content; - landing page повертає 404, 500 або редіректить на неочікувану сторінку;
- у назві кампанії є кирилиця, пробіли або символи, які команда не хоче бачити в аналітиці;
- у посиланні вже є старі UTM, і нові параметри накладаються поверх них.
Окремо треба контролювати дублікати. Два рядки можуть мати різні назви в таблиці, але збирати той самий final URL. Для аналітики це один і той самий тегований клік, а для команди - майбутній спір про те, яка креативна версія спрацювала.
Структура таблиці
Базова вкладка може називатися Campaign_Links. Один рядок - одне посилання, яке піде в рекламу, email, банер або партнерський матеріал.

Практичний набір колонок:
| Поле | Для чого потрібне |
|---|---|
base_url | чистий landing page без UTM |
source | платформа або джерело |
medium | канал трафіку |
campaign | назва кампанії |
content | креатив, placement або варіант листа |
term | keyword або audience, якщо потрібно |
final_url | зібране посилання |
http_status | відповідь landing page |
validation_status | ready, warning або blocked |
notes | що треба виправити |
Другу вкладку краще зробити довідником. Наприклад, Naming_Rules:
| Rule | Allowed values |
|---|---|
source | google, meta, linkedin, newsletter, partner |
medium | cpc, paid_social, email, affiliate, display |
campaign_pattern | lowercase, hyphen або underscore, без пробілів |
Так таблиця стає не просто builder, а контрольним шаром. Людина вводить значення, а система одразу показує, що посилання можна запускати, треба перевірити або не можна випускати.
Warning і blocked - це різні речі
Головна помилка UTM-таблиць - всі проблеми фарбують одним кольором. Через це команда або ігнорує попередження, або блокує запуск через дрібниці.
blocked:
- немає
utmsource,utmmediumабоutm_campaign; base_urlне відкривається;- landing page повертає 4xx або 5xx;
- final URL дублює інший активний рядок;
mediumне входить у затверджений довідник;- у final URL є два набори UTM-параметрів.
warning:
- landing page редіректить, але final target правильний;
utm_contentпорожній для кампанії з кількома креативами;- campaign name формально валідний, але не збігається з naming-патерном команди;
- посилання дуже довге;
- у
termабоcontentвикористані динамічні макроси рекламної системи, які треба перевірити вручну.
Різниця проста: blocked зупиняє запуск, warning змушує людину подивитися на рядок. Не треба зупиняти email-кампанію через відсутній utmcontent, якщо там одне посилання. Але не можна запускати платну кампанію без utmcampaign, бо потім дані доведеться відновлювати вручну.
Архітектура pre-launch QA
Базова схема така:
Campaign brief -> Google Sheets UTM table -> Apps Script validation -> final URL -> launch approval

Маркетолог або PPC-спеціаліст заповнює рядки. Apps Script нормалізує значення, збирає final URL, перевіряє статус landing page через UrlFetchApp, шукає дублікати й записує статус. Менеджер дивиться тільки на рядки, де є blocked або warning.
Для посилань корисно тримати поруч офіційні правила Google Analytics про URL builders і UTM parameters, довідку про manual tagging, а для технічної перевірки - документацію Apps Script по UrlFetchApp і SpreadsheetApp.
Базовий Apps Script
Це не production-система з approval, а мінімальний каркас: взяти рядки з таблиці, нормалізувати поля, зібрати final URL, перевірити landing page і записати статус.
function validateCampaignLinks() {
const ss = SpreadsheetApp.getActive();
const sheet = ss.getSheetByName('Campaign_Links');
const values = sheet.getDataRange().getValues();
const headers = values.shift();
const rows = values.map(row => toObject_(headers, row));
const seen = new Set();
const output = rows.map(row => {
const result = validateRow_(row, seen);
return [
result.finalUrl,
result.httpStatus,
result.status,
result.notes.join('; ')
];
});
if (output.length) {
sheet.getRange(2, 7, output.length, 4).setValues(output);
}
}
function validateRow_(row, seen) {
const notes = [];
let status = 'ready';
const baseUrl = String(row.base_url || '').trim();
const source = normalize_(row.source);
const medium = normalize_(row.medium);
const campaign = normalize_(row.campaign);
const content = normalize_(row.content);
const term = normalize_(row.term);
if (!baseUrl) notes.push('missing base_url');
if (!source) notes.push('missing source');
if (!medium) notes.push('missing medium');
if (!campaign) notes.push('missing campaign');
const allowedMedium = ['cpc', 'paid_social', 'email', 'affiliate', 'display'];
if (medium && !allowedMedium.includes(medium)) {
notes.push('medium is not allowed');
}
if (campaign && !/^[a-z0-9_-]+$/.test(campaign)) {
notes.push('campaign naming warning');
}
const finalUrl = baseUrl ? buildUrl_(baseUrl, {
utm_source: source,
utm_medium: medium,
utm_campaign: campaign,
utm_content: content,
utm_term: term
}) : '';
if (finalUrl && seen.has(finalUrl)) {
notes.push('duplicate final_url');
}
if (finalUrl) seen.add(finalUrl);
const httpStatus = baseUrl ? checkLanding_(baseUrl) : '';
if (httpStatus >= 400 || notes.some(note => note.startsWith('missing') || note === 'medium is not allowed' || note === 'duplicate final_url')) {
status = 'blocked';
} else if (httpStatus >= 300 || notes.length) {
status = 'warning';
}
return { finalUrl, httpStatus, status, notes };
}
function buildUrl_(baseUrl, params) {
const parts = [];
Object.keys(params).forEach(key => {
const value = params[key];
if (value) {
parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
}
});
const separator = baseUrl.indexOf('?') === -1 ? '?' : '&';
return baseUrl + separator + parts.join('&');
}
function checkLanding_(url) {
try {
const response = UrlFetchApp.fetch(url, {
followRedirects: false,
muteHttpExceptions: true
});
return response.getResponseCode();
} catch (error) {
return 0;
}
}
function normalize_(value) {
return String(value || '')
.trim()
.toLowerCase()
.replace(/\s+/g, '_');
}
function toObject_(headers, row) {
return headers.reduce((acc, header, index) => {
acc[String(header).trim()] = row[index];
return acc;
}, {});
}У цьому прикладі baseurl перевіряється без UTM, а finalurl збирається окремо. Так легше відрізнити проблему сторінки від проблеми тегування. Якщо landing page уже містить query string, скрипт додає параметри через &, а не ламає URL другим знаком ?.
Довідники важливіші за формули
UTM governance працює тільки тоді, коли команда домовилася про назви. Без довідника таблиця перетворюється на ще одне місце, де кожен пише як звик.
Практичні правила:
sourceмає описувати платформу або реальне джерело, а не внутрішній відділ;mediumмає відповідати каналу, який команда хоче бачити у звітах;campaignкраще тримати коротким і стабільним;contentпотрібен для різних креативів, кнопок, банерів або листів;termне треба заповнювати всюди, якщо keyword-level tracking не використовується.
Для Google Ads з auto-tagging окремо вирішуйте, де потрібні UTM. Якщо команда змішує auto-tagging і manual tagging, правила мають бути явними. Google Analytics зазначає, що при manual tagging значення параметрів потрапляють у traffic-source dimensions, а відсутні параметри можуть давати (not set). Саме тому частково заповнені UTM небезпечніші, ніж здаються.
Як запускати процес без зайвої бюрократії
Добрий процес не змушує маркетолога чекати технічну команду на кожне посилання. Він відсікає очевидні проблеми й залишає людині тільки рішення.
Робочий сценарій:
- Кампанія створюється у вкладці
Campaign_Links. - Скрипт запускається кнопкою або тригером.
- Всі рядки отримують
ready,warningабоblocked. blockedвиправляються до запуску.warningпереглядаються відповідальним за кампанію.- У рекламу, лист або партнерську інструкцію копіюються тільки
readyURL.
Для невеликої команди цього достатньо. Для більшого marketing ops можна додати поле owner, дату approval, launch_date, історію змін і Telegram/email-сповіщення тільки по blocked.
Коли Sheets уже замало
Google Sheets добре закриває pre-launch QA, але не повинна ставати єдиним campaign management system. Якщо у вас сотні кампаній на тиждень, кілька ринків і багато рекламних кабінетів, краще підключати API рекламних платформ, server-side templates або окремий internal tool.
Sheets достатньо, коли:
- команда запускає десятки, а не тисячі посилань;
- головний біль - дисципліна naming, а не масштаб;
- треба швидко прибрати ручні помилки;
- campaign links готуються людьми перед запуском.
Окремий інструмент потрібен, коли:
- посилання генеруються масово;
- є кілька approval layers;
- UTM залежать від продуктового каталогу або CRM;
- потрібна інтеграція з рекламними кабінетами й automatic publishing.
Починати все одно варто з правил у таблиці. Якщо команда не може домовитися про source, medium і campaign naming у Sheets, складніший інструмент тільки швидше розмножить хаос.
Контроль дешевший за відновлення даних
UTM-перевірка не робить маркетинг складнішим. Вона прибирає ситуацію, коли помилку знаходять після запуску, а аналітик потім вручну склеює джерела, пояснює (not set) і відновлює логіку кампаній по пам'яті.
Сильна UTM-таблиця має три властивості: збирає final URL однаково для всієї команди, блокує помилки, які псують дані, і залишає контрольні попередження там, де потрібне людське рішення. Це дешевше, ніж запускати кампанію двічі: спочатку з помилками, потім з поясненнями.
Останні статті

Sitemap під контролем: як автоматично знаходити 404, редіректи й noindex у важливих URL
Sitemap часто сприймають як файл, який один раз налаштували в CMS і згадують про нього тільки під час великого SEO-аудиту. Для невеликого сайту це ще може пройти. Але ко…

Індексація без ручної рутини: як перевіряти нові URL через Search Console API та Google Apps Script
Публікація нової сторінки ще не означає, що вона вже працює в пошуку. На сайті URL може відкриватися, у CMS статус може бути published , у редакційному плані задача може…

Google Sheets + Merchant Center: як автоматично знаходити помилки у фіді до того, як вони вдарять по продажах
Помилки у товарному фіді рідко виглядають драматично в момент, коли вони з'являються. У таблиці просто зник image_link , у частини SKU не оновилася ціна, кілька това…

Search Console під наглядом: як автоматично ловити просідання сторінок через Google Sheets і Telegram
Коли органічний трафік просідає, бізнес рідко дізнається про це в ту саму хвилину. Частіше сценарій інший: кілька днів падають кліки по ключовій сторінці, ще через тижде…

Міграція сайту без просадки: автоматизація карти 301-редиректів
Міграція сайту — це той момент, коли навіть сильний проєкт може втратити роки накопичених SEO-сигналів за кілька днів. Зміна домену, CMS або структури URL виглядає як те…

SEO-звіт на автопілоті: єдиний дашборд GA4 + Search Console у Google Sheets
У більшості команд SEO-дані живуть у трьох різних місцях. Search Console показує кліки, покази та середню позицію. GA4 дає сесії, користувачів і конверсії. Окремо ще існ…