O objetivo das APIs compatíveis com OpenAI é que o SDK simplesmente funcione — portanto, quando ocorre um 401, o problema quase sempre está nas duas linhas que você alterou: base_url e api_key. Veja abaixo os modos de falha na ordem em que realmente ocorrem.
O erro
{
"error": {
"type": "invalid_api_key",
"message": "Invalid or missing API key.",
"code": "invalid_api_key"
}
}Causas e soluções em resumo
| Causa | Solução |
|---|---|
| base_url sem o sufixo /v1 (ou com duplicação) | A maioria dos gateways exige exatamente https://host/v1 — o SDK acrescenta /chat/completions por conta própria. |
| Chave de outro host | Chaves sk-… só autenticam no serviço que as emitiu; verifique a correspondência entre prefixo e host. |
| Proxy corporativo / WAF removendo o header Authorization | Teste em uma rede limpa; configure o proxy para encaminhar Authorization. |
| A variável de ambiente OPENAI_API_KEY substitui sua chave explícita | O SDK lê variáveis de ambiente por padrão — em algumas configurações, uma variável antiga vence silenciosamente; passe api_key explicitamente. |
Verifique a URL exata acessada pelo SDK
Imprima client.base_url e chame GET /v1/models — o endpoint autenticado mais barato. Se /models funcionar, a autenticação está correta e o erro está em outro lugar:
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])Use Curl no mesmo host para descartar o SDK
Se o curl com Authorization: Bearer funcionar e o SDK não, compare a requisição real do SDK (defina OPENAI_LOG=debug) — nove vezes em dez, um proxy ou uma variável de ambiente reescreveu algo.
Se você estiver chamando pela Kunavo
O endpoint da Kunavo segue rigorosamente o formato da OpenAI em https://api.kunavo.com/v1, com autenticação Bearer, e GET /v1/models funciona como teste rápido de autenticação. Se seu código funciona com api.openai.com, apontar base_url para a Kunavo é a única alteração — mesmo SDK, mesmo formato de transmissão, uma chave para modelos Claude, GPT e de mídia.
Perguntas frequentes
401 versus 403 em um gateway — qual é a diferença?
401 = a própria credencial não foi aceita (chave ausente ou inválida). 403 = a chave é válida, mas não tem permissão para fazer isso (chave desativada, conta suspensa ou modelo não permitido). Leia o corpo do erro — APIs compatíveis colocam o motivo em error.message.
Por que meu código funciona localmente, mas retorna 401 na CI?
A CI é um ambiente diferente: o segredo não está definido, está definido para outro serviço ou um proxy remove o header. Registre repr(key[:12]) e base_url dentro da CI para ver o que realmente está sendo enviado.
Guias relacionados
Mais detalhes sobre o significado dos erros estão em referência de erros; obter uma chave leva um minuto por meio de cadastro e da guia de autenticação.