Códigos de error de la API

Todos los fallos que devuelve esta pasarela, medidos uno a uno contra el endpoint real el 2026-09-01. De los 8 fallos que devuelve, 5 no significan lo que sugiere su código de estado, y 2 de ellos los reintentan los SDK oficiales antes de que tu programa llegue a verlos.

Verificado el 2026-09-01

El código de estado engaña

Lee error.code, no el código de estado

Un nombre de modelo mal escrito vuelve como 503. Un campo messages ausente vuelve como 500. Ambos son errores permanentes de tu petición —ningún reintento los arregla—, pero openai-python, openai-node y los SDK de Anthropic reintentan por defecto dos veces los 408, 409, 429 y todos los 5xx, con retroceso exponencial. Así, una errata se convierte en tres viajes de ida y vuelta y decenas de segundos de espera antes de que aparezca el motivo real. Ese motivo estuvo siempre en error.code, dentro del cuerpo: la parte que miente es el código de estado.

Referencia

Códigos de estado

Ordenados por la frecuencia con la que se encuentran de verdad, no por número.

  • HTTP 401cadena vacía
    Qué ha pasado en realidad
    La clave no existe, se revocó, o la cabecera Authorization falta por completo.
    Qué hacer
    Comprueba en la página «Claves de API» de la consola que la clave sigue ahí, o crea una nueva. Algo que puedes descartar: no es el prefijo "Bearer " ausente —la pasarela acepta también la clave a secas, así que nunca es la causa.
    Los SDK lo reintentan
    No, falla de inmediato
    Medido contra el endpoint real
    probes-errors.mjs: invalid-token / missing-auth
    Mensaje devuelto
    Invalid token
  • HTTP 503model_not_foundEl código de estado engaña
    Qué ha pasado en realidad
    Ese ID de modelo no está en este catálogo; casi siempre viene copiado de la documentación de otro proveedor (gpt-4o, claude-sonnet-4-5). No hay ninguna caída: simplemente no hay ruta para un modelo que aquí no existe.
    Qué hacer
    Compáralo con GET /v1/models, que es la lista autorizada. Aquí todos los IDs tienen la forma proveedor/modelo, por ejemplo deepseek/deepseek-v4-flash.
    Los SDK lo reintentan
    Sí, dos veces por defecto
    Medido contra el endpoint real
    probes-errors.mjs: unknown-model
    Mensaje devuelto
    No available channel for model gpt-4o under group y-api (distributor)
  • HTTP 403insufficient_user_quotaEl código de estado engaña
    Qué ha pasado en realidad
    El saldo de la cuenta llegó a cero. El mensaje está en chino y muestra el saldo restante con un $ de ancho completo, y por eso buscarlo en inglés no devuelve nada.
    Qué hacer
    Recarga en https://y-api.bestvirtualgoods.com/app/billing. El crédito se restablece de inmediato, las claves existentes siguen funcionando y no hay que cambiar código.
    Los SDK lo reintentan
    No, falla de inmediato
    Confirmado en el código del upstream, no reproducido aquí
    service/billing_session.go:355-359
    Mensaje devuelto
    用户额度不足, 剩余额度: $0.00
  • HTTP 403pre_consume_token_quota_failedEl código de estado engaña
    Qué ha pasado en realidad
    La cuenta todavía tiene saldo, pero esta clave concreta alcanzó el límite que se le fijó al crearla. El mensaje indica tanto lo que le queda a la clave como lo que necesitaba la petición.
    Qué hacer
    Sube o quita el límite de esa clave en la página «Claves de API», o usa una clave sin límite. Recargar la cuenta no sirve: el límite es por clave.
    Los SDK lo reintentan
    No, falla de inmediato
    Medido contra el endpoint real
    probes-errors.mjs: quota-exhausted
    Mensaje devuelto
    token quota is not enough, token remain quota: $0.000002, need quota: $0.000074
  • HTTP 500invalid_requestEl código de estado engaña
    Qué ha pasado en realidad
    Falta un campo obligatorio en el cuerpo de la petición, casi siempre messages. Es un error de petición mal formada notificado como error de servidor.
    Qué hacer
    El mensaje nombra el campo textualmente, así que lee eso y no el código de estado. Corrige el cuerpo; reintentar va a fallar igual.
    Los SDK lo reintentan
    Sí, dos veces por defecto
    Medido contra el endpoint real
    probes-errors.mjs: missing-field
    Mensaje devuelto
    field messages is required
  • HTTP 400invalid_request_errorEl código de estado engaña
    Qué ha pasado en realidad
    Ese endpoint no existe aquí: /v1/embeddings, /v1/completions y /v1/images/generations caen todos en este caso. Y el cuerpo afirma que messages es obligatorio, lo que apunta a algo que no tiene nada que ver con el problema real.
    Qué hacer
    Esta pasarela sirve chat completions y el endpoint messages de Anthropic. Si algún framework llama por dentro a un endpoint de embeddings, esa parte necesita otro proveedor.
    Los SDK lo reintentan
    No, falla de inmediato
    Medido contra el endpoint real
    probes-errors.mjs: unsupported-endpoint
    Mensaje devuelto
    `messages` is required and must be a non-empty array.
  • HTTP 400cadena vacía
    Qué ha pasado en realidad
    El cuerpo de la petición no es JSON válido. El upstream duplica su propio prefijo "Invalid request:", que es solo cosmético.
    Qué hacer
    Suele ser un cuerpo escrito a mano o un problema de comillas en el shell. Valida el JSON antes de enviarlo y, en el shell, encierra todo el cuerpo entre comillas simples.
    Los SDK lo reintentan
    No, falla de inmediato
    Medido contra el endpoint real
    probes-errors.mjs: malformed-json
    Mensaje devuelto
    Invalid request: Invalid request: invalid JSON request body
  • HTTP 404cadena vacía
    Qué ha pasado en realidad
    POST /v1/messages/count_tokens no está implementado. Claude Code y el SDK de Anthropic lo llaman para estimar el contexto antes de enviar la petición.
    Qué hacer
    No hay nada que arreglar por tu parte. Los clientes se saltan la estimación o muestran un aviso; el chat, el streaming y las herramientas no se ven afectados.
    Los SDK lo reintentan
    No, falla de inmediato
    Medido contra el endpoint real
    probes-errors.mjs: count-tokens
    Mensaje devuelto
    Invalid URL (POST /v1/messages/count_tokens)

