Skip to content

Rate limiting

Para mantener la API rápida y barata para todos, aplicamos un límite por API key.

Límites actuales (Fase 1)

DimensiónLímiteVentana
Por key600 requests1 minuto

Los límites se aplican por API key, que identifica a tu integración de forma estable y única.

Si excedes el límite, recibes:

HTTP/1.1 429 Too Many Requests
Retry-After: 35
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0

con body:

json
{ "error": "rate_limited", "message": "Too many requests. Please retry later." }

Retry-After es el número de segundos que debes esperar antes del próximo intento.

Protección contra brute-force

No imponemos un lockout adicional sobre intentos fallidos: el espacio de claves es de 128 bits aleatorios (32 hex + prefix finova_sk_), así que adivinar un token válido a fuerza bruta es matemáticamente intratable incluso a 600 intentos por minuto. El digest se compara en tiempo constante y el SHA-256 + pepper en la base de datos garantiza que un volcado de la BD no produce claves utilizables.

Estrategia recomendada

  • Backoff exponencial ante 429: espera 1s, luego 2s, luego 4s, etc., hasta el límite que decidas (sugerido 30s).
  • Respeta Retry-After si viene. Es lo que el server te dice que ya considera "seguro" para reintentar.
  • No reintentes 4xx que no sean 429. Reintentar un 422 validation_failed con el mismo body siempre va a fallar igual.
  • Una key por integración. Si una integración necesita más de 600 req/min, divide la carga en varias keys con el mismo set de scopes — cada una tendrá su propio bucket.

Subir límites

Durante la beta los límites son fijos. En Fase 4 (Billing) podrás escalar tus tiers desde la UI. Si necesitas más antes, escríbenos.

Ejemplo de backoff

javascript
async function withRetry(fn, maxAttempts = 5) {
  let delay = 1000
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    const res = await fn()
    if (res.status !== 429) return res

    const retryAfter = Number(res.headers.get('Retry-After')) || delay / 1000
    await new Promise(r => setTimeout(r, retryAfter * 1000))
    delay = Math.min(delay * 2, 30000)
  }
  throw new Error('Max retries exhausted')
}

Hecho con cuidado por Finova.