Guía
Rate Limits
La API pública aplica dos límites en simultáneo, ambos con ventana deslizante: uno por credencial y uno global por IP de origen. Sirven para mantener la plataforma estable para todos.
Los dos límites
Son dos capas independientes, comunes a todos los endpoints de /api/public/v1. No hay categorías distintas por endpoint. Recibís un 429 al exceder cualquiera de las dos.
| Capa | Se cuenta por | Cuota |
|---|---|---|
| Por credencial | El prefijo de la API Key que presenta el request. Si el request no trae una key con formato válido, esta capa cae a la IP de origen. | 120 requests / 60 s |
| Global por IP | La IP de origen. Se aplica siempre, además de la capa anterior y sin importar qué key se use. | 300 requests / 60 s |
Es el error de dimensionamiento más caro. Si tu ERP atiende a varias empresas con una key por cada una, pero todas salen por la misma IP pública, el techo real es el global por IP: cinco keys no dan 600 requests por minuto, dan 300.
Dimensioná contra el límite por IP, no contra la suma de las cuotas por key. Si necesitás más, escribinos antes de desplegar.
Cómo se cuentan
| Aspecto | Valor |
|---|---|
| Algoritmo | Ventana deslizante en segmentos de 15 s, en ambas capas (la cuota se libera de a poco, no de golpe). |
| Rotar el secreto | No estrena cuota: la capa por credencial cuenta por el prefijo de la key, que la rotación conserva. |
| Cola de espera | No hay: al superar la cuota el request se rechaza de inmediato con 429, no queda encolado. |
| Requests sin autenticar | También consumen cuota: el límite corre antes de validar la key, justamente para que una key inválida no cueste una consulta a base de datos. |
Headers
El único header de rate limit que devuelve la API es Retry-After, y sólo en las respuestas 429. Trae los segundos que conviene esperar antes de reintentar.
X-RateLimit-Limit, X-RateLimit-Remaining ni X-RateLimit-Reset. No construyas lógica de cliente que dependa de ellos: no vas a poder anticipar cuánta cuota te queda, sólo reaccionar al 429.Qué pasa cuando lo superás
Recibís un 429 Too Many Requests con el cuerpo de error habitual y el header Retry-After. Respetá ese valor. El cuerpo es el mismo para las dos capas: la respuesta no dice cuál de los dos límites topaste, así que si sospechás del global por IP, medí el tráfico agregado de todas tus keys, no el de una.
# Al superar el límite:
HTTP/1.1 429 Too Many Requests
Retry-After: 15
Content-Type: application/json
{
"code": "api-publica.rate-limit",
"title": "Demasiadas solicitudes para esta API key. Reintenta en unos segundos.",
"status": 429
}Retry respetando Retry-After
La implementación correcta combina el header con un pequeño jitter aleatorio, para que varios procesos tuyos no reintenten todos en el mismo instante:
async function fetchConRateLimit(url, options, intento = 0) {
const res = await fetch(url, options)
if (res.status !== 429) return res
if (intento >= 5) throw new Error('Rate limit persistente')
// Retry-After viene SIEMPRE en los 429 de esta API.
const retryAfter = Number(res.headers.get('Retry-After') ?? '15')
const jitter = Math.random() * 250
await new Promise((r) => setTimeout(r, retryAfter * 1000 + jitter))
return fetchConRateLimit(url, options, intento + 1)
}
await fetchConRateLimit('https://api-publica.dtecomges.cl/api/public/v1/dte', {
method: 'POST',
headers: { 'X-Api-Key': process.env.COMGES_API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify(cuerpo),
})transactionId: es lo que te protege del caso ambiguo (timeout de red) sin duplicar documentos.