El objetivo de las API compatibles con OpenAI es que el SDK funcione sin más; por eso, cuando devuelve 401, el error casi siempre está en las dos líneas que cambiaste: base_url y api_key. Estos son los modos de fallo, en el orden en que realmente suelen ocurrir.
El error
{
"error": {
"type": "invalid_api_key",
"message": "Invalid or missing API key.",
"code": "invalid_api_key"
}
}Causas y soluciones de un vistazo
| Causa | Solución |
|---|---|
| base_url no incluye el sufijo /v1 (o lo duplica) | La mayoría de las gateways requieren exactamente https://host/v1; el SDK añade /chat/completions por sí mismo. |
| Clave de otro host | Las claves sk-… solo autentican contra el servicio que las emitió; comprueba la correspondencia entre prefijo y host. |
| El proxy corporativo o WAF elimina el header Authorization | Prueba desde una red limpia; configura el proxy para reenviar Authorization. |
| La variable de entorno OPENAI_API_KEY sustituye tu clave explícita | El SDK lee el entorno de forma predeterminada; en algunas configuraciones una variable antigua gana silenciosamente. Pasa api_key explícitamente. |
Verifica la URL exacta a la que llama el SDK
Imprime client.base_url y llama a GET /v1/models: es el endpoint autenticado más barato. Si /models funciona, la autenticación está bien y el error está en otra parte:
from openai import OpenAI
client = OpenAI(
base_url="https://api.kunavo.com/v1", # exactly one /v1
api_key="sk-kn-...", # explicit beats env vars
)
print(client.base_url)
print([m.id for m in client.models.list().data][:5])Usa Curl contra el mismo host para descartar el SDK
Si curl funciona con Authorization: Bearer y el SDK no, compara la solicitud real del SDK (establece OPENAI_LOG=debug); nueve de cada diez veces un proxy o una variable de entorno ha reescrito algo.
Si llamas a través de Kunavo
El endpoint de Kunavo tiene una forma estrictamente compatible con OpenAI en https://api.kunavo.com/v1 y utiliza autenticación Bearer; GET /v1/models sirve como prueba rápida de autenticación. Si tu código funciona contra api.openai.com, apuntar base_url a Kunavo es el único cambio: mismo SDK, mismo formato de cableado y una clave para Claude, GPT y modelos multimedia.
Preguntas frecuentes
401 frente a 403 en una gateway: ¿cuál es la diferencia?
401 = la credencial no fue aceptada (clave ausente o no válida). 403 = la clave es válida, pero no tiene permiso para hacerlo (clave deshabilitada, cuenta suspendida o modelo no permitido). Lee el cuerpo del error: las API compatibles con OpenAI ponen el motivo en error.message.
¿Por qué mi código funciona localmente, pero devuelve 401 en CI?
CI es un entorno distinto: el secreto no está configurado, está configurado para otro servicio o un proxy elimina el header. Registra repr(key[:12]) y base_url desde dentro de CI para ver qué se está enviando realmente.
Guías relacionadas
Encontrarás más detalles sobre el significado de los errores en referencia de errores; obtener una clave lleva un minuto mediante registro y la guía de autenticación.