API
Solicita acceso →
Empezar / Paginación

Paginación

Cuando pides un listado en Alibanca —tus pagos o tus beneficiarios— la API no te devuelve todo de golpe. Te lo entrega por páginas: lotes ordenados de registros que vas pidiendo uno tras otro hasta agotar la lista. Alibanca usa keyset pagination (paginación por cursor) sobre un id que va en orden descendente: lo más nuevo primero. Este método es estable y rápido incluso cuando llegan pagos nuevos mientras paginas. En esta página te explicamos, paso a paso y sin dar nada por sabido, cómo pedir la primera página, cómo pedir la siguiente desde el último id que viste, y cómo saber que ya terminaste.

¿Qué es paginar y qué listados lo usan?

Imagínate que le pides al banco "dame todos los pagos que he hecho". Si llevas medio millón, devolverlos en una sola respuesta sería lento, pesado y frágil. La solución universal en cualquier API seria es la misma: partir la lista en pedazos manejables y entregarlos por turnos. A cada pedazo lo llamamos página, y a la técnica de recorrer todas las páginas la llamamos paginar.

En Alibanca hay dos endpoints de listado, y ambos se paginan exactamente igual:

GET/v2/paymentslista tus pagos, del más reciente al más antiguo
GET/v2/beneficiarieslista tus beneficiarios guardados

Como el mecanismo es idéntico en los dos, en esta guía lo explicamos a fondo con /v2/payments y al final te mostramos que con /v2/beneficiaries no cambia nada. Aprende el patrón una vez y lo usas en cualquier listado que agreguemos después.

Keyset vs. offset: por qué usamos un cursor

Hay dos formas clásicas de paginar. Vale la pena entender la diferencia, porque explica por qué Alibanca hace lo que hace y por qué te va a ahorrar dolores de cabeza.

La forma vieja: offset (saltar N registros)

La paginación por offset funciona como decirle a la base de datos "salta los primeros 40 y dame los siguientes 20". Pides página 1, luego página 2 (salta 20), luego página 3 (salta 40), y así. Es intuitivo, pero tiene dos defectos serios:

Se desincroniza. Entre que pides la página 1 y la página 2, alguien crea un pago nuevo que se cuela al inicio de la lista. Ahora todo se corrió una posición: el registro que estaba en el borde entre página 1 y 2 lo vuelves a ver (duplicado), o peor, uno se te escapa sin que lo veas nunca. En una lista que cambia todo el tiempo —y la de pagos cambia constantemente— el offset miente.

Se pone lento. Para darte "los 20 después de saltar 100.000", el motor tiene que contar y descartar esos 100.000 primero. Mientras más profundo paginas, más lento responde. En listas grandes, las últimas páginas se vuelven dolorosas.

La forma que usamos: keyset (un marcador que avanza)

La paginación por keyset —también llamada por cursor— no cuenta ni salta: usa un marcador. La analogía exacta es el marcalibros: no le dices al libro "ábrete en la página 40 contando desde el principio"; le dices "sigue justo donde dejé el marcador". Ese marcador, en Alibanca, es el id del último registro que viste.

Como cada id es único y la lista viene ordenada por id descendente, pedir "lo que sigue después de este id" siempre te da el mismo pedazo, sin importar cuántos registros nuevos hayan entrado por arriba. Por eso el keyset es:

i
Estable y rápido. Estable: los registros nuevos entran por el tope de la lista, no en medio de tu recorrido, así que nunca te generan duplicados ni saltos. Rápido: el motor va directo al id del marcador y sigue leyendo; no cuenta ni descarta nada, por más profundo que estés.

El orden: id descendente, lo más nuevo primero

Antes de pedir nada tienes que tener clara una regla: los listados vienen ordenados por id en orden descendente. Como el id crece con cada registro nuevo, "descendente" significa del más reciente al más antiguo. La primera página trae tus pagos más nuevos; a medida que avanzas, vas hacia atrás en el tiempo.

Esto importa por dos razones. Primero, es lo que casi siempre quieres ver de una: lo último que pasó. Segundo, define la mecánica del cursor —"dame lo que sigue" significa "dame registros con un id menor que el último que vi", o sea, más viejos. No tienes que calcular nada de esto a mano: solo pasas el último id y Alibanca sabe qué sigue.

Cada registro del listado trae su id y sus campos normales. En el caso de pagos, cada elemento incluye su state, que puede ser cualquiera de los tres estados de primera clase:

completed unknown failed

Pedir la primera página

La primera página es la más fácil: llamas al listado sin cursor. Al no darle un marcador, Alibanca empieza desde el tope —tus registros más recientes.

GET/v2/payments
cURLNodeCopiar
# Primera página: sin cursor, empieza desde lo más reciente
curl https://alibanca-api-production.up.railway.app/v2/payments \
  -H "x-api-key: sk_test_tu_llave" \
  -H "x-signature: <firma-del-envelope>"

La respuesta llega en el sobre estándar de Alibanca —status, traceId y payload— y adentro del payload viene el arreglo data con los registros de esta página:

