Analítica integrada de Telegram
Telegram ofrece herramientas para analizar los resultados de las ubicaciones publicitarias mediante enlaces únicos. Puede crear un enlace distinto para cada ubicación publicitaria. Para canales y grupos, las estadísticas de uniones están disponibles directamente en Telegram, mientras que para bots y Mini Apps es necesario configurar además el guardado de los parámetros del enlace y la recopilación de datos en el lado de su proyecto.
ATENCIÓN
Una ubicación publicitaria = un enlace único. Si se usa el mismo enlace en varios anuncios, las fuentes de tráfico se mezclarán y no se podrán separar a posteriori.
Este tipo de analítica es independiente de las estadísticas de campaña y del seguimiento de conversiones de AdsGram.
Parte 1. Para los responsables de campañas del anunciante
1. ¿Qué enlace usar?
Para comparar los resultados de distintas ubicaciones publicitarias, use un enlace único para cada una.
Por ejemplo, si promociona un proyecto en tres canales de Telegram, cree tres enlaces distintos. Así podrá determinar qué ubicaciones atraen más usuarios.
El tipo de enlace depende del destino del anuncio:
| Destino del anuncio | Formato del enlace | Dónde ver los resultados |
|---|---|---|
| Canal o grupo de Telegram | Un enlace de invitación propio | Telegram: ajustes del canal o grupo → Invite links |
| Bot | t.me/YourBot?start=ad_sep_anna | Su bot, si guarda el parámetro |
| Mini App | t.me/YourBot?startapp=ad_sep_anna | El servidor de su aplicación, si guarda el parámetro |
Sustituya YourBot por el nombre de usuario de su bot y ad_sep_anna por un parámetro único para la ubicación publicitaria.
2. Canales y grupos: enlaces de invitación
Cree un enlace de invitación distinto para cada ubicación publicitaria.
- Abra el canal o grupo y toque su nombre.
- Abra los ajustes de edición o administración. En la versión de escritorio, puede usar ⋮ →
Manage channel/Manage group. - Abra
Invite linksy seleccione la opción para crear un enlace nuevo. - Asigne al enlace el nombre de la ubicación, por ejemplo
ad_sep_anna. El nombre solo lo ven los administradores, no los usuarios que abren el enlace. - Si es necesario, establezca una fecha de caducidad, un límite de usuarios o la aprobación obligatoria por un administrador.
- Use el enlace en una sola ubicación publicitaria.
Dónde ver las estadísticas: la pantalla Invite links de Telegram muestra cuántos usuarios se unieron a través de cada enlace. Abra un enlace concreto para ver la lista de usuarios.
Un enlace público normal como t.me/yourchannel no permite separar las uniones por ubicación publicitaria. Los canales públicos también pueden crear enlaces de invitación, así que use enlaces de invitación distintos en sus anuncios.
3. Bots: enlaces con el parámetro start
Use el siguiente formato de enlace:
https://t.me/<bot>?start=<parameter>Por ejemplo:
https://t.me/YourBot?start=ad_sep_annaCuando un usuario abre el enlace y pulsa Start, el bot recibe un comando con el parámetro:
/start ad_sep_annaTelegram no registra automáticamente la atribución publicitaria de estos inicios del bot. El bot debe guardar el parámetro cuando se crea por primera vez el registro del usuario.
Cuente solo los usuarios nuevos si su métrica es la adquisición de nuevos usuarios. Un usuario existente que abre un enlace publicitario no se convierte en usuario nuevo.
4. Mini Apps: enlaces con el parámetro startapp
Para la Mini App principal de un bot, use:
https://t.me/<bot>?startapp=<parameter>Para una Mini App creada con /newapp en @BotFather, puede usar:
https://t.me/<bot>/<app>?startapp=<parameter>Por ejemplo:
https://t.me/YourBot?startapp=ad_sep_annaSi es necesario, añada un parámetro de modo de visualización:
&mode=compact— modo compacto.&mode=fullscreen— modo de pantalla completa.
La Mini App recibe el parámetro en el campo start_param. El servidor de su aplicación debe guardarlo cuando el usuario inicia la aplicación por primera vez.
Si un usuario abre la Mini App sin un enlace publicitario —por ejemplo, desde el botón de menú o el perfil del bot—, es posible que no haya parámetro publicitario. Considere ese inicio como orgánico, salvo que exista otra fuente de atribución fiable.
5. Reglas de los parámetros
Longitud máxima:
- Parámetro
start— 64 caracteres - Parámetro
startapp— 512 caracteres
- Parámetro
Caracteres permitidos:
A-Z,a-z,0-9,_,-- No use espacios, caracteres cirílicos ni puntos. Para evitar problemas de compatibilidad, limítese al conjunto de caracteres permitido.
No incluya datos personales de los usuarios en los parámetros.
Patrón de nombres recomendado. Use un formato coherente para que las ubicaciones sean fáciles de identificar en los informes:
ad_<month>_<source>_<creative>Ejemplos:
ad_sep_tgads_video1ad_sep_blogger_anna
6. Los enlaces de campaña no son enlaces de referido
Un enlace de referido permite a un usuario invitar a otras personas y recibir una recompensa.
Un enlace publicitario mide el tráfico de una ubicación publicitaria. Por sí solo, no debe generar una recompensa por referido.
Separe ambos tipos con prefijos:
ref_…— enlaces de referido de usuarios.ad_…— enlaces de ubicaciones publicitarias.
Crear un enlace de referido distinto para cada campaña publicitaria es solo una solución temporal. Puede provocar que se paguen recompensas por referido por tráfico publicitario y mezclar las estadísticas.
7. Cómo contar los clics
Telegram no ofrece estadísticas de clics para los enlaces t.me.
Si necesita contar clics, envíe a los usuarios primero a través de su propia redirección y después a Telegram.
Ejemplo:
https://yourdomain.com/go/ad_sep_anna → https://t.me/YourBot?start=ad_sep_annaSu servidor registra el clic y redirige al usuario al enlace de destino. Esto requiere una implementación propia y no sustituye el recuento de inicios, nuevos usuarios ni uniones al canal.
8. Tabla de resultados
Cree una tabla con una fila por cada enlace único.
Columnas recomendadas:
| Campo | Qué registrar |
|---|---|
| Parámetro del enlace | Por ejemplo, ad_sep_anna |
| Ubicación | Nombre del canal, publicación, influencer o anuncio |
| Fechas | Periodo de la ubicación |
| Gasto | Coste de la publicidad |
| Usuarios nuevos | Número de usuarios nuevos atribuidos al enlace |
| Coste por usuario | Gasto ÷ número de usuarios nuevos |
9. Lista de comprobación antes del lanzamiento
Antes de publicar un anuncio, asegúrese de que:
- Cada ubicación tiene su propio enlace; los enlaces no se reutilizan en otras ubicaciones.
- El parámetro no supera la longitud máxima y solo contiene caracteres permitidos.
- El enlace se ha probado con una segunda cuenta: la unión se cuenta o el parámetro se guarda en el bot o la Mini App.
- El enlace, el nombre de la ubicación y el gasto se han añadido a la tabla.
Parte 2. Implementación para desarrolladores
Los ejemplos siguientes usan Python, SQL y aiogram 3 para el bot. Los mismos principios se pueden implementar con otras tecnologías.
El código es una base, no una integración completa lista para usar. Las funciones de acceso a la base de datos y algunos detalles de integración deben adaptarse a su aplicación.
10. Qué datos almacenar
La atribución requiere tres entidades:
- Un registro de enlaces publicitarios y campañas.
- La fuente de adquisición del usuario, guardada al crear su registro.
- Un historial de visitas a enlaces de campaña, incluidas las de usuarios existentes.
Ejemplo de esquema SQL:
-- Una fila por cada enlace publicitario.
-- Por ejemplo, el parámetro "ad_sep_anna" corresponde al código "sep_anna".
CREATE TABLE campaigns (
code text PRIMARY KEY,
name text NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
-- La fuente de adquisición del usuario se establece al crearlo.
-- NULL significa orgánico.
ALTER TABLE users
ADD COLUMN campaign text REFERENCES campaigns (code);
-- Todos los usuarios que abrieron enlaces de campaña,
-- tanto nuevos como existentes.
CREATE TABLE campaign_visits (
code text REFERENCES campaigns (code),
user_id bigint,
first_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (code, user_id)
);El campo code debe contener un código de campaña corto, como sep_anna. El campo name describe la ubicación, por ejemplo, Canal de Anna, publicación de septiembre.
La tabla campaign_visits registra las visitas por separado de la fuente de adquisición original del usuario. La fuente original no debe sobrescribirse cada vez que el usuario abre otro enlace.
11. Analizar los parámetros: un mismo parser para el bot y la Mini App
Use un único parser para los parámetros publicitarios y de referido.
import re
AD = re.compile(r"ad_([A-Za-z0-9_-]{1,61})")
REF = re.compile(r"ref_([A-Za-z0-9]{4,32})")
def parse_start(param: str | None) -> tuple[str, str] | None:
"""("ad", code), ("ref", code) o None en cualquier otro caso."""
if not param:
return None
if m := AD.fullmatch(param):
return "ad", m[1]
if m := REF.fullmatch(param):
return "ref", m[1]
return NoneTras el análisis, compruebe si el código obtenido existe en la tabla campaigns.
- No atribuya un código desconocido a una campaña existente. Considere esa visita como orgánica o no atribuida.
- No guarde valores de parámetros sin procesar ni validar.
- Aplique las mismas reglas de análisis a los parámetros que reciben el bot y la Mini App.
12. Bot: gestionar /start
Cuando un usuario inicia un bot, Telegram pasa el parámetro en los argumentos del comando /start.
Ejemplo con aiogram 3:
@router.message(CommandStart())
async def start(message: Message, command: CommandObject):
source = parse_start(command.args)
# "/start ad_sep_anna" -> ("ad", "sep_anna")
async with db.transaction():
await create_user_if_new(message.from_user, source)
if source and source[0] == "ad":
await record_visit(source[1], message.from_user.id)La función create_user_if_new solo debe crear un registro de usuario si este aún no existe.
Para evitar registros duplicados, use una operación atómica, por ejemplo:
-- create_user_if_new: atómico, seguro cuando el bot y la Mini App consultan a la vez
INSERT INTO users (id, campaign, referrer_id)
VALUES ($1, $2, $3)
ON CONFLICT (id) DO NOTHING
RETURNING id; -- devuelve una fila = usuario nuevo; nada = usuario existente, se deja como estáSi la consulta devuelve una fila, el usuario se ha creado. Si no devuelve ninguna, el usuario ya existe y su fuente de adquisición original debe permanecer sin cambios.
Para registrar una visita a un enlace de campaña, use:
-- record_visit
INSERT INTO campaign_visits (code, user_id)
VALUES ($1, $2)
ON CONFLICT DO NOTHING;Si un usuario existente abre otro enlace y recibe /start ad_…, registre la visita, pero no sobrescriba su campo campaign.
Para la atribución de referidos, establezca referrer_id solo para usuarios nuevos. No permita que los usuarios se refieran a sí mismos.
13. Mini App: gestionar el primer inicio
Una Mini App puede recibir el parámetro start_param como parte de los datos de inicio de Telegram Web App.
En el frontend, envíe a su servidor los datos firmados por Telegram:
// Frontend: cada solicitud incluye los datos de inicialización firmados por Telegram
fetch("/api/me", { headers: { Authorization: `tma ${Telegram.WebApp.initData}` } })En el servidor, valide la autenticidad de initData y su antigüedad antes de usar el ID de usuario o start_param.
Ejemplo de función de validación en Python:
import hashlib, hmac, json, time
from urllib.parse import parse_qsl
def check_init_data(
raw: str,
bot_token: str,
max_age: int = 86400
) -> dict:
fields = dict(
parse_qsl(
raw,
keep_blank_values=True,
strict_parsing=True
)
)
received = fields.pop("hash", "")
# todos los campos excepto hash, incluido "signature", ordenados, un par key=value por línea
data = "\n".join(
f"{k}={v}" for k, v in sorted(fields.items())
)
secret = hmac.new(
b"WebAppData",
bot_token.encode(),
hashlib.sha256
).digest()
expected = hmac.new(
secret,
data.encode(),
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected, received):
raise PermissionError("bad signature")
if time.time() - int(fields["auth_date"]) > max_age:
raise PermissionError("expired")
return {
"user": json.loads(fields["user"]),
"start_param": fields.get("start_param")
}Tras una validación correcta, siga el mismo proceso que en el bot (create_user_if_new y record_visit):
- Obtenga
userystart_paramde los datos validados. - Analice el parámetro con
parse_start. - Cree el usuario solo si aún no existe.
- Registre la visita al enlace de campaña si el parámetro corresponde a una campaña publicitaria conocida.
Seguridad: use solo initData validado. No confíe en initDataUnsafe ni en los parámetros de la URL como fuentes de identidad del usuario o de atribución, porque pueden falsificarse.
El parámetro start_param se proporciona cuando la aplicación se inicia a través del enlace correspondiente. Puede faltar en inicios posteriores desde el menú. Por eso, establezca la fuente de adquisición original al crear el usuario, no en cada inicio de la aplicación.
14. Un usuario, dos puntos de entrada
El bot y la Mini App pueden compartir una única tabla users, en la que cada usuario se identifica por su Telegram user ID.
Por ejemplo, un usuario puede iniciar primero el bot y después abrir la Mini App, o al revés.
Reglas de atribución:
- El primer handler que crea el registro del usuario establece la fuente de adquisición original.
- El segundo handler no debe sobrescribir esa fuente.
- Las visitas posteriores pueden registrarse por separado en
campaign_visits.
Use una inserción atómica, como INSERT … ON CONFLICT DO NOTHING. Esto ayuda a evitar errores si las solicitudes del bot y de la Mini App llegan casi al mismo tiempo.
15. Canales: seguimiento de uniones por enlace de invitación
Si necesita su propio seguimiento de uniones en lugar de depender solo de la interfaz de Telegram, cree un enlace de invitación distinto para cada campaña y guárdelo junto con el código de la campaña.
Ejemplo de creación de enlace en Python:
# Crear un enlace por campaña y guardarlo junto con el código
link = await bot.create_chat_invite_link(
CHANNEL_ID,
name="ad_sep_anna"
) # nombre: 1–32 caracteres
save_channel_link("ad_sep_anna", link.invite_link)En este ejemplo, el nombre del enlace de invitación está limitado a 1–32 caracteres.
Para recibir eventos de unión, el bot debe ser administrador del canal y recibir las actualizaciones chat_member correspondientes. Si las solicitudes de unión requieren aprobación, también debe recibir las actualizaciones chat_join_request.
Ejemplo de configuración de polling:
# Las uniones llegan como actualizaciones chat_member: el bot debe ser administrador y solicitarlas
await dp.start_polling(
bot,
allowed_updates=[
"message",
"chat_member",
"chat_join_request"
]
)Ejemplo de handler de uniones:
@router.chat_member()
async def member(update: ChatMemberUpdated):
joined = (
update.old_chat_member.status in ("left", "kicked")
and update.new_chat_member.status == "member"
)
if joined and update.invite_link:
save_channel_join(
update.invite_link.name,
update.new_chat_member.user.id
)Si un enlace requiere aprobación de un administrador, gestione chat_join_request. El enlace de invitación está disponible en update.invite_link; tras revisar la solicitud, puede llamar a approve_chat_join_request.
También puede registrar las salidas de usuarios del canal para saber quién se quedó.
Telegram ya muestra a los administradores el número de uniones por cada enlace de invitación. Su propia base de datos es útil si quiere combinar los datos de uniones al canal con la información de los usuarios del bot u otra analítica.
16. Opcional: contar clics con una redirección
Para contar los clics antes de que los usuarios lleguen a Telegram, cree su propio endpoint de redirección.
Ejemplo en Python para Mini App:
@app.get("/go/{code}")
async def go(code: str, request: Request):
agent = request.headers.get("user-agent", "")
if code_exists(code) and "TelegramBot" not in agent: # ignorar el rastreador de vistas previas de enlaces de Telegram
record_click(code)
return RedirectResponse(
f"https://t.me/{BOT}?startapp=ad_{code}",
status_code=302
)Este ejemplo valida el código y excluye las solicitudes cuyo User-Agent contiene TelegramBot, para no contar las solicitudes de vista previa de enlaces de Telegram como clics normales de usuarios.
Adapte la implementación a su infraestructura y pruébela con enlaces reales. La redirección debe llevar al enlace correcto del bot o de la Mini App con el parámetro adecuado.
17. Ejemplo de informe
La siguiente consulta muestra, para cada campaña, el número de visitantes únicos de los enlaces de campaña, usuarios nuevos y usuarios activados.
SELECT
c.code,
c.name,
(
SELECT count(*)
FROM campaign_visits v
WHERE v.code = c.code
) AS visitors,
(
SELECT count(*)
FROM users u
WHERE u.campaign = c.code
) AS new_users,
(
SELECT count(*)
FROM users u
WHERE u.campaign = c.code
AND u.first_action_at IS NOT NULL
) AS active
FROM campaigns c
ORDER BY new_users DESC;
-- first_action_at: cuándo el usuario hizo por primera vez la acción por la que paga la campaña (una tarea, una compra)En este ejemplo, first_action_at es el momento en que un usuario completa por primera vez la acción objetivo por la que paga el anunciante, como completar una tarea o realizar una compra.
La métrica visitors representa a los usuarios con una visita registrada a la campaña correspondiente en campaign_visits. No equivale al número de clics en el enlace, salvo que los clics se registren por separado mediante una redirección.
18. Errores habituales y limitaciones
- El bot recibe el parámetro solo cuando el usuario pulsa
Start. No todos los que hacen clic en el enlace inician el bot, por lo que el número de inicios puede ser menor que el de clics. - Reiniciar no significa un usuario nuevo. Si un usuario elimina el chat e inicia el bot de nuevo, el parámetro puede volver a enviarse, pero el usuario no es nuevo.
- No sobrescriba la fuente de adquisición original. Use una tabla de visitas separada con marcas de tiempo para analizar las visitas posteriores.
- Separe las cuentas nuevas de las existentes. Si el objetivo es la adquisición, las visitas repetidas no deben aumentar el número de usuarios nuevos.
- Excluya bots y autorreferidos. Compruebe
is_boty no permita que los usuarios se refieran a sí mismos. - Respete los límites de los parámetros. La longitud total del parámetro
startno debe superar los 64 caracteres. Use códigos cortos, comosep_anna, para dejar espacio al prefijoad_oref_. - Mantenga separadas la atribución de referidos y la publicitaria. Prefijos y reglas de procesamiento distintos ayudan a evitar recompensas e informes incorrectos.