Fallos silenciosos

Peor que un error: HTTP 200

Hay tres fallos que devuelven 200, no lanzan ninguna excepción y dejan que tu programa siga adelante. Lo que está mal es el contenido o la factura, y justamente por eso cuesta tanto descubrirlos por tu cuenta.

Las imágenes se aceptan y luego se ignoran

El upstream marca cinco modelos con image_ratio. Esa marca es un coeficiente de facturación, no una capacidad de visión. Los cinco aceptan una imagen en data URL, devuelven 200 y responden con aplomo sobre una imagen que nunca vieron: la sonda envió un PNG verde puro y ninguno acertó el color. El modelo recomendado, en cambio, las rechaza con un 400, que de los dos fallos es el menos peligroso.

De la matriz de compatibilidad de clientes

max_completion_tokens limita el texto, no la factura

Con el límite en 12, moonshotai/kimi-k2.5 facturó 261 tokens: 21,8 veces más. El recorte sí funciona (finish_reason=length); el exceso son tokens de razonamiento, que se facturan pero nunca aparecen en content. Cualquier estimación de coste basada en ese límite se queda corta en ese mismo factor.

De la matriz de compatibilidad de clientes

Los id de mensaje de Anthropic no llevan el prefijo msg_

POST /v1/messages devuelve un id hexadecimal sin prefijo. El código que da por hecho el prefijo msg_, o que analiza el id para encaminar la respuesta, no va a coincidir, aunque el resto del cuerpo sí cumpla la especificación.

De la matriz de compatibilidad de clientes

Análisis

Qué forma tiene un cuerpo de error

La envoltura siempre es {"error": {...}}, pero el conjunto de campos no es estable: unos errores llevan param y otros lo omiten, y code es a menudo una cadena vacía. Analiza a la defensiva y trata todos los campos como opcionales.

401, sin campo param
{"error":{"code":"","message":"Invalid token (request id: 2026090114474514374…)","type":"new_api_error"}}
404, con param presente pero vacío
{"error":{"message":"Invalid URL (POST /v1/messages/count_tokens)","type":"invalid_request_error","param":"","code":""}}

Cada mensaje termina con un request id, y cambia en cada llamada. Quítalo antes de comparar mensajes con esta página y, al revés, inclúyelo cuando nos reportes un problema: es lo único que identifica tu petición exacta en los registros.

Diagnóstico

Tres pasos que aíslan casi cualquier cosa

En este orden. Cada paso descarta una capa, así que la respuesta está donde la secuencia se detiene.

  1. Comprueba primero la clave y la ruta

    GET /v1/models no cuesta nada y usa la misma clave. Un 200 con la lista de modelos demuestra que la clave, la red y el borde de la CDN están bien, así que el problema está en el cuerpo de la petición y no en tus credenciales.

    curl -s https://api.y-api.bestvirtualgoods.com/v1/models -H "Authorization: Bearer $YAPI_KEY" | head -c 200
  2. Reprodúcelo con curl

    Esto saca al SDK de la ecuación. Si curl funciona y tu código no, la diferencia está en cómo construye la petición esa biblioteca, no en la pasarela.

    curl -i https://api.y-api.bestvirtualgoods.com/v1/chat/completions \
      -H "Authorization: Bearer $YAPI_KEY" \
      -H "Content-Type: application/json" \
      -d '{"model":"deepseek/deepseek-v4-flash","messages":[{"role":"user","content":"hi"}]}'
  3. Desactiva los reintentos mientras depuras

    Sin reintentos, un 500 o un 503 provocado por tu propia petición aparece en el primer intento en lugar de después de dos inútiles. Vuelve a activarlos al terminar: contra fallos transitorios de verdad, los reintentos valen la pena.

    client = OpenAI(
        base_url="https://api.y-api.bestvirtualgoods.com/v1",
        api_key=os.environ["YAPI_KEY"],
        max_retries=0,  # solo para depurar
    )

¿No es ninguno de estos?

La página de estado indica si la pasarela está degradada, con los últimos 90 días de comprobaciones. Si está en verde y sigues atascado, el request id del cuerpo del error es lo único que merece la pena enviarnos.