lección 2
Día 1: Tres fuentes, tres verdades
Explora los datos de Google Ads, Meta Ads y Email Marketing. Descubre las discrepancias de granularidad, naming y definición de conversión.
⏱ 45 min
### Martes 9:15 AM — El standup
El standup de AdPulse es rápido: 10 minutos de pie junto a la máquina de café. Carmen va directo al grano con cada persona: "¿Qué hiciste ayer, qué haces hoy, qué te bloquea?" Cuando llega tu turno, Carmen dice: "El nuevo. Hoy exploras las fuentes de PharmaLife. Iván te da acceso a los datos crudos. Para mañana por la mañana quiero un doc de 1 página con las discrepancias que encuentres entre las 3 fuentes." Se gira hacia Iván: "Asegúrate de que tiene acceso a todo, incluyendo el CRM."
Iván te lleva a su escritorio después del standup. "Vamos a lo básico primero. Necesitas entender cómo llegan los datos de cada plataforma. Cada una tiene su formato, su granularidad temporal y su propia definición de lo que es una conversión. Te voy a dar acceso a los 3 datasets de enero para que los explores. No empieces a construir nada todavía — solo mira, perfila y documenta."
### Fuente 1: Google Ads (CSV diario)
Google Ads exporta un CSV diario vía un script que Raúl configuró hace meses con la API de Google Ads. El archivo llega a las 6 AM a un bucket de S3 y tiene granularidad diaria por campaña. Los nombres de campaña siguen la convención interna de PharmaLife tal como los creó el equipo de paid media: prefijo PL_ seguido de tipo de campaña, producto y segmento.
1-- Estructura: google_ads_weekly.csv2-- Granularidad: 1 fila por día por campaña3-- Periodo: enero 2024 (31 días)4-- Campañas activas: 1456SELECT * FROM google_ads LIMIT 5;78-- date | campaign_name | impressions | clicks | conversions | cost_eur9-- 2024-01-01 | PL_Search_Vitamins_Brand | 1567 | 104 | 9 | 559.8910-- 2024-01-01 | PL_Search_Omega3_Generic | 1111 | 58 | 6 | 359.0411-- 2024-01-01 | PL_Display_Protein_Retargeting | 34197 | 161 | 4 | 129.8612-- 2024-01-01 | PL_Shopping_Supplements_All | 1858 | 125 | 10 | 372.1613-- 2024-01-01 | PL_Search_Vitamins_Competitor | 504 | 40 | 3 | 202.921415-- Total filas: 434 (31 días × 14 campañas)16-- Total impresiones enero: 8.814.36017-- Total clics enero: 46.96418-- Total conversiones enero: 2.34019-- Total gasto enero: 110.000 EUR20-- Ventana de atribución Google: clic 30 días
Google Ads: CSV diario, nombres con prefijo PL_, ventana de atribución de 30 días
Iván señala algo importante: "Fíjate en la ventana de atribución. Google cuenta como conversión cualquier compra que ocurra dentro de los 30 días posteriores a un clic. Eso significa que si alguien hace clic el 1 de enero y compra el 28 de enero, Google cuenta esa conversión en el día del CLIC (1 de enero), no en el día de la compra. Eso distorsiona los datos diarios."
### Fuente 2: Meta Ads (JSON de la API)
Meta (Facebook + Instagram) expone una Marketing API que Iván configuró para descargar datos cada noche a las 2 AM. El formato es JSON con estructura anidada — las conversiones vienen dentro de un array "actions" que mezcla diferentes tipos de evento (purchases, add_to_cart, view_content). Los nombres de campaña siguen una convención DIFERENTE a Google porque los creó otro equipo dentro de PharmaLife.
Iván te advierte sobre un detalle crítico del JSON de Meta: "Todos los valores dentro de actions son STRINGS, no números. Meta devuelve todo como texto por un tema de compatibilidad con versiones antiguas de su API. Tienes que castear a int/float explícitamente. Si sumas strings en vez de números, vas a tener bugs silenciosos: 45 + 38 te daría 4538 en vez de 83."
1{2 "data": [3 {4 "campaign_id": "120208345",5 "campaign_name": "pharmalife_vitamins_broad_fb",6 "date_start": "2024-01-01",7 "date_stop": "2024-01-01",8 "impressions": "53032",9 "reach": "38995",10 "clicks": "1276",11 "actions": [12 {"action_type": "purchase", "value": "8"},13 {"action_type": "add_to_cart", "value": "74"},14 {"action_type": "view_content", "value": "256"},15 {"action_type": "initiate_checkout", "value": "47"}16 ],17 "spend": "201.50",18 "cost_per_action_type": [19 {"action_type": "purchase", "value": "25.19"}20 ]21 }22 ]23}
Meta Ads: JSON anidado, conversiones en array "actions", ventana clic 7d + view 1d
Notas clave sobre Meta: (1) Los valores dentro de "actions" vienen como strings, no como números. (2) Solo te interesa el action_type "purchase" — el resto son métricas intermedias del funnel. (3) La ventana de atribución de Meta es MUCHO más corta: clic 7 días + view 1 día. Eso significa que Meta es más conservador que Google en lo que cuenta como conversión... pero aún así infla. (4) En el fichero de enero todas las campañas registran compras. El caso de una campaña de awareness con cero compras no aparece en estos datos, pero sí ocurre en producción: por eso el código de aplanado tiene que soportarlo desde el primer día.
Raúl te da contexto adicional sobre Meta: "La API de Meta es una pesadilla. Cambian cosas cada trimestre sin avisar. El campo cost_per_action_type desapareció en la v16, luego volvió en la v17 con otro nombre. Iván ya ha construido 3 versiones del script de ingesta de Meta en 2 años. Por eso insiste en que el código de parseo sea modular — cuando Meta cambie algo (que lo hará), solo tienes que tocar una función, no reescribir todo."
En 2021, Meta redujo su ventana de atribución de 28 días a 7 días (por privacidad de iOS 14). Esto hizo que de la noche a la mañana "perdiera" un 30% de conversiones reportadas. Los anunciantes entraron en pánico pensando que Meta ya no funcionaba. En realidad las ventas seguían igual — solo que Meta ya no se las podía atribuir. Este episodio demostró lo frágil que es confiar en los números de una sola plataforma.
### Fuente 3: Email Marketing (CSV semanal)
El proveedor de email marketing de PharmaLife (similar a Klaviyo) exporta un CSV SEMANAL cada viernes con las métricas acumuladas de la semana. Este es un problema de granularidad: Google y Meta reportan diario, pero email solo semanal. Además, la atribución del email es distinta: cuenta como conversión si el usuario abre un email y compra dentro de los 7 días siguientes a la apertura.
Hay un detalle operativo que Iván señala: "El CSV de email lo manda el proveedor por email (ironía) cada viernes a las 18:00. Raúl lo descarga manualmente y lo sube a S3 el lunes por la mañana. Eso significa que los datos de email siempre llegan 2-3 días más tarde que Google y Meta. Para el pipeline semanal no importa (se ejecuta los lunes y ya tiene todo), pero para análisis ad-hoc del fin de semana es un problema. En la v2 vamos a conectar la API del proveedor directamente."
1-- Estructura: email_campaigns.csv2-- Granularidad: 1 fila por semana por campaña3-- Periodo: enero 2024 (5 semanas)4-- Campañas activas: 8 (las mismas las 5 semanas)56SELECT * FROM email_campaigns LIMIT 6;78-- week_start | week_end | campaign_name | sent | opened | clicked | converted | revenue_eur | total_cost9-- 2024-01-01 | 2024-01-07 | Weekly_Vitamins_Promo | 3361 | 1046 | 230 | 16 | 1465.29 | 606.1410-- 2024-01-08 | 2024-01-14 | Weekly_Vitamins_Promo | 2988 | 928 | 190 | 15 | 1655.97 | 505.8811-- 2024-01-15 | 2024-01-21 | Weekly_Vitamins_Promo | 3154 | 976 | 242 | 16 | 1386.33 | 530.4312-- 2024-01-22 | 2024-01-28 | Weekly_Vitamins_Promo | 5280 | 1308 | 349 | 18 | 2035.46 | 940.3013-- 2024-01-29 | 2024-02-04 | Weekly_Vitamins_Promo | 3964 | 1001 | 264 | 13 | 1424.41 | 667.6214-- 2024-01-01 | 2024-01-07 | Weekly_Omega3_Launch | 4211 | 1223 | 345 | 15 | 1767.92 | 694.561516-- Total envíos enero: 110.69017-- Total conversiones enero: 48018-- Total ingresos enero: 48.000 EUR19-- Total coste enero: 20.000 EUR20-- Ventana de atribución: apertura 7 días21-- Nota: email es la única fuente que reporta ingresos Y coste. Las otras dos solo dan coste.22-- Ojo con la última semana: empieza el 29 de enero y se sale del mes.
Email Marketing: CSV semanal, granularidad diferente, incluye revenue y coste
Si sumas las dos últimas columnas del CSV de email sale algo que llama la atención: 20.000 € de coste para 48.000 € de ingresos. Parece un buen negocio, pero cuidado: ese «coste» no es lo que cuesta enviar un correo (eso son céntimos). Lo que hay en esa columna es el coste que la agencia IMPUTA al canal — horas de diseño, de copy, de gestión de la herramienta, de segmentación — repartido entre las campañas. Que un canal reporte coste imputado y los otros dos reporten gasto de medios es exactamente el tipo de diferencia que hace que sumar tres fuentes dé un número sin sentido. No sumes los costes de las tres fuentes hasta saber qué hay dentro de cada uno.
### La tabla comparativa de las 3 fuentes
Después de 2 horas explorando los 3 datasets, te sientas con Iván para documentar las diferencias. Él te ayuda a estructurar el doc que Carmen pidió:
1DOCUMENTO: Discrepancias entre fuentes - PharmaLife (Enero 2024)2================================================================34| Dimension | Google Ads | Meta Ads | Email Marketing |5|------------------|----------------------|----------------------|----------------------|6| Formato | CSV plano | JSON anidado (API) | CSV plano |7| Granularidad | Diaria por campaña | Diaria por campaña | SEMANAL por campaña |8| Naming | PL_Type_Product_Seg | pharmalife_x_y_plat | Texto libre humano |9| Ventana atrib. | Click 30 días | Click 7d + View 1d | Open 7 días |10| Tipo conversión | Purchase (implicito) | Purchase (en array) | Converted (directo) |11| Revenue | NO reporta | NO reporta | SI reporta |12| Moneda | EUR | EUR | EUR |13| ID comun | NO | NO | NO |14| Entrega | S3 6AM automático | API pull 2AM auto | Email viernes manual |15| Fiabilidad | Alta (automático) | Alta (automático) | Media (manual) |1617PROBLEMAS IDENTIFICADOS:181. No hay ID compartido entre fuentes -> necesitamos mapping manual192. Granularidad temporal incompatible -> email semanal vs resto diario203. Ventanas de atribución diferentes -> no miden lo mismo214. Nombres de campaña inconsistentes -> imposible JOIN directo225. Email no llega hasta lunes (se envia viernes) -> 2-3 días de retraso
Tu primer entregable: el doc de discrepancias para Carmen
### La mapping table de Raúl
A media mañana, Raúl se acerca a tu mesa con su portátil. "Carmen me dice que necesitas mi mapping. Te lo paso por Slack." Raúl tiene una Google Sheet donde mapea manualmente los nombres de campaña entre las 3 plataformas. Es su tesoro: lo actualiza cada lunes revisando qué campañas nuevas se crearon en cada plataforma durante la semana.
Raúl te explica el proceso: "Cada lunes abro las 3 plataformas y miro las campañas activas. Las que son nuevas, las añado al sheet conectando los nombres. El problema es que los equipos de paid de PharmaLife no siguen una convención uniforme. El que gestiona Google usa PL_Type_Product_Segment, el de Meta usa pharmalife_product_tipo_plataforma en minúsculas, y los de email usan nombres legibles tipo Titulo_Con_Mayusculas. Nadie se puso de acuerdo y ahora es un lío."
Tú preguntas: "¿Y no se puede forzar una convención única?" Raúl suspira: "Llevo 2 años pidiéndolo. El problema es que cada plataforma tiene sus restricciones: Google no permite ciertos caracteres, Meta trunca a 60 chars, y el equipo de email usa un editor visual que no respeta convenciones. Carmen ha escalado el tema pero PharmaLife no lo prioriza. Así que lo resolvemos nosotros con mapping manual y paciencia."
1#canal-pharmalife [11:23]23Raúl Merino: Hey! Bienvenido al equipo. Carmen me dice que4estás con lo de la unificación. Te paso mi mapping table:56[campaign_mapping.csv]78Columnas: campaign_group, google_name, meta_name, email_name, product_category928 filas (28 grupos de campaña mapeados)1011Ejemplo:12 campaign_group: "Vitaminas Brand"13 google_name: "PL_Search_Vitamins_Brand"14 meta_name: "pharmalife_vitamins_broad_fb"15 email_name: "Weekly_Vitamins_Promo"16 product_category: "vitamins"1718Ojo que no todas las campañas están en las 3 plataformas.19Protein Retargeting solo existe en Google y Meta (no tiene email).20Cart Recovery solo existe en Email. Etc.2122Si encuentras alguna campaña que no está mapeada, dime y la añado.23Suele haber 3-4 nuevas cada semana que aún no he mapeado.
Raúl comparte su Google Sheet — la pieza clave para la unificación
1-- campaign_mapping.csv (28 filas) - La "biblia" de Raúl2-- NULL significa que esa campaña no existe en esa plataforma34SELECT * FROM campaign_mapping LIMIT 8;56-- campaign_group | google_name | meta_name | email_name | product_category7-- Vitaminas Brand | PL_Search_Vitamins_Brand | pharmalife_vitamins_broad_fb | Weekly_Vitamins_Promo | vitamins8-- Omega3 Generic | PL_Search_Omega3_Generic | pharmalife_omega3_retarget_ig | Weekly_Omega3_Launch | omega39-- Protein Retargeting | PL_Display_Protein_Retargeting | pharmalife_protein_retarget_fb | NULL | protein10-- Supplements Shopping | PL_Shopping_Supplements_All | NULL | NULL | supplements11-- Cart Recovery | NULL | NULL | Abandoned_Cart_Recovery | cross-sell12-- Collagen Generic | PL_Search_Collagen_Generic | pharmalife_collagen_awareness_ig | VIP_Collagen_Exclusive | collagen13-- Brand Awareness Video | PL_Video_Brand_Awareness | pharmalife_brand_video_fb | NULL | brand14-- Vitaminas Competitor | PL_Search_Vitamins_Competitor | pharmalife_vitamins_competitor_fb | NULL | vitamins
La mapping table: NULL donde no aplica, product_category para agrupar
La mapping table de Raúl tiene 28 filas pero en las fuentes hay campañas que NO están en la tabla: campañas nuevas que se crearon esta semana, campañas de test que alguien lanzó por error, o campañas de otros clientes que se filtraron por un bug. Parte de tu trabajo será detectar estas "huérfanas" y alertar a Raúl.
### Las 4 discrepancias clave
Al final del día, Iván te pide que le resumas los problemas en una frase cada uno. Es su forma de verificar que has entendido:
- 01.NAMING: La misma campaña tiene nombres diferentes en cada plataforma. No hay ID compartido. La única forma de conectarlas es la mapping table manual de Raúl.
- 02.GRANULARIDAD: Google y Meta son diarios. Email es semanal. Para comparar necesitas o agregar a semanal (perdiendo detalle) o distribuir email a diario (inventando distribución).
- 03.DEFINICION: Google cuenta clicks de 30 días, Meta clicks de 7 días + views de 1 día, Email aperturas de 7 días. No miden lo mismo. Una "conversión" en Google no es comparable a una "conversión" en Meta.
- 04.OVERLAP: Un usuario puede aparecer en las 3 plataformas antes de comprar. Las 3 se atribuyen la venta completa. Es el origen de la inflación del 49,5%.
Documentar la discrepancia no la arregla. Para cada una hay que decidir qué se hace: (1) Granularidad — se sube todo a semanal; inventar un reparto diario del email fabrica un dato que nadie puede verificar. (2) Definición de conversión — no se unifican; se etiquetan con fuente y ventana, y sumarlas sin etiqueta es el error que genera la inflación. (3) Naming — la mapping table manual es la solución correcta mientras el cliente no unifique convenciones; lo que hay que añadir es una alerta cuando aparezca una campaña sin mapear. (4) Solapamiento — no se arregla en el pipeline; se arregla cambiando la pregunta: «cuántas ventas hubo en total» (CRM) y «cómo repartimos el mérito» (modelo de atribución, día 3). Regla práctica: el pipeline nunca resuelve una ambigüedad de negocio por su cuenta. La deja visible y etiquetada, y que decida quien tenga que decidir.
### El impacto de las ventanas de atribución
Iván te dibuja un diagrama temporal en la pizarra para que entiendas como las ventanas de atribución distorsionan los números:
1TIMELINE DE UN CLIENTE (ejemplo real):23Día 1: Ve anuncio en Instagram (Meta) -> NO hace click4Día 3: Busca "omega 3 precio" en Google -> CLICK en anuncio PL5Día 3: Visita web PharmaLife, mira productos, se va sin comprar6Día 5: Recibe email "Te dejaste algo en el carrito" -> ABRE email7Día 7: Vuelve a buscar en Google "pharmalife omega 3" -> CLICK8Día 7: COMPRA (90.48 EUR)910QUIEN SE LA ATRIBUYE?1112Google: "Fue mia" (click en día 3 Y día 7, ambos dentro de 30 días)13Meta: "Fue mia" (view en día 1, dentro de 1 día de view... NO!14 View fue día 1, compra día 7 = 6 días. Fuera de ventana 1d.)15 Meta NO se la atribuye en este caso.16Email: "Fue mia" (open día 5, compra día 7 = 2 días. Dentro de 7d.)1718Resultado: Google + Email = 2 atribuciones para 1 venta.19Inflación en este caso: 2x (100% inflado).2021Y si el cliente hubiera hecho CLIC en Instagram el día 1?2223Antes de 2021 (ventana de clic de 28 días): Meta también se la atribuye.24Google + Meta + Email = 3 atribuciones. Inflación 3x.2526Después de 2021 (ventana de clic de 7 días): el clic del día 1 y la compra27del día 7 están justo en el límite. Un día más y Meta la pierde.2829Lo que Meta recortó en 2021 fue la ventana de CLIC, de 28 días a 7.30La ventana de VISUALIZACIÓN sigue siendo de 1 día, igual que siempre.
Las ventanas de atribución crean la inflacion — entender esto es clave
Iván concluye: "Esto no es un bug — es by design. Cada plataforma elige una ventana que la favorece. Google usa 30 días porque Search captura intent muy temprano. Meta redujo a 7 días por presión regulatoria (iOS 14), no por elección. Email usa 7 días de apertura porque el email es un canal de cierre rápido. Entender estas ventanas te ayuda a interpretar los números."
Iván te dice: "Regla número 1 de data engineering en AdTech: NUNCA confíes en los números de conversión de una sola plataforma. Siempre son optimistas porque la plataforma tiene incentivo de mostrar buenos resultados para que el anunciante siga gastando. La única fuente de verdad son las ventas reales del CRM del cliente. Todo lo demás es una aproximación."
Regístrate para guardar tu progreso.