Caso práctico
Cómo reduje un 39% el coste de un monitor SEO rediseñando su arquitectura
Reduje el gasto diario de un monitor SEO de USD 0,28 a USD 0,17 sin eliminar funcionalidades, consultar menos keywords ni sacrificar su histórico. Eso representa una reducción del 39,3%.
El ahorro vino de una decisión de arquitectura: dejar de pagar varias veces por obtener exactamente la misma información.
Eso ocurría en un monitor de posiciones SEO que desarrollé para consultar resultados orgánicos de Google mediante DataForSEO. El sistema permitía organizar proyectos, asociar keywords y dominios, y conservar un histórico de rankings. Cumplía su función, pero su arquitectura vinculaba cada consulta externa a un proyecto específico.
El problema aparecía cuando varios proyectos utilizaban la misma keyword.
Si tres proyectos necesitaban conocer su posición para “cerrajeros Barcelona”, el sistema enviaba tres solicitudes independientes. Sin embargo, Google no genera una página de resultados diferente para cada uno de esos proyectos: la SERP que necesitábamos analizar era la misma.
La solución no consistía simplemente en optimizar una función. Había que cambiar la unidad de trabajo del sistema. Ese cambio redujo el gasto diario de la aplicación en USD 0,11: aproximadamente USD 3,30 al mes o USD 40,15 al año si funciona todos los días.
El resultado
Después del rediseño, la aplicación:
- Pasó de gastar USD 0,28 a USD 0,17 al día: una reducción del 39,3%.
- Conservó todas sus funcionalidades de seguimiento y comparación.
- Mantuvo los 19.294 rankings históricos existentes.
- Puede utilizar una sola SERP para calcular resultados de varios proyectos.
- Puede incorporar nuevos proyectos con keywords existentes sin generar inmediatamente otra consulta externa.
El ahorro no provino de reducir el valor ofrecido por el producto. Surgió de eliminar trabajo externo duplicado y trasladar las comparaciones por dominio al procesamiento interno.
El problema: una consulta por cada proyecto, keyword y dominio
En la arquitectura anterior, cada proyecto tenía sus propias tareas de seguimiento. Conceptualmente, el coste se comportaba de esta manera:
Proyecto + keyword + dominio = una llamada a la API
Por ejemplo:
“cerrajeros Barcelona”
Proyecto A → una llamada
Proyecto B → otra llamada
Proyecto C → otra llamada
La duplicación no solo aumentaba el consumo de la API. También repartía una misma fuente de información entre tareas externas diferentes, duplicaba lógica en varios endpoints y hacía más difícil mantener un histórico consistente.
Durante la revisión aparecieron otros problemas relacionados:
- Credenciales y tokens dentro del código.
- Fechas históricas inconsistentes.
- Funciones redundantes o parcialmente rotas.
- Ausencia de una fuente única para cada resultado SERP.
- Dificultad para impedir consultas simultáneas o repetidas.
- Una estructura que se volvía más costosa a medida que se añadían proyectos.
La arquitectura estaba modelando la necesidad de cada proyecto, pero no el recurso que realmente comprábamos: una página de resultados para una keyword.
La decisión principal: convertir la keyword en la unidad de consulta
Rediseñé el flujo para que cada keyword única produzca una sola solicitud externa:
Keyword única = una llamada a la API
Cuando DataForSEO devuelve la SERP completa, el sistema la almacena una vez y realiza internamente todas las comparaciones necesarias. Una respuesta puede utilizarse para buscar los dominios correspondientes a varios proyectos.
Keyword: “cerrajeros Barcelona”
↓
Una consulta a DataForSEO
↓
SERP completa almacenada
↓
Ranking del Proyecto A
Ranking del Proyecto B
Ranking del Proyecto C
Este cambio separó dos conceptos que antes estaban mezclados:
- Obtener una SERP desde un servicio externo.
- Calcular qué posición ocupa cada dominio dentro de esa SERP.
La primera operación tiene un coste externo y debe evitar duplicaciones. La segunda se ejecuta internamente y puede repetirse tantas veces como sea necesario.
Un catálogo global para deduplicar keywords
Para que la nueva arquitectura funcionara, no bastaba con comparar el texto exactamente como lo introducía el usuario. Variaciones de mayúsculas, tildes, espacios o puntuación podían crear duplicados que parecían términos diferentes.
Por eso incorporé un catálogo global de keywords y un proceso de normalización antes de guardarlas:
- Conversión a minúsculas.
- Eliminación de tildes.
- Normalización de espacios.
- Eliminación de puntuación irrelevante.
De esta manera:
cerrajería barcelona
Cerrajeria Barcelona
CERRAJERÍA BARCELONA
se convierten en una única clave normalizada:
cerrajeria barcelona
La normalización no es solamente una mejora de limpieza de datos. Es la garantía de que el sistema no volverá a pagar por una consulta duplicada debido a diferencias de formato.
La nueva arquitectura de datos
El rediseño se apoyó en cinco entidades principales:
keywords: catálogo global y deduplicado de términos de búsqueda.projects: proyectos que necesitan seguimiento.project_keywords: relación entre un proyecto, una keyword y el dominio que debe buscarse.serp_runs: registro de cada consulta realizada a DataForSEO, incluyendo estados, payloads, respuestas, intentos, errores y fechas.project_rankings: resultados calculados para cada proyecto a partir de una SERP almacenada.
Esta separación crea una fuente única de verdad:
serp_runs conserva lo que devolvió el proveedor y
project_rankings conserva la interpretación específica para
cada proyecto.
También mejora la trazabilidad. Ante un resultado inesperado, ahora es posible saber qué solicitud lo originó, qué respuesta se recibió y cómo se calculó cada ranking.
Procesamiento asíncrono sin duplicar solicitudes
DataForSEO no siempre devuelve el resultado definitivo en el momento de publicar una tarea. Por esa razón separé el procesamiento en dos procesos programados.
El cron de publicación se ejecuta una vez al día y selecciona únicamente keywords que:
- Están activas.
- Pertenecen al menos a un proyecto activo.
- No tienen una solicitud pendiente.
- Todavía no fueron consultadas ese día.
Un segundo cron se ejecuta cada cinco minutos para revisar las solicitudes publicadas o pendientes. Si el resultado aún no está disponible, conserva su estado. Cuando se completa, almacena la SERP, calcula todos los rankings asociados y deja de consultarla.
También añadí bloqueos en la base de datos para impedir que dos ejecuciones del cron procesen simultáneamente el mismo trabajo.
Así, la deduplicación no depende únicamente del diseño de las tablas. Está protegida durante todo el ciclo de vida de la consulta.
Una SERP, múltiples rankings
Cuando se recibe una respuesta completa, el procesamiento interno sigue este flujo:
- Recorre los resultados orgánicos.
- Normaliza los dominios encontrados.
- Los compara con los dominios configurados para cada proyecto.
- Selecciona la mejor posición correspondiente a cada dominio.
- Guarda la URL exacta encontrada.
- Registra explícitamente los dominios que no aparecen dentro de la profundidad consultada.
Una sola respuesta puede generar tantos rankings como asociaciones existan para esa keyword.
Además, si se crea posteriormente un proyecto y se le asigna una keyword que ya tiene una SERP almacenada, el sistema puede calcular su posición inmediatamente sin hacer una nueva llamada. Añadir seguimientos que reutilizan keywords existentes tiene, por tanto, un coste externo marginal prácticamente nulo.
Migrar sin perder el histórico
El rediseño debía conservar los datos existentes. La base anterior contenía:
- 7 proyectos.
- 84 tareas de seguimiento.
- 57 keywords únicas.
- 19.294 snapshots históricos.
Después de la migración se conservaron:
- Los 7 proyectos.
- Las 57 keywords normalizadas.
- Las 84 asociaciones entre proyecto, keyword y dominio.
- Los 19.294 rankings históricos.
- 19.268 ejecuciones históricas compartidas.
No se descartó ningún snapshot y tampoco se mezclaron identificadores externos pertenecientes a keywords diferentes.
Una dificultad adicional fue que los valores antiguos de
captured_at no eran fiables. Para reconstruir la cronología
utilicé las fechas válidas de created_at. Las ejecuciones
migradas quedaron identificadas como datos heredados para garantizar que
su presencia no provocara nuevas llamadas a la API.
Funcionalidad conservada y mejorada
Reducir llamadas no debía significar perder capacidad de análisis. El nuevo sistema conserva y amplía las comparaciones históricas:
- Ranking actual y anterior.
- Variación positiva o negativa.
- Comparaciones a 7 y 30 días.
- Comparación con una fecha específica.
- Hasta 100 puntos históricos por seguimiento.
- URL encontrada en cada medición.
- Estados diferenciados para pendiente, completado, sin ranking y error.
La interfaz muestra las mejoras en verde, las caídas en rojo y los resultados sin cambios en un tono neutro. También ordena las posiciones de mejor a peor y coloca al final los seguimientos pendientes.
Seguridad como parte del rediseño
La revisión arquitectónica también permitió corregir riesgos que no estaban directamente relacionados con el coste:
- Las credenciales salieron del repositorio.
- La configuración local quedó ignorada por Git.
- Los endpoints de escritura quedaron protegidos mediante un token administrativo enviado por cabecera.
- Los procesos programados solo pueden ejecutarse desde CLI.
- Las operaciones públicas y administrativas quedaron separadas.
- Se incorporaron bloqueos y validaciones para impedir solicitudes externas duplicadas.
La optimización no terminó en hacer menos llamadas: el sistema resultante también es más seguro, auditable y fácil de mantener.
La lección principal
La mejora más importante no fue una consulta SQL, un cron o una función concreta. Fue identificar que el sistema había elegido incorrectamente su unidad de trabajo.
Mientras la consulta estuvo vinculada al proyecto, cada proyecto nuevo podía multiplicar el consumo de la API. Al vincularla a la keyword única y procesar los dominios internamente, el crecimiento de proyectos dejó de implicar necesariamente el mismo crecimiento en llamadas externas.
Cuando varios consumidores necesitan la misma información, conviene preguntarse si realmente necesitan obtenerla por separado o si pueden compartir una única fuente y derivar de ella sus propios resultados.
En este caso, esa pregunta convirtió un conjunto de seguimientos aislados en una arquitectura compartida, reutilizable y preparada para escalar. El resultado medible fue pasar de USD 0,28 a USD 0,17 diarios —una reducción del 39,3%— sin perder datos ni funcionalidad.