Límites de OpenRouter y error HTTP 429: diagnóstico y recuperación
Guía práctica para diagnosticar errores HTTP 429 y 402 en OpenRouter: aprende a diferenciar las cuotas del nivel gratuito de los límites de plataforma y del proveedor upstream, inspecciona cabeceras y metadatos, e implementa un script de reintento resiliente en Python.
Índice

Al enviar un volumen elevado de solicitudes a modelos de inteligencia artificial a través de la pasarela de OpenRouter, las aplicaciones cliente encuentran con frecuencia el código de estado HTTP 429 Too Many Requests. Dado que OpenRouter agrega decenas de proveedores de inferencia independientes, este error puede originarse en capas completamente distintas de la infraestructura. Un reintento inmediato e ingenuo a menudo provoca un bloqueo por frecuencia en el cliente o desperdicia intentos de ejecución de forma inútil. Restablecer un flujo de solicitudes estable requiere identificar la capa exacta donde se produjo el fallo y seleccionar la acción correctiva adecuada: aplicar un tiempo de espera, reducir la concurrencia, cambiar de proveedor de modelos o ajustar los límites de gasto.
Niveles de limitaciones: nivel gratuito, plataforma y upstream
La documentación oficial sobre OpenRouter Limits distingue entre los límites de frecuencia de la plataforma, el rendimiento de los proveedores individuales y el bloqueo por saldo en la cuenta.
En la página de OpenRouter Pricing, el plan gratuito base establece un límite máximo de 50 solicitudes al día para modelos gratuitos accesibles públicamente (aquellos identificados con el sufijo :free). Dado que los límites exactos de solicitudes por minuto (RPM) y los umbrales de cada nivel pueden cambiar con el tiempo, los valores numéricos vigentes deben verificarse siempre en la tabla de límites en vivo.
Al diagnosticar fallos, es fundamental distinguir los niveles responsables de generar el código 429 y el estado financiero asociado 402:
- Límites de frecuencia de la plataforma OpenRouter. Ocurren cuando las solicitudes dirigidas al propio enrutador son demasiado frecuentes. La cuota diaria del grupo gratuito pertenece a esta categoría de limitaciones de la plataforma (y no a una restricción de un proveedor externo independiente): al exceder el límite de 50 solicitudes al día en modelos gratuitos, la plataforma rechaza las peticiones hasta que el contador diario se restablezca. Cuando se produce un límite de frecuencia en la plataforma, el servidor devuelve las cabeceras
X-RateLimit-Limit,X-RateLimit-RemainingyX-RateLimit-Reset. Las respuestas exitosas con códigoHTTP 200no incluyen estas cabeceras operativas, por lo que las aplicaciones cliente no pueden depender de ellas en condiciones normales de tráfico para anticipar los límites. - Límites de frecuencia del proveedor ascendente (upstream rate limit). Los modelos están alojados físicamente y se ejecutan en la infraestructura de empresas específicas (como Anthropic, Meta, DeepSeek, Mistral o proveedores de computación en la nube especializados). Si la infraestructura de dicho socio upstream se sobrecarga, OpenRouter reenvía el código 429 al cliente. En la estructura del cuerpo de respuesta, el campo
error.metadata.provider_codecontiene el código de error original devuelto por el proveedor upstream cuando está disponible (por ejemplo, 429), y no el nombre o identificador en cadena del proveedor. - Restricciones financieras (
HTTP 402 Payment Required). La documentación de OpenRouter Limits separa explícitamente el agotamiento de fondos de las restricciones de frecuencia. El estado 402 señala un saldo insuficiente o negativo en la cuenta de la organización, o que se ha alcanzado el límite de gasto configurado en una clave de API específica (key cap), en lugar de indicar exclusivamente un saldo contable exactamente en cero.
Los parámetros actuales de una clave se pueden consultar mediante una solicitud directa:
curl -s -X GET https://openrouter.ai/api/v1/key \
-H "Authorization: Bearer $OPENROUTER_API_KEY"
La respuesta devuelve los campos usage, limit_reset y limit_remaining. Un valor de limit_remaining: null indica que no se ha establecido un límite de gasto local (key cap) para esta clave de API en particular. Este valor no confirma la existencia de fondos en el saldo principal de la organización, sino que únicamente comprueba que no existe una restricción artificial configurada para dicho token individual.
Tabla de resumen de diagnóstico
| Código de respuesta y señales | Fuente de inspección | Causa raíz | Acción del cliente |
|---|---|---|---|
HTTP 402 Payment Required | Endpoint GET /api/v1/key o panel de control | Saldo de la organización insuficiente o negativo, o límite de gasto de la clave alcanzado (limit_remaining: 0) | Recargar saldo o incrementar la cuota del token; los reintentos automáticos sin modificaciones son inútiles |
HTTP 429 con cabeceras X-RateLimit-* | Cabeceras HTTP de respuesta de la pasarela | Se superó el límite de concurrencia o de frecuencia de peticiones de la propia plataforma OpenRouter | Inspeccionar la cabecera Retry-After y reducir el número de hilos concurrentes |
HTTP 429 con código en provider_code | Campo JSON error.metadata.provider_code (opcional) | Sobrecarga o interrupción del proveedor upstream (código de error original del upstream) | Cambiar de modelo o de proveedor puede ayudar, pero no garantiza la recuperación; atribuir el proveedor específico en Activity > provider_responses |
HTTP 429 en modelos :free | Sección OpenRouter Pricing | Cuota diaria de la plataforma agotada (50 peticiones/día) o capacidad total saturada | Pasar a un modelo de pago o posponer la ejecución de la tarea |
HTTP 429 con enrutamiento complejo | Panel de control: Activity > Petición > View Raw Metadata | Fallo intermedio de nodos en el objeto provider_responses | Identificar al proveedor con fallos y verificar la cadena de sustitución en la guía de BYOK/Routing |
Análisis de dos escenarios: límite de clave frente a fallo del proveedor
El comportamiento de la aplicación cliente ante la interrupción de peticiones se fundamenta en la inspección de metadatos. A continuación se presentan dos escenarios hipotéticos (ejemplos ilustrativos y no observaciones tomadas de una cuenta real).
Escenario 1 (hipotético): Agotamiento del límite local del token
En este escenario hipotético, un proceso en segundo plano recibe una respuesta de error con estado HTTP 402. Que la cuenta cuente con saldo positivo es una premisa inicial explícita aquí, verificada de forma independiente a través del panel de control web (la respuesta del endpoint de la clave por sí misma no valida el estado del saldo global). La consulta al endpoint https://openrouter.ai/api/v1/key devuelve:
{
"data": {
"label": "worker-key",
"usage": 25.04,
"limit": 25.0,
"is_free_tier": false,
"limit_remaining": 0.0,
"limit_reset": null
}
}
Aunque por definición el saldo de la cuenta principal es positivo, el campo limit_remaining ha llegado a cero. La clave alcanzó el tope de gasto de 25 dólares fijado por el administrador. Cualquier intento posterior de reintento utilizando esta misma clave fallará con el mismo código 402. El proceso debe finalizar inmediatamente su ejecución y emitir una alerta al operador para que ajuste el límite de gasto de la clave.
Escenario 2 (hipotético): Sobrecarga del proveedor ascendente (upstream)
Como ejemplo ilustrativo, consideremos el caso en el que una solicitud devuelve HTTP 429 debido a un fallo en el lado del proveedor upstream. El simple hecho de recibir un código 429 no ofrece fundamentos para concluir si la pasarela funciona correctamente o si existe saldo suficiente en la cuenta. El cuerpo de la respuesta de error puede incluir un bloque de metadatos:
{
"error": {
"message": "Provider returned rate limit error",
"code": 429,
"metadata": {
"provider_code": 429
}
}
}
El campo error.metadata.provider_code es opcional y contiene el código de error original del proveedor upstream cuando está disponible (en este ejemplo, 429), y no el nombre o identificador del proveedor. La presencia de este código no permite determinar cuál fue el proveedor específico que rechazó la solicitud.
Para atribuir con precisión el proveedor que falló, es necesario ingresar en la consola de administración en: Activity > petición específica > View Raw Metadata. Dentro del objeto provider_responses se muestra la lista de hosts consultados junto con sus estados reales devueltos, tal como describe la guía de enrutamiento. Cambiar a otro proveedor o sustituir el modelo en esta situación puede ayudar, pero no garantiza una recuperación inmediata.
Script de reintento de cliente en Python 3
Para escenarios en los que el código 429 es de naturaleza transitoria, el intervalo de espera antes de realizar el siguiente intento se calcula mediante la cabecera Retry-After. El servidor envía esta cabecera como un número entero en segundos o como una fecha formateada según el estándar HTTP.
La implementación mostrada a continuación utiliza únicamente la biblioteca estándar de Python 3. Gestiona exclusivamente el código 429, aplica un retroceso exponencial con una variación aleatoria (jitter) cuando falta la indicación del servidor y detiene la ejecución si el servidor requiere un período de espera superior a 60 segundos.
import email.utils
import json
import os
import random
import sys
import time
import urllib.error
import urllib.request
API_KEY = os.environ.get("OPENROUTER_API_KEY")
MODEL_ID = os.environ.get("OPENROUTER_MODEL_ID", "openai/gpt-4o-mini")
MAX_ATTEMPTS = 3
MAX_ACCEPTABLE_WAIT = 60.0
def parse_retry_after(header_value: str | None) -> float | None:
if not header_value:
return None
raw = header_value.strip()
if raw.isdigit():
return max(0.0, float(raw))
try:
parsed_date = email.utils.parsedate_to_datetime(raw)
delay = parsed_date.timestamp() - time.time()
return max(0.0, delay)
except Exception:
return None
def execute_completion(prompt_text: str) -> str | None:
if not API_KEY:
sys.stderr.write("Переменная окружения OPENROUTER_API_KEY не задана.\n")
return None
endpoint = "https://openrouter.ai/api/v1/chat/completions"
payload = json.dumps(
{
"model": MODEL_ID,
"messages": [{"role": "user", "content": prompt_text}],
}
).encode("utf-8")
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
for attempt in range(1, MAX_ATTEMPTS + 1):
req = urllib.request.Request(endpoint, data=payload, headers=headers, method="POST")
try:
with urllib.request.urlopen(req, timeout=30) as response:
status_code = response.getcode()
body = response.read().decode("utf-8")
if status_code == 200:
data = json.loads(body)
return data["choices"][0]["message"]["content"]
except urllib.error.HTTPError as err:
if err.code == 429:
retry_header = err.headers.get("Retry-After")
server_delay = parse_retry_after(retry_header)
if server_delay is not None:
wait_seconds = server_delay
else:
base_delay = 2.0 ** attempt
wait_seconds = base_delay + random.uniform(0.1, 1.0)
if wait_seconds > MAX_ACCEPTABLE_WAIT:
sys.stderr.write(
f"Сервер запросил паузу {wait_seconds:.1f} с. "
"Ожидание превышает 60 секунд. Запрос отменен.\n"
)
return None
if attempt == MAX_ATTEMPTS:
sys.stderr.write("Исчерпан лимит из 3 попыток на статус 429.\n")
return None
sys.stderr.write(
f"Получен 429. Попытка {attempt} завершилась неудачей. "
f"Пауза {wait_seconds:.2f} с перед следующим запросом.\n"
)
time.sleep(wait_seconds)
continue
elif err.code == 402:
sys.stderr.write("Ошибка 402: проверьте баланс счета и лимит ключа.\n")
return None
else:
sys.stderr.write(f"HTTP-ошибка {err.code}: запрос отклонен без повтора.\n")
return None
except urllib.error.URLError as err:
sys.stderr.write(f"Сетевой сбой: {err.reason}. Повтор отменен.\n")
return None
return None
if __name__ == "__main__":
result = execute_completion("Назови три базовых принципа надежности сетевых API.")
if result:
print(result)
Dado que el fragmento de script anterior conserva sus cadenas de diagnóstico originales en ruso byte por byte, a continuación se detallan las decisiones de su árbol interno y sus equivalencias localizadas:
Переменная окружения OPENROUTER_API_KEY не задана.(«La variable de entorno OPENROUTER_API_KEY no está configurada»): indica que falta la variable de entornoOPENROUTER_API_KEY; la función se detiene de inmediato sin realizar llamadas a la red.Сервер запросил паузу ... Ожидание превышает 60 секунд. Запрос отменен.(«El servidor solicitó una pausa de … s. La espera supera los 60 segundos. Petición cancelada»): se activa cuando la cabeceraRetry-Afterrequiere esperar más tiempo queMAX_ACCEPTABLE_WAIT(60 segundos); la ejecución se cancela de inmediato en lugar de bloquear indefinidamente los procesos de trabajo.Исчерпан лимит из 3 попыток на статус 429.(«Se agotó el límite de 3 intentos para el estado 429»): indica que se han consumido los 3 intentos permitidos ante respuestas HTTP 429.Получен 429. Попытка X завершилась неудачей. Пауза Y с перед следующим запросом.(«Recibido 429. El intento X falló. Pausa de Y s antes de la siguiente petición»): registra un fallo transitorio por límite de frecuencia 429 en el intento X y suspende la ejecución durante el tiempo de espera calculado Y antes de reintentar.Ошибка 402: проверьте баланс счета и лимит ключа.(«Error 402: verifique el saldo de la cuenta y el límite de la clave»): registra un error financiero HTTP 402 que señala que el saldo de la cuenta está agotado o que se ha alcanzado el límite de gasto de la clave; no se ejecutan reintentos.HTTP-ошибка {err.code}: запрос отклонен без повтора.(«Error HTTP {err.code}: petición rechazada sin reintento»): registra cualquier otro código de estado HTTP recibido y finaliza la ejecución sin reintentar.Сетевой сбой: {err.reason}. Повтор отменен.(«Fallo de red: {err.reason}. Reintento cancelado»): captura fallos de conexión o de socket a bajo nivel (URLError) e interrumpe la ejecución para evitar reenviar peticiones cuando se desconoce si la solicitud anterior fue recibida y procesada.- El prompt de prueba
Назови три базовых принципа надежности сетевых API.se traduce como: «Menciona tres principios básicos de fiabilidad para las API de red».
Reintento de peticiones de red y efectos secundarios
Al diseñar flujos de trabajo que involucran llamadas a funciones externas (tool calling) dentro de bucles de agentes autónomos, el reintento de solicitudes HTTP exige un cuidado extremo. Si en el paso anterior el modelo ya activó una herramienta externa que modificó el estado de un sistema externo (como escribir en una base de datos, emitir un pago o crear un ticket de soporte), repetir ciegamente toda la secuencia provocará operaciones duplicadas. Bajo ninguna circunstancia deben repetirse llamadas si las herramientas de negocio vinculadas ya se ejecutaron, o ante un resultado de red ambiguo (por ejemplo, una desconexión o un tiempo de espera de socket, donde no es posible determinar si la solicitud fue en realidad recibida y procesada por el servidor).
En el script presentado, los reintentos se restringen exclusivamente a aquellas solicitudes rechazadas explícitamente por el modelo con el estado HTTP 429. Asimismo, la generación de texto no debe considerarse estrictamente idempotente ni gratuita: cada llamada repetida consume presupuesto y cuota de tokens, y la naturaleza estocástica del muestreo de los modelos puede generar respuestas diferentes. Si el fallo ocurre durante la ejecución de un comando externo por parte de un agente, el estado del sistema debe sincronizarse a través de un registro de acciones antes de reanudar el diálogo con el modelo.
Creación de arquitecturas de enrutamiento resilientes
En la página de tarifas, el plan Free impone un límite de plataforma de 50 peticiones al día en modelos gratuitos. Cabe destacar que la ausencia de límites de plataforma mencionada en la documentación se refiere a la transición a un modelo de pago y no simplemente a la compra de créditos; recargar saldo no elimina automáticamente el límite de uso en los modelos gratuitos. Las condiciones actuales y las cuotas aplicables a cada cuenta deben consultarse periódicamente en la tabla de límites en vivo. Asimismo, el uso de modelos de pago tampoco descarta por completo la sobrecarga en clústeres de servidores de proveedores específicos (aunque alternar de proveedor puede mitigar el impacto, no garantiza una recuperación inmediata).
Para garantizar la fiabilidad en entornos de producción, los equipos de ingeniería combinan las siguientes estrategias:
- Indicar modelos de respaldo dentro de la matriz del parámetro
modelsen las peticiones a OpenRouter, lo que permite a la pasarela redirigir de forma automática la llamada a un proveedor alternativo si la opción principal falla. - Limitar el número máximo de solicitudes concurrentes en el cliente mediante colas de tareas, limitadores de tasa o algoritmos de cubeta de tokens.
- Mantener una ruta de reserva independiente a través de otras API multimodelo con formatos de solicitud compatibles en componentes críticos de la infraestructura, lo que permite desviar el tráfico en caso de indisponibilidad prolongada de la pasarela principal.