Ir al contenido

Desarrolladores

Límites de uso

Ráfaga por minuto, cuota mensual, la respuesta 429 y cómo reintentar bien.

Límites por minuto

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.

TipoPor tokenPor cuentaQué cuenta
Lectura1.200 por minuto2.400 por minutoRutas cuyo permiso es de lectura (read).
Escritura300 por minuto600 por minutoRutas 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.

Cuota mensual

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.

Token, cuenta y usuario

Los límites son jerárquicos. Una petición pasa solo si está dentro de los tres niveles que apliquen:

  • Token: siempre aplica (1.200 / 300 por minuto, salvo tope propio).
  • Cuenta: siempre aplica. Todos los tokens cuya cuenta principal es esa comparten 2.400 / 600 por minuto, salvo que BMC fije otro tope a la cuenta. Crear más tokens no multiplica la ráfaga.
  • Usuario: si BMC fijó un tope al usuario que creó el token, lo comparten todos sus tokens.

Respuesta 429

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"
}
CabeceraQué indica
Retry-AfterSegundos hasta que abre la ventana siguiente. Respétalo siempre.
X-RateLimit-LimitEl tope de la ventana alcanzada.
X-RateLimit-RemainingPeticiones que quedan en esa ventana.
X-RateLimit-ResetMomento Unix (segundos) en que se reinicia.
X-RateLimit-Windowminute (ráfaga) o month (cuota mensual).

scope vale token, acct (cuenta) o user (usuario).

Buenas prácticas

  • Respeta Retry-After. Espera esos segundos antes de reintentar.
  • Espera creciente. Ante 429 y 5xx, duplica la espera en cada intento y limita el número de intentos.
  • Usa webhooks en lugar de consultar cada poco. Si necesitas enterarte de que una factura se emitió o un contacto cambió, suscríbete al evento y pide el detalle solo cuando llegue (Webhooks).
  • Almacena en caché lo que cambia poco (catálogo de cuentas, contactos) y vuelve a pedirlo solo cuando haga falta.
  • Filtra en origen con los parámetros de fecha de cada ruta en lugar de descargar todo y filtrar después.
  • Pagina con un tamaño grande (limit hasta el máximo de la ruta) para gastar menos peticiones (Paginación).
  • No lances peticiones en paralelo sin tope. Limita la concurrencia de tu cliente.
TypeScript (fetch)
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");
}
Python (requests)
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")
Email
Contacto