Notta Docs

Límites de tasa

Shape del 429, dónde viene el tiempo de espera y el patrón de backoff que también cubre los errores transitorios del SII.

La API de Notta protege su estabilidad con límites de tasa (rate limiting). Cuando superas el límite, la API responde 429 con el código rate_limit y el tiempo de espera en detail.retryAfterSec:

{
  "code": "rate_limit",
  "message": "RateLimitError",
  "detail": { "retryAfterSec": 30 },
  "request_id": "9f3b2c61-7d4a-4e0b-9c2d-1a5e8f3b7c90"
}

Qué límites hay hoy, y dónde

FrenoAlcanceVentanaTope
Emisión por MCPPor organización60 sEl que fije la empresa en /app/mcp (por defecto 60 por minuto)
Acuse o reclamo de compras por MCPPor organización60 sEl mismo tope configurable

Los dos cuentan lo que ocurrió de verdad en la ventana —documentos emitidos por la conexión MCP más pedidos que quedaron esperando aprobación—, así que un umbral de aprobación no vuelve invisible al agente que está en loop.

Las llamadas con API key clásica todavía no tienen un tope por minuto. Lo que sí las gobierna es la cuota mensual de tu plan (los planes están en https://notta.cl/pricing, y en markdown en https://notta.cl/pricing.md); cuando el tope por minuto exista, se anunciará en estos mismos headers antes de aplicarse.

Headers de cuota

Las respuestas de los endpoints con freno anuncian cuánto te queda antes de que topes, en los dos formatos que hoy conviven: el del borrador del IETF y el trío clásico.

RateLimit-Policy: "mcp-emit";q=60;w=60
RateLimit: "mcp-emit";r=48;t=41
RateLimit-Limit: 60
RateLimit-Remaining: 48
RateLimit-Reset: 41
  • q / RateLimit-Limit — el tope de la ventana.
  • w — el largo de la ventana en segundos.
  • r / RateLimit-Remaining — cuánto te queda.
  • t / RateLimit-Reset — segundos hasta que se libere el primer cupo. Es una cota inferior: con la ventana llena, que expire el evento más viejo puede no alcanzar.

Cuando topas, el 429 agrega Retry-After con los mismos segundos. Un endpoint sin freno propio no manda estos headers: preferimos no anunciar una cuota que nadie está contando.

Cómo manejarlo

  • Respeta detail.retryAfterSec: espera esa cantidad de segundos antes de reintentar.
  • Para reintentos sucesivos usa backoff exponencial con jitter.
  • El mismo patrón cubre los errores transitorios del SII: sii_unavailable (503), boleta.rest.unavailable y bhe.rest.unavailable (502) traen el mismo detail.retryAfterSec.
async function withBackoff(fn: () => Promise<Response>, max = 5): Promise<Response> {
  for (let i = 0; i < max; i++) {
    const res = await fn();
    if (res.status !== 429 && res.status !== 502 && res.status !== 503) return res;
    const body = await res.clone().json();
    const waitMs = (body.detail?.retryAfterSec ?? 2 ** i) * 1000;
    await new Promise((r) => setTimeout(r, waitMs));
  }
  throw new Error("rate limit: máximo de reintentos alcanzado");
}

No reintentes los demás 4xx: un 400/422 va a fallar igual hasta que corrijas el payload (ver Catálogo de errores).

Estado actual

El contrato 429 / rate_limit / detail.retryAfterSec ya está mapeado y es estable, así que puedes programar tu manejo de reintentos contra él desde ahora. Los frenos por minuto de la tabla de arriba ya se aplican; el enforcement de cuotas por plan se está habilitando.

Próximos pasos

  • Catálogo de errores: el shape completo de error y qué códigos se reintentan.
  • Paginación: cómo iterar listados sin gatillar el límite con requests gigantes.
  • Referencia API de DTEs: los endpoints sujetos a estos límites.
  • Webhooks: la forma de no gastar cupo siguiendo el estado de un documento. El push no consume tus límites; el polling sí.

Última actualización

En esta página