Todas las AI API tienen rate limits. Toparse con ellos en desarrollo es molesto. Toparse con ellos en producción y sus usuarios verán errores, streams parciales y tiempos de espera que parecen aleatorios hasta que se inspecciona el patrón.
El error principal es tratar el rate limiting como un único problema. Por lo general, se trata de cuatro problemas diferentes que se esconden tras el mismo 429:
- requests per minute (solicitudes por minuto)
- tokens per minute (tokens por minuto)
- concurrent in-flight requests (solicitudes concurrentes en curso)
- account-level or project-level quota exhaustion (agotamiento de cuota a nivel de cuenta o proyecto)
Si construye su solución pensando solo en uno de ellos, los demás seguirán afectándole.
Si aún se encuentra en la etapa de migración de proveedor, lea primero la guía de migración. Si está evaluando si un gateway ayuda con el fallback y la carga operativa, la comparativa de OpenRouter es la mejor lectura complementaria.
Qué significan realmente los Rate Limits
Límites de solicitudes (Request limits)
Este es el más obvio. Envió demasiadas solicitudes en un intervalo de tiempo corto.
Límites de tokens (Token limits)
Este es el que los equipos suelen subestimar. Un solo prompt largo puede consumir tanto presupuesto como muchas solicitudes pequeñas. Si de repente añade un prompt de sistema de 20 KB, el recuento de solicitudes puede parecer saludable mientras que el presupuesto de tokens ya se ha agotado.
Límites de concurrencia (Concurrency limits)
Algunos proveedores y gateways aceptan perfectamente su promedio por minuto hasta que abre cincuenta streams a la vez. El plan de rate es correcto, pero la forma del pico (burst) no lo es.
Agotamiento de cuota o saldo (Quota exhaustion)
A menudo aparece como un síntoma de “rate limit” en los dashboards porque el resultado operativo es el mismo: las llamadas dejan de tener éxito. Pero la solución es diferente. El backoff es inútil si el problema es un saldo de cero.
Cómo aplican los límites los proveedores habitualmente
Los números exactos cambian con el tiempo, por lo que codificar una tabla de precios pública en la documentación de su aplicación envejece mal. El patrón estable es este:
- Los proveedores al estilo OpenAI suelen exponer headers de solicitudes y tokens, y ajustan su límite máximo en función del historial de la cuenta o del nivel de uso.
- Los proveedores al estilo Anthropic suelen aplicar tanto el throughput a nivel de minuto como límites de proyecto más amplios, especialmente en modelos de gama alta.
- Los proveedores al estilo Google a menudo separan el comportamiento del nivel gratuito del nivel de pago y pueden variar los límites drásticamente según la familia de modelos.
- Los agregadores añaden una capa más de límites sobre las restricciones de los proveedores originales (upstream), pero a cambio pueden redirigir a otros canales cuando un upstream está temporalmente saturado.
Trate los límites de los proveedores como configuración dinámica, no como constantes.
Lectura de los Headers de Rate Limit
Todos los principales proveedores devuelven información sobre el rate limit en los headers de la respuesta:
x-ratelimit-limit-requests: 500
x-ratelimit-remaining-requests: 499
x-ratelimit-reset-requests: 60s
x-ratelimit-limit-tokens: 200000
x-ratelimit-remaining-tokens: 199500
Utilice estos headers de forma proactiva. No espere a un error 429 para reducir la velocidad.
El hábito operativo que busca es sencillo:
- Registre los headers cuando la operación tenga éxito, no solo cuando falle.
- Genere alertas cuando la capacidad restante caiga por debajo de un umbral.
- Moldee el tráfico antes de que la siguiente solicitud cruce la línea.
Si solo consulta los headers después de un fallo, ya va con retraso.
Construcción de la lógica de reintento (Retry Logic)
La forma incorrecta
# Don't do this
import time
def call_api(messages):
while True:
try:
return client.chat.completions.create(
model="gpt-4.1",
messages=messages
)
except Exception:
time.sleep(1) # Fixed delay, no backoff, catches everything
Problemas: no hay exponential backoff, captura errores que no son reintentables, no hay límite máximo de reintentos, no hay jitter.
La forma correcta
import time
import random
from openai import RateLimitError, APIError, APIConnectionError
def call_with_retry(messages, model="gpt-4.1", max_retries=3):
"""Retry with exponential backoff and jitter."""
for attempt in range(max_retries + 1):
try:
return client.chat.completions.create(
model=model,
messages=messages
)
except RateLimitError as e:
if attempt == max_retries:
raise
# Use retry_after from response if available
wait = getattr(e, 'retry_after', None)
if wait is None:
wait = (2 ** attempt) + random.uniform(0, 1)
print(f"Rate limited. Waiting {wait:.1f}s (attempt {attempt + 1})")
time.sleep(wait)
except APIConnectionError:
if attempt == max_retries:
raise
wait = (2 ** attempt) + random.uniform(0, 1)
time.sleep(wait)
except APIError as e:
# Don't retry client errors (400, 401, 403)
if e.status_code and 400 <= e.status_code < 500:
raise
if attempt == max_retries:
raise
time.sleep((2 ** attempt) + random.uniform(0, 1))
Principios clave:
- Exponential backoff: 1s, 2s, 4s, 8s
- Jitter: se añade un valor aleatorio de 0-1s para evitar el efecto de "manada atronadora" (thundering herd)
- Respetar el header
retry_aftercuando se proporcione - No reintentar errores del cliente (solicitud incorrecta, fallo de autenticación)
- Establecer un recuento máximo de reintentos
Dos reglas de producción adicionales son importantes:
- nunca reintente indefinidamente en un endpoint de streaming
- nunca reintente una solicitud que ya esté vinculada a efectos secundarios visibles para el usuario, a menos que la operación sea idempotente
Las chat completions suelen ser seguras de reintentar. Los efectos secundarios activados por herramientas (tools) a menudo no lo son.
Versión Async
import asyncio
import random
from openai import AsyncOpenAI, RateLimitError
async_client = AsyncOpenAI(
api_key="sk-lemon-xxx",
base_url="https://api.lemondata.cc/v1"
)
async def call_with_retry_async(messages, model="gpt-4.1", max_retries=3):
for attempt in range(max_retries + 1):
try:
return await async_client.chat.completions.create(
model=model,
messages=messages
)
except RateLimitError:
if attempt == max_retries:
raise
wait = (2 ** attempt) + random.uniform(0, 1)
await asyncio.sleep(wait)
Moldear el tráfico antes de que se convierta en una tormenta de reintentos
La lógica de reintento es solo la mitad de la solución. Si su upstream ya está sobrecargado, los reintentos pueden convertir un pico de tráfico en una interrupción del servicio autoinfligida.
Tres controles marcan la diferencia:
1. Cola por tenant o usuario
Si un cliente inicia un trabajo por lotes masivo, no querrá que todos los demás clientes hereden el radio de impacto.
2. Limitar los streams concurrentes
Los endpoints de streaming son fáciles de subestimar porque cada solicitud "parece" barata mientras permanece abierta durante mucho tiempo.
3. Recortar los prompts antes de enviarlos
Los límites de tokens suelen ser el techo real. Un prompt que es el doble de largo reduce el throughput seguro aproximadamente a la mitad.
Token Bucket del lado del cliente
Para aplicaciones de alto throughput, implemente rate limiting en el lado del cliente para evitar alcanzar los límites del servidor:
import time
import asyncio
class TokenBucket:
def __init__(self, rate: float, capacity: int):
self.rate = rate # tokens per second
self.capacity = capacity # max burst size
self.tokens = capacity
self.last_refill = time.monotonic()
async def acquire(self, tokens: int = 1):
while True:
now = time.monotonic()
elapsed = now - self.last_refill
self.tokens = min(
self.capacity,
self.tokens + elapsed * self.rate
)
self.last_refill = now
if self.tokens >= tokens:
self.tokens -= tokens
return
# Wait for enough tokens
wait = (tokens - self.tokens) / self.rate
await asyncio.sleep(wait)
# 500 requests per minute = ~8.3 per second
limiter = TokenBucket(rate=8.0, capacity=20)
async def rate_limited_call(messages, model="gpt-4.1"):
await limiter.acquire()
return await async_client.chat.completions.create(
model=model,
messages=messages
)
Los token buckets son útiles cuando conoce su límite máximo. Son aún mejores cuando los ajusta a partir de los datos observados en los headers en lugar de una estimación codificada.
Fallback de modelo ante Rate Limits
Cuando su modelo principal tiene el rate limitado, recurra a una alternativa:
FALLBACK_CHAIN = [
"claude-sonnet-4-6",
"gpt-4.1",
"gpt-4.1-mini",
]
async def call_with_fallback(messages):
for model in FALLBACK_CHAIN:
try:
return await async_client.chat.completions.create(
model=model,
messages=messages
)
except RateLimitError:
continue
raise Exception("All models rate limited")
Aquí es donde los gateways de modelos ayudan, pero solo si el fallback es deliberado. No salte silenciosamente de un modelo de razonamiento premium a un modelo económico diminuto sin pensar en el impacto para el usuario.
Una cadena de fallback razonable es:
- mismo proveedor, modelo hermano más pequeño
- familia de modelos equivalente de otro proveedor
- solo entonces un modelo más barato o con menor contexto
Si mezcla el “fallback por disponibilidad” con el “fallback por coste” en un solo paso, la depuración se vuelve complicada rápidamente.
Monitoreo del uso de Rate Limits
Realice un seguimiento de su consumo de rate limits para detectar problemas antes de que afecten a los usuarios:
import logging
def log_rate_limits(response):
headers = response.headers
remaining = headers.get("x-ratelimit-remaining-requests")
limit = headers.get("x-ratelimit-limit-requests")
if remaining and int(remaining) < int(limit) * 0.1:
logging.warning(
f"Rate limit warning: {remaining}/{limit} requests remaining"
)
Establezca alertas cuando la capacidad restante caiga por debajo del 10%. Esto le da tiempo para implementar el throttling antes de que los usuarios vean errores 429.
También debería registrar:
- ID de solicitud
- modelo
- estimación del tamaño de entrada
- duración del stream
- recuento de reintentos
- resultado final (
success,rate_limited,network_error,quota_exhausted)
Sin esos campos, los incidentes de rate limit se convierten en meras conjeturas.
Una lista de verificación de producción sencilla
Antes de considerar que su chatbot o agente es “seguro frente a rate limits”, verifique estos cinco puntos:
- Existe una política de reintentos limitada tanto para rutas síncronas como asíncronas.
- Registra los headers de rate limit en las respuestas exitosas.
- Existe un moldeado de tráfico por usuario o por tenant antes de la llamada al upstream.
- Existe al menos un modelo de fallback validado.
- El frontend recibe un estado de error limpio en lugar de un stream colgado.
Si está construyendo la aplicación completa y no solo la primitiva de reintento, la guía de chatbot con una sola clave muestra cómo encajan estas piezas en un servicio FastAPI real.
Resumen
El rate limiting no es un caso aislado. Es una condición operativa normal para cualquier producto de AI con uso real. Los equipos que lo manejan bien no tienen límites mágicamente más altos. Tratan el throughput, los reintentos y el fallback como parte del diseño de la aplicación desde el principio.
Cree una API key en LemonData, pruebe su ruta de reintento antes del tráfico de producción y prepárese para el próximo 429 antes de que llegue.
| Estrategia | Cuándo usarla |
|---|---|
| Exponential backoff | Siempre (base) |
| Rate limiter en el cliente | Apps de alto throughput (>100 RPM) |
| Fallback de modelo | Apps en producción con requisitos de SLA |
| Monitoreo proactivo | Cualquier despliegue en producción |
| Batch API | Cargas de trabajo que no son en tiempo real |
El objetivo no es evitar los rate limits por completo. Es manejarlos con elegancia para que sus usuarios nunca lo noten.
Construya aplicaciones de AI resilientes: lemondata.cc ofrece enrutamiento multicanal que gestiona automáticamente los rate limits de los proveedores. Una sola API key, más de 300 modelos.
