Límites de uso
La API tiene un presupuesto de llamadas por minuto. Es de la empresa, no de la clave.
Cuánto tiene cada plan
Sección titulada «Cuánto tiene cada plan»| Plan | API | Llamadas por minuto | Claves activas |
|---|---|---|---|
| Gratis | No | 0 | 0 |
| Start | Sí | 60 | 3 |
| Pleno | Sí | 180 | 10 |
| Supra | Sí | 600 | 25 |
Esta tabla se genera del código que decide de verdad: es la misma función que la API llama en cada solicitud para saber si la llamada pasa.
El tope que vale para su empresa ahora viene en la respuesta de GET /public/v1/me, en limits.requests_per_minute. Léalo de ahí en vez de fijar el número en su código: la empresa puede cambiar de plan, y el panel también permite ajustar el valor de una empresa en particular.
Un cambio de plan tarda hasta un minuto en valer en la API, porque el plan resuelto queda en memoria por ese tiempo.
Los encabezados
Sección titulada «Los encabezados»Toda respuesta que pasa por el contador trae tres encabezados:
| Encabezado | Qué es | Cómo usarlo |
|---|---|---|
RateLimit-Limit | El tope por minuto de la empresa | Compárelo con lo que planea disparar |
RateLimit-Remaining | Cuántas llamadas todavía caben en la ventana | Por debajo de un margen suyo, baje el ritmo antes de recibir un 429 |
RateLimit-Reset | En cuántos segundos se abre el próximo lugar | Úselo como intervalo mínimo cuando RateLimit-Remaining llegue a cero |
En el 429 entra un cuarto:
| Encabezado | Qué es |
|---|---|
Retry-After | Cuántos segundos esperar antes de intentar de nuevo |
La ventana es deslizante: mira los últimos 60 segundos a partir de ahora, no el minuto del reloj. Por eso el RateLimit-Reset baja de a poco en vez de volver a cero de una vez.
Una empresa sin tope en la aplicación no pasa por el contador, y entonces esos encabezados no aparecen. En ese caso limits.requests_per_minute viene null en GET /public/v1/me.
Qué hacer en el 429
Sección titulada «Qué hacer en el 429»{ "error": { "type": "rate_limit_error", "code": "rate_limit_exceeded", "message": "Se alcanzó el límite de llamadas por minuto. Intente de nuevo en unos instantes.", "request_id": "3d81b6ac-2f45-4a0e-9c7b-1e5f8a2d4c60", "doc_url": "https://docs.fatureihoje.com/es/errors#rate_limit_exceeded" }}- Espere lo que dice el
Retry-After. Viene en segundos y se calcula sobre la ventana real, no es una estimación. - Retroceda progresivamente. Si el segundo 429 llega enseguida, duplique la espera en cada intento, hasta un tope suyo.
- Disperse un poco. Sume algunos milisegundos aleatorios a la espera. Sin eso, varias colas que recibieron 429 en el mismo instante vuelven juntas en el mismo instante.
- Nunca repita en un bucle apretado. Repetir al instante no sirve y además atrasa las llamadas buenas de la misma empresa, que comparten el mismo contador.
Una llamada rechazada por permiso es distinta: el 403 ocurre después del contador y sí gasta presupuesto, a propósito. Sondear una ruta que la clave no puede usar no sale gratis.
Leer los encabezados
Sección titulada «Leer los encabezados»curl -sS -D - -o /dev/null https://api.fatureihoje.com/public/v1/me \ -H "Authorization: Bearer $FH_API_KEY"-D - imprime los encabezados y -o /dev/null descarta el cuerpo.
const res = await fetch('https://api.fatureihoje.com/public/v1/me', { headers: { Authorization: `Bearer ${process.env.FH_API_KEY}` },});
console.log('límite', res.headers.get('RateLimit-Limit'));console.log('quedan', res.headers.get('RateLimit-Remaining'));console.log('se reabre en', res.headers.get('RateLimit-Reset'), 'segundos');
if (res.status === 429) { const espera = Number(res.headers.get('Retry-After') ?? 1); console.log('esperar', espera, 'segundos');}Ejecute con node limites.mjs.
<?php
$headers = [];$ch = curl_init('https://api.fatureihoje.com/public/v1/me');curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('FH_API_KEY')], CURLOPT_HEADERFUNCTION => function ($ch, $line) use (&$headers) { $parts = explode(':', $line, 2); if (count($parts) === 2) { $headers[strtolower(trim($parts[0]))] = trim($parts[1]); } return strlen($line); },]);
// curl_exec devuelve false cuando la conexión ni siquiera ocurrió, sin lanzar nada.if (curl_exec($ch) === false) { fwrite(STDERR, 'falla de red: ' . curl_error($ch) . PHP_EOL); exit(1);}
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
echo 'límite ', $headers['ratelimit-limit'] ?? '', PHP_EOL;echo 'quedan ', $headers['ratelimit-remaining'] ?? '', PHP_EOL;echo 'se reabre en ', $headers['ratelimit-reset'] ?? '', ' segundos', PHP_EOL;
if ($status === 429) { echo 'esperar ', $headers['retry-after'] ?? '1', ' segundos', PHP_EOL;}Ejecute con php limites.php. Sin curl_close: desde PHP 8 el recurso se libera solo, y en 8.5 la función quedó obsoleta.
import osimport urllib.errorimport urllib.request
request = urllib.request.Request( "https://api.fatureihoje.com/public/v1/me", headers={"Authorization": f"Bearer {os.environ['FH_API_KEY']}"},)
try: response = urllib.request.urlopen(request) status, headers = response.status, response.headersexcept urllib.error.HTTPError as failure: status, headers = failure.code, failure.headers
print("límite", headers.get("RateLimit-Limit"))print("quedan", headers.get("RateLimit-Remaining"))print("se reabre en", headers.get("RateLimit-Reset"), "segundos")
if status == 429: print("esperar", headers.get("Retry-After", "1"), "segundos")Ejecute con python3 limites.py.
Otros límites que también devuelven 429
Sección titulada «Otros límites que también devuelven 429»El presupuesto por minuto no es el único contador. La autenticación tiene un tope propio de fallas por IP: una clave equivocada repetida muchas veces en el mismo minuto pasa a recibir 429 incluso antes de que la clave sea verificada. Ese contador solo cuenta lo que salió mal, así que el uso normal no lo roza. Está descrito en Autenticación.
Los dos usan el mismo code, rate_limit_exceeded, y los dos traen Retry-After. La diferencia aparece en el contexto: si sus llamadas están siendo aceptadas y de repente llega un 429, es el presupuesto de la empresa; si están siendo rechazadas con 401 y después se convierte en 429, es el tope de fallas.
Límite de claves
Sección titulada «Límite de claves»La tabla de arriba también trae cuántas claves activas permite cada plan. Una clave revocada y una clave vencida no cuentan; una clave en convivencia de rotación sí cuenta, porque todavía autentica.
Pasar de ese número devuelve plan_limit_reached, con estado 403. Rotar una clave existente no choca con ese límite; crear una más, sí.
Próximo paso
Sección titulada «Próximo paso»- Errores: el catálogo completo, con
rate_limit_exceededyplan_limit_reached. - Paginación: recorrer una lista larga es lo que más consume presupuesto.