Paginación y límites de tasa
Implementado en el gateway; el servicio aún no está desplegado.
Paginación por cursor
Las listas se recorren por cursor (keyset), sin número de página ni total exacto. Así una lista de movimientos no se desordena si llegan operaciones nuevas mientras la recorres.
| Parámetro | Descripción |
|---|---|
limit | Tamaño de página, de 1 a 200. Por defecto 50 |
after | El valor de siguiente de la página anterior. Omítelo en la primera página |
Cada página trae items y siguiente. Cuando siguiente es null, no hay más.
# Primera página
curl 'https://api-cms.example.com/v1/accounts/3f0c2a7e-5b1d-4e8f-a9c2-7d6e5f4a3b21/movements?limit=100&desde=2026-10-01' \
-H "Authorization: Bearer $TOKEN"
# Siguiente página: mismos filtros, más el cursor
curl 'https://api-cms.example.com/v1/accounts/3f0c2a7e-5b1d-4e8f-a9c2-7d6e5f4a3b21/movements?limit=100&desde=2026-10-01&after=eyJ0IjoiMjAyNi0xMC0xOVQwMDozMDowMC0wNDowMCJ9.c2ln' \
-H "Authorization: Bearer $TOKEN"
async function* todos(url, token) {
let after = null;
do {
const u = new URL(url);
if (after) u.searchParams.set('after', after);
const r = await fetch(u, { headers: { Authorization: `Bearer ${token}` } });
if (!r.ok) throw new Error(`HTTP ${r.status}`);
const pagina = await r.json();
yield* pagina.items;
after = pagina.siguiente;
} while (after);
}
El cursor está firmado
El cursor es opaco y lo firma el gateway para tu cliente, la operación, el recurso y los filtros de la consulta. Por eso:
- No lo construyas ni lo modifiques: un cursor alterado responde 400
CURSOR_INVALIDO. - Úsalo con los mismos filtros con que lo obtuviste (por ejemplo, el mismo
desde,hastaynaturaleza) y sobre el mismo recurso. Si cambias un filtro, empieza de nuevo sinafter. - No lo compartas entre clientes: solo vale para el tuyo.
Rangos de fechas
En las listas con desde y hasta, desde es inclusivo y hasta exclusivo. Si el rango supera 93 días, hasta es obligatorio; si falta o el rango es incoherente, 400 RANGO_FECHAS_INVALIDO.
Parámetros estrictos
La consulta se valida contra el contrato: un parámetro no declarado, un parámetro repetido o un booleano distinto de true o false responden 400 VALIDACION. Un número de tarjeta en la consulta también se rechaza (400, campo marcado como «dato sensible»).
Límites de tasa
Cada cliente tiene cubetas separadas por clase de operación. Valores por defecto (el emisor puede fijar otros en tu registro):
| Clase | Límite por defecto |
|---|---|
| Lecturas | 50 por segundo |
| Escrituras | 10 por segundo |
Lotes (POST /v1/limit-jobs) | 1 por minuto |
Todas las respuestas traen tus cabeceras de cuota:
| Cabecera | Significado |
|---|---|
X-RateLimit-Limit | Tamaño de la cubeta |
X-RateLimit-Remaining | Solicitudes que te quedan |
X-RateLimit-Reset | Segundos hasta que se renueva |
Al excederla, 429 LIMITE_DE_TASA con Retry-After:
HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
Retry-After: 1
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1
{
"type": "urn:finora:cms-api:limite-de-tasa",
"title": "Demasiadas solicitudes",
"status": 429,
"codigo": "LIMITE_DE_TASA",
"requestId": "01JBX2Q7M8N9P0R1S2T3V4W5X6"
}
Mira X-RateLimit-Remaining para frenar antes de llegar a cero, y reparte las lecturas masivas en el tiempo.
Otros topes y cabeceras
| Elemento | Valor |
|---|---|
| Tamaño del cuerpo | 64 KB (413 CUERPO_DEMASIADO_GRANDE) |
| Lote en JSON | Hasta 1.000 filas (413 LOTE_DEMASIADO_GRANDE). Lote por CSV: Próximamente |
Content-Type de las escrituras | application/json (415 TIPO_NO_SOPORTADO) |
X-Finora-Api-Version | Opcional; la única versión es 1. Otra responde 400 VERSION_NO_SOPORTADA |
X-Request-Id | Toda respuesta lo trae; inclúyelo al pedir soporte |
X-Finora-Operador-Final | Opcional, [A-Za-z0-9._@-] hasta 64 caracteres: quién pidió la acción de tu lado |