Desarrolladores
Ráfaga por minuto, cuota mensual, la respuesta 429 y cómo reintentar bien.
Hay dos topes por minuto que se aplican siempre, distintos para lecturas y escrituras: el de cada token y el de la cuenta, que comparten todos sus tokens.
| Tipo | Por token | Por cuenta | Qué cuenta |
|---|---|---|---|
| Lectura | 1.200 por minuto | 2.400 por minuto | Rutas cuyo permiso es de lectura (read). |
| Escritura | 300 por minuto | 600 por minuto | Rutas cuyo permiso es de creación, modificación, borrado o una acción crítica. |
El tipo lo decide el permiso que exige la ruta, no el verbo HTTP. Al crear o editar un token puedes fijarle un tope propio en Límites por minuto; el de la cuenta se sigue aplicando igual. BMC puede fijar a una cuenta un tope distinto del de por defecto.
El contador es una ventana fija por minuto natural: se reinicia al empezar cada minuto. Una petición rechazada no consume cupo de ningún contador.
Además de la ráfaga por minuto, cada cuenta tiene una cuota de llamadas al mes, por defecto 50.000. La comparten todos los tokens de la cuenta: crear más tokens no da más cuota. BMC puede ampliarla para una cuenta concreta. Al crear un token puedes fijarle una Cuota mensual propia, dentro de la de la cuenta. La cuota se reinicia el día 1 de cada mes (UTC). El consumo del mes se ve en el área de cliente, en Desarrolladores › API y MCP › Uso de la API.
Los límites son jerárquicos. Una petición pasa solo si está dentro de los tres niveles que apliquen:
Al alcanzar un límite, la API responde 429 con estas cabeceras y un cuerpo que indica qué nivel y qué ventana lo alcanzaron:
HTTP/1.1 429 Too Many Requests
Retry-After: 23
X-RateLimit-Limit: 1200
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1791195600
X-RateLimit-Window: minute
Content-Type: application/problem+json
{
"type": "https://bm.consulting/en/developers/errors#rate_limited",
"title": "Too Many Requests",
"status": 429,
"detail": "Per-minute limit reached (1200, token). Retry in 23 seconds.",
"code": "rate_limited",
"scope": "token",
"window": "minute",
"error": "Too Many Requests"
} | Cabecera | Qué indica |
|---|---|
Retry-After | Segundos hasta que abre la ventana siguiente. Respétalo siempre. |
X-RateLimit-Limit | El tope de la ventana alcanzada. |
X-RateLimit-Remaining | Peticiones que quedan en esa ventana. |
X-RateLimit-Reset | Momento Unix (segundos) en que se reinicia. |
X-RateLimit-Window | minute (ráfaga) o month (cuota mensual). |
scope vale token, acct (cuenta) o user (usuario).
429 y 5xx, duplica la espera en cada intento y limita el número de intentos.limit hasta el máximo de la ruta) para gastar menos peticiones (Paginación).async function bmcFetch(url: string, init: RequestInit = {}, attempts = 5): Promise<Response> {
for (let i = 0; i < attempts; i++) {
const res = await fetch(url, {
...init,
headers: { ...init.headers, Authorization: `Bearer ${process.env.BMC_TOKEN}` },
});
if (res.status !== 429 && res.status < 500) return res;
const wait = Number(res.headers.get("Retry-After")) || 2 ** i;
await new Promise((r) => setTimeout(r, wait * 1000));
}
throw new Error("Demasiados reintentos");
} import os
import time
import requests
def bmc_get(url, params=None, attempts=5):
for i in range(attempts):
res = requests.get(
url,
params=params,
headers={"Authorization": f"Bearer {os.environ['BMC_TOKEN']}"},
timeout=30,
)
if res.status_code != 429 and res.status_code < 500:
return res
time.sleep(int(res.headers.get("Retry-After", 2 ** i)))
raise RuntimeError("Demasiados reintentos")