API Key 401 Unauthorized 错误排查指南
简答:API Key 遇到 401 Unauthorized,优先检查三件事:请求是否真的带上了认证信息、Key 是否完整有效、Base URL 和认证方式是否与服务商文档匹配。最快的定位方法是先用带 <code>-i</code> 的最小 curl 请求验证,再对比 SDK 或工具实际发送的请求;如果 curl 成功而程序失败,问题通常在环境变量、配置覆盖、缓存或代理转发链路。
定义:401 Unauthorized 表示服务器没有收到可用于认证的有效凭据;它通常不是模型参数错误,而是 API Key 缺失、无效、格式错误、未生效或被中间层丢弃。
先试这一步:用最小请求判断 Key 还是客户端配置
本站为第三方指南,非 UnitySVC 官方,以其官网为准。下面的命令只用于定位通用的 API 鉴权问题;请把 https://api.example.com/v1 替换为目标服务商文档规定的 Base URL,并确认该服务支持列出模型的端点。
| 环境 | 命令 | 如何判断 |
|---|---|---|
| Bash、macOS、Linux | curl -i "$BASE_URL/models" -H "Authorization: Bearer $API_KEY" | 重点看 HTTP 状态、响应正文、WWW-Authenticate、request ID 等响应头 |
| PowerShell | curl.exe -i "$env:BASE_URL/models" -H "Authorization: Bearer $env:API_KEY" | 使用 curl.exe,避免 PowerShell 对 curl 命令的别名行为造成差异 |
| 服务商要求专用请求头 | curl -i "$BASE_URL/models" -H "X-API-Key: $API_KEY" | 不要把 Bearer 写法套用到明确要求其他 Header 的服务 |
OpenAI API 的官方示例使用 Authorization: Bearer OPENAI_API_KEY,并以 /v1/models 作为可验证的请求路径;兼容接口可能拥有不同的路径、Header 或模型端点,应以对应文档为准。可参考 API 鉴权文档。
如果最小请求本身返回 401,先不要改 SDK 参数,继续检查 Key、Header 和 Base URL。如果最小请求成功,但 Postman、脚本或客户端仍返回 401,则问题位于本地配置生效链路或代理层。
按证据链排查 401:请求、凭据、地址、转发四层
- 先读响应正文和响应头。记录错误类型、错误消息、
WWW-Authenticate、request-id或服务商提供的请求编号。HTTP 规范语义中的 401 是未完成有效认证,服务器通常会通过WWW-Authenticate提示可接受的认证方案,可参考 MDN 对 401 的说明。 - 确认使用的是 API Key。网页登录密码、Cookie、浏览器会话、项目名称或控制台 URL 都不能自动替代 API Key。不要把完整 Key 粘贴到日志、截图、工单或聊天记录中。
- 检查 Key 是否被破坏。重点看复制时是否带了前后空格、换行、额外引号或不可见字符;同时确认 Key 未过期、未撤销,并且属于当前 API 项目或组织。具体有效期和撤销规则必须以服务商控制台和文档为准。
- 核对认证格式。常见格式是
Authorization: Bearer <API_KEY>,但部分网关使用X-API-Key或其他专用 Header。不要同时发送多个互相冲突的认证 Header。 - 核对 Base URL 和路径。常见错误包括把控制台地址当 API 地址、重复拼接
/v1、漏掉必要的版本前缀、把 Anthropic 风格路径发给 OpenAI 兼容端点,或使用了不支持该资源的域名。Base URL 不匹配时,错误可能表现为 401、404 或网关自定义错误,不能只看状态码。 - 排查代理和网关。如果本机直接请求成功,但经过反向代理、API Router、公司网关或自建服务后变成 401,检查代理是否允许并转发
Authorization,是否发生了重定向,以及是否把 Header 改写为空。用代理前后的-i响应头、网关访问日志和 request ID 对照确认。
Postman 官方建议检查 URL、Authorization 类型、凭据,并通过 Postman Console 查看实际发送的请求头、变量值和重定向;可参考 Postman 的 401 排查说明。
Key 看似存在但仍然 401:检查环境变量和配置覆盖
程序中写了 API_KEY 不代表运行时真的读到了它。常见情况是:在一个终端设置变量,却在另一个终端启动程序;PowerShell 变量只对当前会话生效;IDE、Docker、CI 或服务管理器覆盖了本地变量;项目根目录的 .env 没有被加载;或者旧进程仍在使用缓存配置。
Bash 中只检查变量是否存在,不打印完整 Key:
test -n "$API_KEY" && echo "API_KEY is set" || echo "API_KEY is missing"
printf 'API_KEY length: %s\n' "${#API_KEY}"
printf 'BASE_URL=%s\n' "$BASE_URL"
PowerShell 中:
if ([string]::IsNullOrEmpty($env:API_KEY)) { "API_KEY is missing" } else { "API_KEY is set" }
if ($env:API_KEY) { "API_KEY length: $($env:API_KEY.Length)" }
$env:BASE_URL
不要把 Key 写入源代码或提交到 Git。OpenAI 官方文档建议把 Key 放在环境变量或密钥管理服务中,官方 Python SDK 也展示了通过 OPENAI_API_KEY 读取凭据的方式,可参考 官方鉴权文档 和 官方 Python SDK 文档。使用 .env 时,应将其加入忽略规则,并确认负责加载它的库或启动脚本确实执行。
如果你刚修改了变量,重新启动当前终端、IDE、开发服务器、容器或后台进程,再做一次最小请求。不要通过打印完整变量值来验证,以免密钥进入终端历史或日志。
curl 成功但 JavaScript、Python 或 SDK 失败怎么办
此时不要立即更换 Key,先把成功的 curl 与失败程序做逐项对照:请求 URL、HTTP 方法、Authorization Header、Content-Type、重定向、代理和实际使用的模型或资源路径。只要其中一项不同,就不能说明两次请求等价。
JavaScript:显式确认运行时配置
const key = process.env.API_KEY;
if (!key) throw new Error('API_KEY is missing');
const response = await fetch(`${process.env.BASE_URL}/models`, {
headers: { Authorization: `Bearer ${key}` }
});
console.log(response.status, await response.text());
不要把服务端 Key 放到浏览器前端。官方 JavaScript SDK 默认从 OPENAI_API_KEY 读取 Key,也支持通过 OPENAI_BASE_URL 配置兼容端点;相关配置见 官方 JavaScript SDK 鉴权文档。
Python:区分变量缺失与服务端拒绝
import os
import requests
key = os.environ.get("API_KEY")
base_url = os.environ.get("BASE_URL")
if not key:
raise RuntimeError("API_KEY is missing")
r = requests.get(f"{base_url}/models", headers={"Authorization": f"Bearer {key}"}, timeout=30)
print(r.status_code, r.text)
如果直接请求成功、SDK 失败,检查 SDK 的 api_key、base_url 或对应环境变量是否被显式参数覆盖。官方 Python SDK 文档说明客户端可以从环境变量读取 Key,也可以传入自定义的 Base URL,详见 官方 Python SDK README。
401、403、404、429、402 和 400 怎么区分
| 状态或错误 | 通常先检查什么 | 不要误判成什么 |
|---|---|---|
| 401 Unauthorized | Key 是否缺失、无效、格式错误;Authorization 是否被发送或被代理剥离 | 不要先把问题归因于模型参数 |
| 403 Forbidden | 凭据已被识别后,检查项目权限、资源权限、来源限制或策略 | 与 401 的“未认证”不同 |
| 404 Not Found | Base URL、版本前缀、资源路径和模型名称 | 可能是端点不存在,不一定是 Key 错误 |
| 429 Too Many Requests | 限速、并发、重试策略和配额 | 不要反复更换 Key 作为第一反应 |
| 402 Payment Required 或 billing 错误 | 账户余额、计费状态、项目额度及服务商规则 | 402 的具体含义并非所有 API 都统一 |
| 400 Bad Request | JSON、HTTP 方法、必填字段、请求头和参数格式 | 不要把参数错误当作鉴权失败 |
| unsupported_parameter | 检查参数名是否被当前模型或端点支持,删除不兼容字段 | 通常不是 API Key 失效 |
HTTP 状态码的通用定义可参考 MDN HTTP 状态码参考;具体服务仍可能返回自定义错误类型,因此最终应以该服务的错误文档和响应正文为准。
一份不泄露密钥的 401 自查清单
- 记录状态码、错误类型、响应正文、
WWW-Authenticate和 request ID。 - 确认请求使用 API Key,而不是网页登录密码、Cookie 或项目名称。
- 确认 Key 没有前后空格、换行、额外引号,也没有被撤销或过期。
- 确认使用了正确的认证 Header:通常是
Authorization: Bearer ...,但要以服务商要求为准。 - 用
curl -i或curl.exe -i做一次最小请求。 - 检查
BASE_URL是否重复或遗漏/v1,以及资源路径是否属于该协议。 - 检查当前终端、IDE、容器、CI 和后台进程实际读取的变量;PowerShell 的
$env:只对当前会话生效。 - 用 Postman Console、SDK 调试日志或网关日志核对实际发送的 Header,但遮盖完整 Key。
- 如果直连成功、代理失败,检查重定向和代理是否剥离或改写
Authorization。 - 若确认 Key 可能泄露,立即按服务商流程撤销并重新生成,不要继续在公开日志中使用旧 Key。
常见问题
API Key 401 Unauthorized 最常见的原因是什么?
最常见方向包括 Key 没有发送、Authorization 格式错误、Key 复制时带了空格或换行、Key 已撤销或过期,以及 Base URL 与 Key 所属服务不匹配。先用 <code>curl -i</code> 发最小请求,能快速区分服务端凭据问题与 SDK 配置问题。
Authorization 应该写成什么格式?
许多 OpenAI 兼容接口使用 <code>Authorization: Bearer <API_KEY></code>,但不是所有服务都相同。若服务商文档要求 <code>X-API-Key</code> 或其他 Header,应按其规定发送,不要同时添加互相冲突的认证字段。
环境变量明明设置了,为什么程序仍然返回 401?
可能是在不同终端设置了变量,PowerShell 会话已经结束,IDE 或容器没有继承变量,<code>.env</code> 没有被加载,或者 SDK 的显式参数覆盖了环境变量。只检查变量是否存在和长度,不要打印完整 Key;修改后重启实际运行程序的进程。
curl 成功但 Postman 或 SDK 失败,应该怎么办?
把两次请求的 URL、路径、认证 Header、重定向、代理和请求体逐项对比。Postman 可打开 Console 查看实际发送的请求头、变量值和重定向;SDK 则重点检查 <code>api_key</code>、<code>base_url</code> 和对应环境变量是否生效。
401 和 403 有什么区别?
401 通常表示服务器没有收到有效认证凭据;403 通常表示凭据已被识别,但当前身份没有访问该资源或执行该操作的权限。实际服务可能使用自定义错误,因此还要结合响应正文和服务商文档判断。
代理或网关会导致 API Key 变成 401 吗?
会。若直连 API 成功、经过反向代理或网关后变成 401,应检查代理是否转发 <code>Authorization</code>、是否发生重定向、是否改写了 Header,以及后端日志中是否实际收到请求。
相关阅读
参考资料
- OpenAI API 鉴权与请求调试文档
- MDN:401 Unauthorized
- MDN:HTTP 状态码参考
- Postman:Fixing a 401 Unauthorized response error(support.postman.com)
- OpenAI 官方 Python SDK README
- OpenAI 官方 JavaScript SDK 鉴权文档
- github.com