Бот получает пользователей из рекламы, постов в каналах, партнёрских ссылок и поиска Telegram. Если не размечать эти источники, через месяц непонятно, что из них работает, а что просто съедает бюджет. В Telegram для этого есть встроенный механизм — ссылки с параметром start. Разберём, как их генерировать, принимать в боте на aiogram 3, хранить и считать, и на каких мелочах легко ошибиться.
Как устроена ссылка с параметром start
Обычная ссылка на бота выглядит как https://t.me/your_bot. Если добавить к ней параметр, получится deep link:
https://t.me/your_bot?start=src_dzen_oct Когда человек открывает такую ссылку и нажимает «Запустить», бот получает сообщение /start src_dzen_oct. Всё, что после пробела, — ваша метка.
Ограничения у параметра такие:
- не длиннее 64 символов;
- только латиница, цифры,
_и-— без пробелов, кириллицы, точек и=.
Если в метку нужно положить что-то сложнее, её кодируют в base64url. aiogram умеет делать это сам, но помните: после кодирования строка становится длиннее примерно на треть, и лимит в 64 символа применяется уже к закодированному значению.
Подробно про форматы ссылок, startgroup для групп и отдельные ссылки для рекламы мы писали в статье как сделать ссылку на Telegram-бота с параметром Start.
Генерируем ссылки
Для простых меток кодирование не нужно — читаемые значения удобнее разбирать в отчётах:
from aiogram import Bot from aiogram.utils.deep_linking import create_start_link SOURCES = ["dzen_oct", "tg_ads_1", "partner_ivan", "seo_blog"] async def make_links(bot: Bot) -> dict[str, str]: return {src: await create_start_link(bot, f"src_{src}") for src in SOURCES} Если метка содержит недопустимые символы, передайте encode=True — aiogram закодирует её в base64url:
link = await create_start_link(bot, "promo:october/2026", encode=True) Принимаем параметр в боте
В aiogram 3 для этого есть фильтр CommandStart(deep_link=True) и объект CommandObject, в котором лежит сам параметр:
from aiogram import Router from aiogram.filters import CommandObject, CommandStart from aiogram.types import Message router = Router() @router.message(CommandStart(deep_link=True)) async def start_with_source(message: Message, command: CommandObject): save_user(message.from_user.id, command.args) await message.answer("Привет! Рад, что вы здесь.") @router.message(CommandStart()) async def start_plain(message: Message): save_user(message.from_user.id, None) await message.answer("Привет! Рад, что вы здесь.") Порядок важен: обработчик с deep_link=True должен стоять раньше обычного /start, иначе метки до него не дойдут. Если вы кодировали метку, раскодируйте её через decode_payload из aiogram.utils.deep_linking.
Храним первый источник пользователя
Для начала хватит SQLite. INSERT OR IGNORE сохраняет только первый источник — так вы не перезапишете метку, если человек потом откроет бота по другой ссылке:
import sqlite3 db = sqlite3.connect("bot.db") db.execute( "CREATE TABLE IF NOT EXISTS users (" " user_id INTEGER PRIMARY KEY," " source TEXT," " first_seen TEXT DEFAULT CURRENT_TIMESTAMP)" ) def save_user(user_id: int, source: str | None) -> None: db.execute( "INSERT OR IGNORE INTO users (user_id, source) VALUES (?, ?)", (user_id, source or "direct"), ) db.commit() Считаем, какой источник работает
Сколько пользователей пришло из каждого источника за последний месяц:
SELECT source, COUNT(*) AS users FROM users WHERE first_seen >= date('now', '-30 days') GROUP BY source ORDER BY users DESC; Число запусков — только половина картины. Добавьте таблицу целевых действий (оплата, заявка, подписка на канал) и считайте конверсию по источникам: часто дешёвый источник приводит много людей, которые ничего не делают дальше. Какие ещё показатели стоит смотреть, мы разобрали в статье про статистику Telegram-бота.
Подводные камни
- Метку видит пользователь. Не кладите в неё ID заказов, промокоды и любые данные, которые нельзя показывать.
- Одна ссылка — один источник. Если одну и ту же ссылку поставить и в рекламу, и в пост, данные смешаются.
- Не все приходят по ссылке. Часть людей найдёт бота через поиск Telegram или по имени — они попадут в
direct, и это нормально. - Лимит считают после кодирования. Длинная метка с
encode=Trueлегко вылезает за 64 символа.
Метрика, которую видят все: monthly users
В профиле бота Telegram показывает, сколько людей пользовались им за месяц. Эту цифру видит каждый, кто открывает бота, и по ней судят, живой ли он. По наблюдениям владельцев ботов, активная аудитория помогает и в поиске Telegram, где боты конкурируют за те же ключевые слова, что и каналы. Пошаговый план мы собрали в статье как продвигать Telegram-бота.
Когда нужно поднять этот счётчик под конкретный ключ, используют Premium Bot Start — запуски бота аккаунтами с Telegram Premium. Удобная деталь для разработчика: заказ принимает ссылку с параметром start. Дайте такому заказу отдельную метку, например ?start=src_premium_test, — и эти пользователи будут в вашей аналитике отдельной строкой, их легко сравнить с остальными источниками. Для простого первого объёма есть обычные старты бота, а прикинуть, сколько Premium-аудитории нужно под ваш ключ, помогает калькулятор поиска Telegram.
Чек-лист
- Для каждого источника — своя ссылка
?start=src_.... - Метки из латиницы, цифр,
_и-, не длиннее 64 символов; сложные — черезencode=True. - Обработчик
CommandStart(deep_link=True)стоит раньше обычного/start. - В базе хранится первый источник пользователя.
- Источники сравниваются по конверсии в целевое действие, а не только по числу запусков.
Больше материалов про продвижение каналов и ботов в Telegram — в блоге BoostTelega.
