Límites de uso
Cuántas solicitudes por minuto acepta la API por credencial y por plan, los encabezados X-RateLimit, el 429 y cómo reintentar bien.
La API cuenta las solicitudes de cada credencial en ventanas de 60 segundos.
Al pasar de un presupuesto responde 429 RATE_LIMITED y te dice cuánto
esperar.
Presupuestos
Una solicitud se cuenta contra todos los presupuestos que le aplican.
| Presupuesto | Qué cuenta | Light | Pro |
|---|---|---|---|
| Por organización | Las solicitudes de una credencial en una organización, en cualquier ruta | 60 | 120 |
| Rutas pesadas | Esa misma pareja, en las rutas pesadas | 20 | 20 |
| Toda la credencial | Las solicitudes de una credencial en todas sus organizaciones, sumadas | 300 | 300 |
| Rutas de cuenta | getMe, getMyUsage y listOrganizations, que no nombran organización | 60 | 60 |
- Los números son solicitudes por ventana de 60 segundos.
- Light o Pro es el plan de la organización de la ruta. Una prueba de Pro y una organización exenta cuentan como Pro.
- Una solicitud pesada gasta uno del presupuesto pesado y uno del presupuesto por organización.
- Las rutas de cuenta se cuentan aparte de los otros tres presupuestos.
- El límite es por credencial: cada token de API tiene sus propios presupuestos.
getPlan responde los límites de una organización en rateLimits:
windowSeconds, organization, heavy, credential y account.
Rutas pesadas
Son las que llevan x-rate-limit-heavy: true en el documento OpenAPI:
| Operación | Qué lee |
|---|---|
getFloor | La planta entera en una respuesta |
getEfficiency, getProduction, getBilling | Una cifra sobre un rango de días |
getOperatorReport | El reporte de un «operario» sobre un rango |
getModuleEfficiency, getOperatorEfficiency | La «Eficiencia» de un «módulo» o de un «operario» sobre un rango |
getMonth | El mes |
getPauses | El reporte de «Pausas» |
Salvo getFloor, son rutas de reporte: responden según el plan de la
organización. Mira Planes.
La ventana
La ventana es fija. Se abre con la primera solicitud de un presupuesto y termina 60 segundos después, sin importar cuántas llegaron en medio. Al terminar, la cuenta vuelve a empezar.
Encabezados
Toda respuesta los lleva, también un error:
| Encabezado | Valor |
|---|---|
X-RateLimit-Limit | Solicitudes por ventana del presupuesto que estás más cerca de gastar |
X-RateLimit-Remaining | Solicitudes que le quedan a ese presupuesto en su ventana. 0 en un 429 |
X-RateLimit-Reset | Cuándo termina esa ventana, en segundos epoch (UTC) |
Retry-After | Solo en un 429: segundos que debes esperar |
getMe responde lo mismo en rateLimit (limit, remaining, reset) para
el presupuesto de las rutas de cuenta.
El 429
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1794499260
Retry-After: 23{
"statusCode": 429,
"code": "RATE_LIMITED",
"retryAfterSeconds": 23,
"message": "Demasiadas solicitudes. Espera 23 segundos antes de volver a intentar."
}retryAfterSeconds repite el valor de Retry-After. La misma llamada antes
de ese tiempo vuelve a ser rechazada.
Qué cuenta y qué no
- Los errores también cuentan. Una solicitud con una credencial válida gasta su presupuesto aunque la respuesta sea un 4xx.
- Un 403
INSUFFICIENT_SCOPEy un 404ORGANIZATION_NOT_FOUNDse cuentan contra el presupuesto de las rutas de cuenta, sea cual sea la ruta. - Un 429 no cuenta contra nada. Seguir pidiendo no alarga tu ventana.
- Un 401
INVALID_API_TOKENtampoco cuenta: no hay credencial verificada a la cual cargarlo.
Reintentar bien
Espera lo que dice Retry-After y pon un tope de intentos. Lee el token de
una variable de entorno, no del código.
const BASE = 'https://api.fluenta.io/v1/external';
async function fluenta(path, maxRetries = 5) {
for (let attempt = 0; ; attempt += 1) {
const response = await fetch(`${BASE}${path}`, {
headers: { Authorization: `Bearer ${process.env.FLUENTA_TOKEN}` },
});
if (response.status !== 429) return response;
if (attempt >= maxRetries) throw new Error('Rate limited: retries spent');
const seconds = Number(response.headers.get('Retry-After')) || 1;
await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
}
}
const response = await fluenta('/organizations/42/floor');import os
import time
import requests
BASE = "https://api.fluenta.io/v1/external"
def fluenta(path, max_retries=5):
headers = {"Authorization": f"Bearer {os.environ['FLUENTA_TOKEN']}"}
for attempt in range(max_retries + 1):
response = requests.get(f"{BASE}{path}", headers=headers, timeout=30)
if response.status_code != 429:
return response
if attempt < max_retries:
time.sleep(int(response.headers.get("Retry-After", "1")))
raise RuntimeError("Rate limited: retries spent")
response = fluenta("/organizations/42/floor")Para no llegar al 429, mira X-RateLimit-Remaining: cuando llegue a 0, espera
hasta X-RateLimit-Reset antes de la siguiente solicitud.
Ver tu propio uso
getMyUsage responde lo que tu credencial pidió a la API:
curl 'https://api.fluenta.io/v1/external/me/usage?days=7' \
-H 'Authorization: Bearer fluenta_live_xxxxxxxxxxxxxxxx'daysva de 1 a 90 y vale 30 si no lo envías. El uso se guarda 90 días.totalssuma el periodo,endpointslo separa por método y ruta, ydayspor día.- Cada uno trae
requests(rechazos incluidos),errors(las respuestas 400 o más),averageDurationMs,maxDurationMsylastAt. - Los días son UTC, no los de la planta.
- Solo cuenta esta credencial, en todas sus organizaciones. Las cifras pueden ir unos segundos atrás.