RESPUESTA 200 application/json
{
  "status": "success",
  "traceId": "trc_01HZX9K3M7Qance",
  "payload": {
    "data": [
      { "id": "pay_01HZX9G4A0", "amountMinor": "150000", "currency": "VES", "rail": "PM", "state": "completed" },
      { "id": "pay_01HZX8F2Z9", "amountMinor": "42050", "currency": "VES", "rail": "CCE", "state": "unknown" }
    ]
  }
}

Fíjate en la forma: payload.data es un arreglo. Cada elemento tiene un id, y los id vienen de mayor a menor (el primero, pay_01HZX9G4A0, es más nuevo que el segundo). El último elemento del arreglo —el de id más pequeño— es tu marcador para pedir la página siguiente.

Pruébalo. En sandbox (https://alibanca-api-production.up.railway.app) el proveedor es simulado: no mueve un centavo real. Emite unos cuantos pagos con POST /v2/payments y luego pide GET /v2/payments. Verás tus pagos de prueba en data, del más reciente al más antiguo. Es el lugar perfecto para practicar el bucle de paginación sin riesgo.

Pedir la siguiente página desde el último id

Para avanzar, tomas el id del último elemento de la página que acabas de recibir y lo pasas como cursor. El cursor le dice a Alibanca "ya vi hasta aquí; dame lo que sigue". No hay número de página que llevar ni offset que calcular: solo el último id visto.

1

Toma el último id de la página anterior

Del arreglo data que recibiste, agarra el id del último elemento. En el ejemplo de arriba, ese es pay_01HZX8F2Z9. Ese es tu cursor.

2

Pásalo como cursor en la siguiente llamada

Vuelves a pedir el mismo listado, pero agregando el parámetro cursor con ese id. Alibanca te devuelve la tanda de registros que sigue —con id menores, o sea, más antiguos.

3

Repite

Cada página nueva te vuelve a dar un último id. Úsalo como cursor de la siguiente. Sigues así hasta que la lista se agote (lo vemos en la próxima sección).

cURLNodeCopiar
# Siguiente página: cursor = el último id que viste
curl "https://alibanca-api-production.up.railway.app/v2/payments?cursor=pay_01HZX8F2Z9" \
  -H "x-api-key: sk_test_tu_llave" \
  -H "x-signature: <firma-del-envelope>"
!
El cursor es un id, no un número de página. No inventes el valor del cursor ni intentes "adivinar" el siguiente. Siempre sale de la respuesta anterior: es literalmente el id del último registro que Alibanca te entregó. Pasar un id que no vino de un listado real te dará resultados inesperados.

Cómo saber cuándo terminaste

Iteras hasta que no haya más. ¿Y cómo se ve "no hay más"? Muy simple: cuando pides una página y el arreglo data llega vacío. Eso significa que ya no quedan registros más antiguos que tu último cursor —recorriste toda la lista.

RESPUESTA 200 application/json
// Última página: data vacío = terminaste, corta el bucle
{
  "status": "success",
  "traceId": "trc_01HZXA1B2C",
  "payload": { "data": [] }
}
i
La regla de parada. Mientras data traiga elementos, sigue avanzando con el último id como cursor. En cuanto data llegue vacío, detente: ya no hay más. Es la única condición de fin que necesitas y funciona igual en todos los listados.

El bucle completo: recorrer todo un listado

Junta las tres piezas —primera página sin cursor, siguiente página con el último id, parar cuando data venga vacío— y tienes un bucle que recorre todos tus registros de principio a fin. Este patrón es el que vas a copiar en tu integración:

cURLNodeCopiar
// cliente.get() se encarga de firmar el envelope y del x-api-key por ti.
// Aquí solo nos ocupamos de la lógica de paginación.
async function traerTodosLosPagos(cliente) {
  const todos = [];
  let cursor = null;

  while (true) {
    const ruta = cursor
      ? `/v2/payments?cursor=${cursor}`
      : "/v2/payments";

    const { payload } = await cliente.get(ruta);
    const pagina = payload.data;

    if (pagina.length === 0) break;          // data vacío = terminaste

    todos.push(...pagina);
    cursor = pagina[pagina.length - 1].id;   // el último id visto
  }

  return todos;
}

Léelo con calma: la primera vuelta cursor es null, así que pide /v2/payments pelado (primera página). En cada vuelta guarda los registros y actualiza cursor al id del último elemento. Cuando una página vuelve vacía, break corta el bucle. Sencillo, estable y sin offsets.

i
La firma vive en tu cliente. Cada request —también los GET de listado— viaja firmado. Por eso el ejemplo delega en cliente.get(): ahí adentro se arma el envelope, se firma con tu llave privada y se manda el x-api-key. Consulta cómo construir ese cliente en Autenticación y firma →.

Estabilidad: qué pasa si llegan pagos nuevos mientras paginas

Esta es la ventaja concreta del keyset y vale la pena verla en cámara lenta. Supón que estás a mitad del recorrido —tu cursor es pay_01HZX8F2Z9— y en ese instante emites tres pagos nuevos. Esos pagos reciben id más grandes (más nuevos), así que entran por el tope de la lista, muy por encima de tu cursor.

¿Te afectan? No. Tu cursor sigue pidiendo "registros con id menor que pay_01HZX8F2Z9", y los pagos nuevos tienen id mayores: quedan fuera de tu ventana de recorrido. No los ves duplicados, no se te cuela ninguno viejo, no se corre nada. Con offset, en cambio, esos tres registros nuevos habrían empujado toda la lista y te habrían generado duplicados o saltos. El marcador te blinda de eso.

i
¿Y si quieres ver lo nuevo? Fácil: cuando termines un recorrido, vuelve a empezar desde la primera página (sin cursor) más tarde. Los pagos que entraron mientras tanto estarán arriba, esperándote. La paginación te da una foto consistente hacia atrás; para lo nuevo, empiezas de cero otra vez —o mejor aún, te enteras al instante con Webhooks →.

Beneficiarios: exactamente el mismo patrón

No hay nada nuevo que aprender para GET /v2/beneficiaries. Mismo orden (id descendente), mismo cursor (el último id visto), misma señal de fin (data vacío), misma forma de respuesta (payload.data como arreglo). Solo cambia la ruta:

cURLNodeCopiar
# Primera página de beneficiarios
curl https://alibanca-api-production.up.railway.app/v2/beneficiaries \
  -H "x-api-key: sk_test_tu_llave" \
  -H "x-signature: <firma-del-envelope>"

# Siguiente página: cursor = el último id de beneficiario que viste
curl "https://alibanca-api-production.up.railway.app/v2/beneficiaries?cursor=ben_01HZX7D0Q4" \
  -H "x-api-key: sk_test_tu_llave" \
  -H "x-signature: <firma-del-envelope>"

Recuerda que los beneficiarios se archivan (borrado suave con DELETE), no se borran duro. Al paginar el listado verás los que correspondan según su estado; los archivados no reaparecen en el flujo normal. El mecanismo de cursor, sin embargo, es idéntico byte por byte al de pagos.

Errores comunes y buenas prácticas

La paginación por keyset es simple, pero hay tropiezos clásicos. Evítalos y tu integración recorrerá millones de registros sin sudar:

No pienses en "offset" ni en "número de página"

No existe ?page=3 ni "saltar 60". El único parámetro que avanza la lista es cursor, y su valor es siempre un id real que vino de la respuesta anterior. Si te descubres calculando posiciones, párate: estás usando el modelo mental equivocado.

Toma el cursor del último elemento, no del primero

Como la lista es descendente, el registro más antiguo de la página —el último del arreglo— es el borde por donde continúas. Usar el id del primer elemento te haría dar vueltas o saltarte registros. Siempre data[data.length - 1].id.

Trata el id como texto opaco

El id es un identificador, no un número para hacerle cuentas. No lo conviertas a entero, no le sumes uno, no intentes "predecir" el siguiente. Guárdalo tal cual (string) y pásalo tal cual como cursor.

Maneja siempre la página vacía

Tu bucle tiene que parar cuando data llega vacío. Si asumes que "siempre habrá más", entrarás en un ciclo infinito pidiendo páginas vacías. El break ante data.length === 0 no es opcional.

No asumas un total ni una cantidad fija por página

La paginación por cursor no te promete un conteo total ni un número exacto de elementos por página. La lógica correcta no depende de cuántos vengan: procesa lo que llegue en data, avanza con el último id, y detente cuando venga vacío. Diseña tu recorrido alrededor del cursor y de la señal de fin, nunca alrededor de un total.

Guarda el último cursor si vas a reanudar

Si tu proceso puede cortarse a la mitad (un reinicio, un despliegue), persiste el último cursor que usaste. Al reanudar, arrancas desde ahí en vez de empezar todo el recorrido otra vez. Esa es otra gracia del keyset: el marcador es un simple string que puedes guardar donde quieras.

Firma en los listados (GET)

Un recordatorio para cerrar: paginar no te exime de firmar. Cada GET de listado viaja con x-api-key y x-signature igual que cualquier otro request. La firma es RSA-SHA256 sobre el envelope canónico —método, ruta, api-key, idempotency-key, cuerpo crudo y nonce, en ese orden. En un GET el cuerpo va vacío, pero la ruta incluye el query string, así que la firma de la primera página y la de la página con ?cursor=... son distintas: cada URL firma su propia ruta.

!
El cursor va en la ruta firmada. Como el ?cursor=... forma parte de la ruta que entra al envelope, firma la URL completa de cada página —con su cursor— justo antes de enviarla. Y recuerda el nonce creciente: cada request usa un nonce mayor que el anterior, así que cada página de tu recorrido lleva su propio nonce, siempre hacia arriba. Los detalles del envelope y del nonce high-water están en Autenticación y firma →.

Con esto tienes el cuadro completo: pides la primera página sin cursor, avanzas pasando el último id como cursor, y te detienes cuando data viene vacío. Estable frente a registros nuevos, rápido a cualquier profundidad, y con el mismo patrón para pagos y beneficiarios. Copia el bucle, ajústalo a tu cliente firmado, y ya sabes paginar Alibanca.