FluentaAPI

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.

PresupuestoQué cuentaLightPro
Por organizaciónLas solicitudes de una credencial en una organización, en cualquier ruta60120
Rutas pesadasEsa misma pareja, en las rutas pesadas2020
Toda la credencialLas solicitudes de una credencial en todas sus organizaciones, sumadas300300
Rutas de cuentagetMe, getMyUsage y listOrganizations, que no nombran organización6060
  • 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ónQué lee
getFloorLa planta entera en una respuesta
getEfficiency, getProduction, getBillingUna cifra sobre un rango de días
getOperatorReportEl reporte de un «operario» sobre un rango
getModuleEfficiency, getOperatorEfficiencyLa «Eficiencia» de un «módulo» o de un «operario» sobre un rango
getMonthEl mes
getPausesEl 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:

EncabezadoValor
X-RateLimit-LimitSolicitudes por ventana del presupuesto que estás más cerca de gastar
X-RateLimit-RemainingSolicitudes que le quedan a ese presupuesto en su ventana. 0 en un 429
X-RateLimit-ResetCuándo termina esa ventana, en segundos epoch (UTC)
Retry-AfterSolo 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_SCOPE y un 404 ORGANIZATION_NOT_FOUND se 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_TOKEN tampoco 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'
  • days va de 1 a 90 y vale 30 si no lo envías. El uso se guarda 90 días.
  • totals suma el periodo, endpoints lo separa por método y ruta, y days por día.
  • Cada uno trae requests (rechazos incluidos), errors (las respuestas 400 o más), averageDurationMs, maxDurationMs y lastAt.
  • 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.

En esta página