Data APIs: cómo diseñar la paginación para que el pipeline no pierda datos en producción
Data APIs: cómo diseñar la paginación para que el pipeline no pierda datos en producción
El pipeline arranca, consume la primera página, falla en la tercera y relanza desde el principio. Al terminar tienes duplicados en las dos primeras páginas y un hueco silencioso justo en el rango que importaba. Nadie lo detecta hasta que el analista pregunta por qué faltan señales de esa ventana horaria.
Este escenario no es un bug puntual. Es la consecuencia predecible de usar la estrategia de paginación equivocada para el tipo de datos que estás consumiendo. La mayoría de los equipos elige el mecanismo de paginación que viene por defecto en la documentación del proveedor sin evaluar si encaja con su patrón de consumo real. En datos de alta frecuencia, ese error sale caro.
El problema con offset que nadie menciona en los tutoriales
offset + limit es el mecanismo más documentado, más intuitivo y, en ciertos contextos, el más peligroso.
La lógica parece sólida: pide los primeros 100 registros, luego los 100 siguientes, y así hasta vaciar el conjunto. El problema es que offset es relativo a una foto instantánea que el servidor no congela. Si entre la petición de la página 1 y la de la página 2 el proveedor indexa nuevas señales, el desplazamiento se mueve. Recibes registros repetidos o te saltas otros que nunca verás.
En APIs de medios públicos con alta frecuencia de actualización —donde pueden procesarse decenas de miles de menciones por hora— el deslizamiento del offset puede generar una pérdida de cobertura del 5-15% en ventanas de consumo lentas. No es ruido: es sesgo sistemático sobre exactamente los momentos de mayor actividad, que son los que más te interesa no perder.
Regla práctica: reserva offset para conjuntos de datos estáticos o casi estáticos (catálogos, taxonomías, listas de fuentes). Para flujos de señales con actualización continua, necesitas otra estrategia.
Cursores: la mejora que tiene su propio punto ciego
La paginación basada en cursores resuelve el problema del deslizamiento. El servidor emite un token opaco que encapsula la posición exacta en el conjunto de resultados. Tú lo devuelves en la siguiente petición y el servidor sabe exactamente dónde estabas, independientemente de cuántos registros se hayan incorporado entre medias.
Es la solución correcta para flujos con inserción continua. Pero tiene un punto ciego que pocas documentaciones señalan: los cursores suelen tener TTL.
Si tu pipeline procesa lento, o si hay una pausa no planificada —un timeout, un reinicio del worker, un incidente upstream— el cursor puede expirar antes de que puedas reutilizarlo. El resultado es que tu código de reanudación falla silenciosamente y relanza desde el origen o desde un punto arbitrario.
La mitigación no es técnica en el proveedor; es arquitectónica en tu lado. Antes de emitir cada cursor a la siguiente iteración, persístelo en un almacén durable (Redis con AOF, una tabla de control en PostgreSQL, un archivo en almacenamiento objeto con escritura atómica). No lo tengas solo en memoria del proceso. Si el proceso muere, el cursor sobrevive.
Keyset pagination: cuando el tiempo es el cursor real
Para APIs que exponen señales ordenadas por timestamp —que es el caso habitual en media intelligence y monitorización de fuentes públicas— la estrategia más robusta es keyset pagination: paginar usando el valor del campo de ordenación como ancla, no un offset ni un cursor opaco.
La petición tiene esta forma lógica:
GET /signals?published_after=2026-08-21T14:32:00Z&limit=500
La siguiente página arranca desde el timestamp del último registro recibido:
GET /signals?published_after=2026-08-21T14:47:23Z&limit=500
Las ventajas son claras: la ancla es determinista, reproducible y no caduca. Si el pipeline se detiene horas o días, relanzas desde el último timestamp registrado y no pierdes cobertura. Si el proveedor indexa registros con timestamp pasado (reindexación, corrección de fuentes), puedes detectar el hueco haciendo una consulta retrospectiva sobre la ventana afectada.
El riesgo está en los empates de timestamp: si varios registros comparten exactamente el mismo valor en el campo de ordenación y el conjunto cae justo en el límite de página, puedes perder o duplicar registros del empate. La solución es usar un campo secundario de desempate —un identificador único de registro— como segundo criterio de ordenación y ancla compuesta:
GET /signals?published_after=2026-08-21T14:47:23Z&after_id=a3f9c1&limit=500
Lo que el proveedor no controla: la consistencia de tu side del pipeline
Incluso con la estrategia de paginación correcta, el pipeline puede introducir pérdidas o duplicados desde tu propio código. Tres patrones de error recurrentes:
Commits prematuros. Marcas el cursor o el timestamp como procesado antes de confirmar que el lote se escribió correctamente en el destino. Si la escritura falla después, pierdes el rango sin posibilidad de recuperarlo sin reindexar.
Procesamiento sin idempotencia. Si tu lógica no es idempotente, relanzar un rango duplica registros en destino. La solución mínima es un INSERT ... ON CONFLICT DO NOTHING en el destino o una clave de deduplicación hash del identificador del registro.
Ventanas de tiempo sin solapamiento. Cuando paginas por timestamp y usas published_after sin solapamiento, asumes que los registros llegan en orden estricto al proveedor. En la práctica, el procesamiento distribuido introduce latencia variable: un registro con timestamp T puede aparecer disponible en la API varios segundos o incluso minutos después de T. La práctica estándar es consumir con un retraso configurable (ingestion_lag) y solapar la ventana de cada consulta en ese margen para capturar registros de llegada tardía.
Evalúa la paginación antes de firmar el contrato con el proveedor
La estrategia de paginación que soporta una data API no suele estar en las especificaciones comerciales, pero determina la complejidad de tu integración más que cualquier otro parámetro. Antes de comprometerte con un proveedor para un pipeline en producción, valida:
- ¿Soporta keyset pagination sobre campo temporal o solo offset?
- ¿Los cursores tienen TTL? ¿Cuánto? ¿Está documentado?
- ¿Qué ocurre si solicitas un cursor expirado? ¿Error explícito o comportamiento indefinido?
- ¿El API garantiza consistencia de lectura durante la paginación o el conjunto puede mutar?
- ¿Hay un campo de
ingestion_timestampseparado delpublished_timestamppara manejar llegadas tardías?
En FeedScale el modelo de paginación está orientado a consumo por ventana temporal, precisamente para que los pipelines puedan relanzarse desde un punto conocido sin necesidad de gestionar cursores opacos con TTL. No es un detalle menor: es una decisión de diseño que elimina una clase entera de bugs en producción.
La paginación es arquitectura, no configuración
Los equipos que tratan la paginación como un detalle de implementación a resolver en el sprint de integración acaban pagando esa deuda en incidentes de producción. Los que la modelan como una decisión arquitectónica —con sus implicaciones en idempotencia, persistencia de estado y manejo de llegadas tardías— construyen pipelines que sobreviven a fallos sin intervención manual.
La pregunta no es "¿cómo pagino esta API?". La pregunta es "¿qué garantías de cobertura necesito y qué estrategia de paginación las hace posibles?". Son preguntas distintas y llevan a soluciones distintas.