Los lanzamientos de campaña no empiezan en un panel. Empiezan en una hoja de cálculo que alguien compartió en Slack. Las URLs viven en la columna A, los metadatos UTM llenan las columnas B a G, los slugs están en la columna H, y el brief dice que el lanzamiento es mañana. La parte más lenta de todo el proceso es copiar cada fila a una UI de acortador un enlace a la vez - no porque algo sea técnicamente difícil, sino porque no hay razón para hacerlo así.
Este post es el flujo de trabajo directo: cómo se ve la hoja, cómo se mapea al endpoint de importación masiva de Elido, tres rutas de importación dependiendo del número de filas, el paso de dry-run que captura errores antes de que estén en producción, y un snippet de Apps Script que automatiza todo el proceso en un trigger. Para el contexto más amplio sobre higiene UTM de extremo a extremo, el cornerstone de tracking UTM cubre las plantillas de workspace y el reenvío de conversiones del lado del servidor en profundidad. Este post es la porción de hoja a enlaces cortos de ese pipeline.
TL;DR#
- Mantén una hoja por campaña:
target_url,slug,utm_source,utm_medium,utm_campaign,tagscomo columnas nombradas. Las columnas UTM que están en blanco se llenan desde tu plantilla de workspace. - Tres rutas de importación: pegar filas en la UI (hasta 1.000 filas), subida CSV (hasta 10.000 filas), o la API vía script (ilimitado, repetible).
- Siempre ejecuta primero con
dry_run=true. La vista previa muestra el enlace corto resuelto y la cadena de consulta UTM completamente renderizada sin confirmar nada. - Prefija los slugs de campaña (
q2-,jun-) para asignarles un espacio de nombres. Las colisiones aparecen en el dry-run, no a mitad de importación.
Forma de la hoja#
Las columnas requeridas son target_url y una de slug o auto_slug. Todo lo demás es opcional pero tiene una interpretación definida cuando está presente.
| Columna | Requerida | Notas |
|---|---|---|
target_url | sí | URL completa de destino incluyendo esquema |
slug | una de dos | Preferida - te da URLs cortas predecibles |
auto_slug | una de dos | Establece a true y Elido genera un slug |
utm_source | opcional | Anula el valor de la plantilla del workspace |
utm_medium | opcional | Anula el valor de la plantilla del workspace |
utm_campaign | opcional | Anula el valor de la plantilla del workspace |
utm_content | opcional | Generalmente la variante creativa |
utm_term | opcional | Palabra clave pagada o segmento de audiencia |
tags | opcional | Separados por comas, aplicados al enlace |
title | opcional | Mostrado en la lista de enlaces del panel |
La regla UTM es simple: si target_url ya contiene un ?utm_source= (o cualquier utm_*) parámetro de consulta, esos valores se pasan sin cambios. Sin sobrescribir, sin fusionar. Si la URL de destino no tiene parámetros UTM, Elido los construye desde las columnas UTM, recurriendo a tu plantilla de workspace para cualquier columna que esté en blanco. Esto importa en la práctica - algunos equipos mantienen URLs de destino pre-etiquetadas para su proveedor de servicios de email, y una herramienta de importación masiva que las re-etiqueta silenciosamente produce analytics rotos. Elido advierte en filas de modo mixto (algunos UTMs presentes, algunos faltantes) y te pide que confirmes.
La columna de tags merece su propia nota. Los valores son cadenas separadas por comas: campaign:q2-spring, channel:paid-social, variant:hero-a. Esa forma de tres partes (dimensión:valor) te da ejes filtrables en el panel sin necesitar una configuración de taxonomía separada. Más sobre esto en la sección de taxonomía de tags a continuación.
Las tres rutas de importación#
Pegar filas en la UI de importación masiva (hasta 1.000 filas)#
Para cualquier cosa por debajo de 1.000 filas, la ruta más rápida es copiar el rango de la hoja y pegar en el área de texto de importación masiva. La UI de Elido auto-detecta valores separados por tabulaciones desde un pegado de hoja de cálculo y mapea columnas por encabezado. No exportas un CSV; simplemente pegas.
Esto funciona bien para el caso más común: un brief de campaña que ya vive en Sheets, un plazo de lanzamiento a una hora de distancia, y sin apetito por scripting. La UI muestra una vista previa de todas las filas antes de confirmar (el mismo dry-run que obtendrías de la API) y te permite arreglar cualquier fila fallida en línea antes de proceder.
Una trampa: si tu hoja tiene celdas fusionadas o formato complejo, el pegado puede producir salida confusa. El movimiento seguro para cualquier hoja con estructura no trivial es copiar a una hoja limpia primero (pegar como valores), luego pegar el rango limpio en la UI de importación.
Subida CSV (hasta 10.000 filas)#
Para lanzamientos con más de 1.000 filas (campañas de catálogo grandes, códigos de evento, enlaces personalizados), la ruta de subida CSV maneja hasta 10.000 filas. Exporta la hoja como CSV (Archivo > Descargar > CSV) y súbelo en el diálogo de importación. El mapeo de encabezado de columna es el mismo; la diferencia es que las subidas grandes se procesan asincrónicamente y reportan su estado vía un webhook o endpoint de polling.
La exportación CSV de Google Sheets vía la API (consultada el 2026-05-12) soporta exportar un rango con nombre en lugar de la hoja completa, lo que es útil cuando tu hoja de campaña tiene múltiples pestañas o filas de encabezado que no quieres limpiar manualmente.
Llamada API desde un script (más de 10.000 filas, o ejecuciones repetidas)#
Para catálogos grandes, o para campañas que se ejecutan semanalmente y necesitan el mismo proceso automatizado, la ruta API es la correcta. Dos implementaciones comunes: Apps Script (sin herramientas locales requeridas, se ejecuta en el navegador) y Python (mejor para equipos con pipelines de datos existentes). El endpoint es el mismo en cualquier caso.
curl -X POST \
https://api.elido.app/v1/links/bulk \
-H "Authorization: Bearer $ELIDO_TOKEN" \
-H "Content-Type: multipart/form-data" \
-F "csv=@q2_spring_links.csv" \
-F "campaign_id=cmp_8a2f" \
-F "dry_run=false" \
-F "on_conflict=skip"
El parámetro on_conflict controla qué pasa cuando un slug ya existe: skip deja el enlace existente en su lugar y registra una advertencia, fail aborta toda la importación en la primera colisión, y replace actualiza el destino del enlace existente. Para la mayoría de las importaciones de campaña, skip es el predeterminado correcto: una re-ejecución del mismo CSV no sobrescribirá enlaces que ya creaste.
La API acepta hasta 10.000 filas por llamada. Para catálogos más grandes, divide en chunks de 5.000 filas; cada llamada es independiente e idempotente si usas slugs estables.
Dry run pre-importación#
Ejecuta cada importación con dry_run=true antes de confirmar. La respuesta es idéntica a la importación en vivo (cada fila muestra su enlace corto resuelto, la cadena de consulta UTM analizada, la lista de tags y cualquier advertencia) pero nada se escribe en la base de datos.
Las cosas que dry-run captura que no captarás de ninguna otra manera antes del lanzamiento:
- Un slug en la fila 14 que colisiona con un enlace existente en tu workspace (aparece como advertencia de conflicto)
- Una columna UTM que se dejó en blanco accidentalmente (Elido marca
utm_mediumfaltante como advertencia, no como error duro, pero uno que quieres saber antes del lanzamiento) - Una
target_urlcon un espacio al final que sobrevivió a la copia de la hoja de cálculo (la URL resuelta se ve bien en el CSV pero el destino real tiene%20añadido) - Valores de tag que exceden 32 caracteres (truncados silenciosamente; el dry-run hace visible el valor almacenado)
La respuesta del dry-run está paginada en el mismo formato que un resultado de importación real. Abre la primera página, revisa la fila 2 (la primera fila de datos después de tu probable fila perfecta 1) y la última fila. Luego mira cualquier fila que marque una advertencia. Dos minutos de revisión capturan los errores que de otro modo aparecerían como "¿por qué este enlace de campaña da 404?" la mañana después del lanzamiento.
Conflictos de slug#
Los conflictos de slug ocurren cuando un slug que estás intentando importar ya existe en tu workspace o en tu dominio personalizado. La importación los expone en la respuesta de dry-run con el tipo de conflicto (same_workspace, same_domain, reserved) y la URL de destino del enlace existente.
La solución práctica es asignar espacios de nombres. Prefija los slugs de campaña con un identificador corto: q2-, jun26-, sm- (para redes sociales), em- (para email). Un slug como q2-spring-hero-a es poco probable que colisione con algo de una campaña anterior. Los prefijos también hacen obvio el filtro del panel - todos los enlaces etiquetados q2-* pertenecen a un trimestre de campaña.
Un caso que vale la pena destacar: si estás migrando desde otro acortador y quieres preservar slugs heredados, importa esos primero sin prefijo, luego usa slugs prefijados para nuevo contenido de campaña. La importación masiva de Elido te dirá en el dry-run si alguno de los slugs heredados entra en conflicto con los que ya están en el workspace.
Taxonomía de tags#
Los tags aplicados en el momento de la importación obtienen la misma estructura de tres partes que las columnas de la hoja que los impulsaron: campaign:q2-spring, channel:email, variant:hero-a. Cuando abras el panel más tarde y filtres por channel:email, no estás filtrando cadenas de texto libre - estás consultando una taxonomía consistente.
Los nombres de dimensión (campaign, channel, variant) provienen de la convención de tu equipo, no de ningún esquema forzado por Elido. La restricción es el formato: un separador de dos puntos, sin espacios en la clave, valores bajo 32 caracteres. Los equipos que aplican esto en la hoja (una columna tags que una fórmula construye como "campaign:"&E2&", channel:"&F2) nunca tienen tags malformados en el panel. Los equipos que dejan la columna de tags como texto libre tienen un problema de limpieza dentro de tres campañas.
Para la visión general de la función de campañas, la agrupación basada en tags es la forma principal en que Elido agrupa clics por dimensión de campaña en el panel de analytics - así que la taxonomía que definas en la hoja es la taxonomía por la que filtrarás al reportar.
Automatización con Apps Script#
Para equipos que ejecutan la misma estructura de campaña semanalmente (enlaces de newsletter, enlaces sociales pagados, variantes de email), el movimiento correcto es automatizar la importación por completo. Google Apps Script se ejecuta en el navegador, tiene acceso a los datos de la hoja y puede dispararse en un cron basado en tiempo o envío de formulario.
El patrón: un trigger se dispara, el script lee cualquier fila de la hoja que no tenga un valor short_link en la columna I, las POSTea a la API de importación masiva y escribe los enlaces cortos creados de vuelta en la columna I. En el siguiente trigger, las filas ya importadas se saltan porque la columna I está poblada.
// Google Apps Script - bulk import new rows via Elido API
// Trigger: time-driven, every hour (or on form submit)
// Docs: https://developers.google.com/apps-script/guides/triggers (accessed 2026-05-12)
function importNewLinks() {
const sheet =
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Q2 Spring");
const data = sheet.getDataRange().getValues();
const headers = data[0];
const urlCol = headers.indexOf("target_url");
const slugCol = headers.indexOf("slug");
const srcCol = headers.indexOf("utm_source");
const medCol = headers.indexOf("utm_medium");
const campCol = headers.indexOf("utm_campaign");
const tagsCol = headers.indexOf("tags");
const doneCol = headers.indexOf("short_link"); // write back here
const newRows = [];
const rowIndexes = [];
for (let i = 1; i < data.length; i++) {
const row = data[i];
if (!row[urlCol] || row[doneCol]) continue; // skip empty or already imported
newRows.push({
destination: row[urlCol],
slug: row[slugCol] || undefined,
utm_source: row[srcCol] || undefined,
utm_medium: row[medCol] || undefined,
utm_campaign: row[campCol] || undefined,
tags: row[tagsCol]
? String(row[tagsCol])
.split(",")
.map((t) => t.trim())
: [],
});
rowIndexes.push(i);
}
if (!newRows.length) return;
const payload = JSON.stringify({
links: newRows,
campaign_id: "cmp_8a2f",
on_conflict: "skip",
});
const resp = UrlFetchApp.fetch("https://api.elido.app/v1/links/bulk", {
method: "post",
contentType: "application/json",
headers: {
Authorization:
"Bearer " +
PropertiesService.getScriptProperties().getProperty("ELIDO_TOKEN"),
},
payload: payload,
muteHttpExceptions: true,
});
const result = JSON.parse(resp.getContentText());
const created = result.links || [];
// Write short links back into column I
created.forEach((link, idx) => {
if (!link.short_url) return;
const sheetRow = rowIndexes[idx] + 1; // 1-indexed
sheet.getRange(sheetRow, doneCol + 1).setValue(link.short_url);
});
}
Algunas notas de implementación:
Almacena el token de API en PropertiesService.getScriptProperties(), no codificado en el script. La documentación de triggers de Apps Script (consultada el 2026-05-12) cubre la configuración de triggers tanto basados en tiempo como en eventos. Para una hoja de campaña que un equipo llena de forma colaborativa, un trigger onEdit se dispara cuando se llena la columna A; el enlace corto aparece en la columna I a segundos de escribir la URL de destino.
La bandera muteHttpExceptions: true es importante. Sin ella, un 422 de la API lanza una excepción a nivel de script y el trigger deja de reintentar. Con ella, obtienes el cuerpo del error y puedes registrarlo en su lugar.
Para una integración más pesada (un script Python, un paso de CI que lee una hoja vía la Sheets API, o un trabajo programado en tu pipeline de datos existente), el endpoint spreadsheets.values.get de la Sheets API (consultado el 2026-05-12) te da JSON directamente. Desde ahí, la forma de la llamada de importación masiva es idéntica al ejemplo de curl arriba.
Errores comunes#
Espacios al final en slugs. Un slug copiado desde una celda de hoja de cálculo puede tener un espacio al final que es invisible en la UI. Elido lo permite (el slug es técnicamente válido), pero go.example.com/q2-promo con un espacio al final es una URL fea y la copia al portapapeles desde la barra de direcciones de un navegador generalmente lo elimina, por lo que la persona que pega el enlace corto más tarde obtiene un 404. La solución es una fórmula =TRIM(H2) en la columna de slug antes de exportar.
utm_medium faltante. Elido advierte pero no bloquea un utm_medium faltante porque algunas campañas lo saltan intencionalmente. Pero un medium faltante es casi siempre un error: GA4 enruta cualquier cosa sin él al canal (none), lo que hace inútil la atribución de canal. La referencia canónica del constructor de URL de GA4 (consultada el 2026-05-12) lista utm_medium como requerido para que la atribución de campaña funcione correctamente. Si tu plantilla de workspace tiene un predeterminado de utm_medium, las celdas en blanco en la columna lo heredan; si no, la advertencia del dry-run es tu última oportunidad de captarlo.
Valores de tag sobre 32 caracteres. Elido trunca silenciosamente los valores de tag que exceden 32 caracteres. La truncación es invisible en las advertencias del dry-run a menos que la busques (la respuesta muestra el valor almacenado, no el original). Los valores largos de tag generalmente provienen de pegar nombres de campañas UTM en la columna de tags: spring-2026-dach-email-reactivation-week3 tiene 42 caracteres y se convertirá en spring-2026-dach-email-reactivation-we en el panel. Mantén cortos los valores de dimensión de tag; mueve los metadatos verbosos al título del enlace en su lugar.
Olvidar dry_run=true en las re-ejecuciones. Si re-ejecutas una subida CSV contra una campaña que ya tiene enlaces, on_conflict=skip es seguro pero on_conflict=replace actualizará las URLs de destino en cualquier slug que aparezca tanto en el CSV antiguo como en el nuevo. En una campaña donde las URLs de destino no han cambiado, esto es inofensivo. En una campaña donde has actualizado las URLs de landing page a mitad de vuelo, es lo que quieres. Sabe en qué modo estás antes de confirmar.
El resumen de configuración a lanzamiento#
La versión más completa de este flujo de trabajo: construye la hoja una vez con nombres de columna estables, define una plantilla UTM de workspace para que las columnas UTM en blanco hereden predeterminados sensatos (cubierto en setup-branded-short-links), ejecuta la importación en seco para capturar conflictos y advertencias, confirma, y escribe el trigger de Apps Script para que la siguiente campaña requiera cero pasos manuales.
Para la capa de atribución que cierra el bucle después del clic, tracking de conversiones del lado del servidor cubre cómo cablear el click_id desde la respuesta de redirección de Elido hasta Meta CAPI y GA4, la pieza del lado del servidor que sobrevive a Safari ITP y a la interferencia de bloqueadores de anuncios. Ese post y este juntos te dan una imagen completa del flujo de trabajo de campaña de solutions/marketers, desde la hoja hasta el enlace corto hasta la conversión atribuida.
La superficie completa de gestión de URLs de campaña - plantillas, importación masiva, agrupación de campañas, reenvío de conversiones - está en la página features/campaigns.
Prueba Elido
Pega una URL, obtén un enlace corto
Sin registro. El enlace vive 30 días. Crea una cuenta para conservarlo.
Gratis, sin registro · 2 por día