Isaak aplica límites por token y por familia de endpoint para proteger AEAT y la experiencia compartida. Los límites escalan con el plan del tenant.
Por token y por minuto. Las llamadas dentro del mismo tenant se suman entre todas las keys activas.
| Plan | Read | Write | AEAT | MCP |
|---|---|---|---|---|
| Free | 60 / min | 10 / min | 5 / min | 30 / min |
| Starter | 120 / min | 30 / min | 15 / min | 60 / min |
| Pro | 300 / min | 90 / min | 60 / min | 180 / min |
| Business | 600 / min | 300 / min | 120 / min | 360 / min |
Read = GETs sobre facturas, contactos, fiscal. Write = POST/PATCH sobre borradores. AEAT = emisiones VeriFactu / validaciones contra AEAT. MCP = llamadas tools/call al servidor MCP.
La API expone tres headers de inspección y un header de retry. Úsalos para evitar 429 proactivamente en lugar de reactivamente.
# Headers que la API devuelve en cada respuesta 2xx
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 287
X-RateLimit-Reset: 1759320000 # epoch seconds
# Cuando devolvemos 429 Too Many Requests
Retry-After: 23 # segundos hasta que se libera un slotRecomendamos backoff exponencial con jitter para errores 429 y 5xx. Respeta siempre el header Retry-After cuando esté presente.
// Backoff exponencial con jitter
async function withRetry(fn, attempt = 0) {
try {
return await fn();
} catch (err) {
if (err.status !== 429 && err.status < 500) throw err;
if (attempt >= 5) throw err;
const baseMs = Math.min(30_000, 1000 * 2 ** attempt);
const jitter = Math.random() * 500;
const wait = err.retryAfter ? err.retryAfter * 1000 : baseMs + jitter;
await new Promise(r => setTimeout(r, wait));
return withRetry(fn, attempt + 1);
}
}