上周三帮朋友排一个诡异的 bug:他的 Grok 4.5 调用一切正常,代码原封不动把 model 换成 grok-4.7,直接 401。Key 没过期、余额充足、网络没问题。折腾了大半天,最后发现根源不在 Key 本身,而是 Authorization 请求头里 Bearer 前缀的格式问题------多一个空格、大小写不对都可能导致 401。如果你也遇到了"Key 没问题但 grok-4.7 就是 401"的情况,值得按下面的流程逐步排查。
下面把完整排查链路和最小复现写出来,5 分钟能定位。
为什么会出现这个问题
先看一下 xAI 官方对 Authorization 头的要求:
Authorization: Bearer <XAI_API_KEY>
Bearer 首字母大写,后面恰好一个空格,再接 Key。
RFC 6750 规范本身要求 Bearer 方案名大小写敏感,但不同服务端实现的宽严程度不一。根据实测(测试时间:2026 年 7 月,使用 requests 库直接构造请求头),grok-4.5 和 grok-4.7 在 Authorization 头格式处理上表现出差异------以下为实测结论,不代表官方行为规范,建议以你自己的环境复现为准:
Bearer xai-xxx(标准写法):两个版本均正常bearer xai-xxx(小写 b):grok-4.5 实测可通过,grok-4.7 实测返回 401Bearer xai-xxx(两个空格):grok-4.7 实测返回 401- Key 尾部带
\n或空格:grok-4.7 实测返回 401
目前没有找到 xAI 官方 changelog 或公告说明这一行为差异,上述结论仅基于实测,如有不同结果欢迎评论区补充。
不符合标准格式时,报错长这样:
AuthenticationError: 401 Unauthorized
{'error': {'message': 'Invalid API key provided.',
'type': 'invalid_request_error',
'code': 'invalid_api_key'}}
这个报错信息挺坑人的------它说的是"Invalid API key",但实际上 Key 完全没问题,是头格式不对。
排查流程
按这个顺序走,基本不会绕弯路。
第一步:确认 Key 存在且前缀正确
先别急着看代码逻辑,跑一行诊断:
python
import os
key = os.environ.get('XAI_API_KEY', 'NOT SET')
print(f'prefix: {key[:8]}')
正常输出应该是 prefix: xai-xxxx。如果是 NOT SET 或者前缀是 sk-,那问题不在格式校验------你要么没设环境变量,要么用了 OpenAI 的 Key。
去 xAI 控制台确认 Key 状态是 Active,这一步别跳过。
第二步:确认 base_url 没指错
社区反馈最高频的坑。用 OpenAI SDK 调 xAI 的时候,base_url 必须显式设成 https://api.x.ai/v1,不设的话请求默认打到 OpenAI 的服务器,拿 xAI 的 Key 去认证,当然 401。
报错信息还会带上这么一句,非常容易误判:
'message': 'Incorrect API key provided: xai-xxxx.
You can find your API key at https://console.x.ai'
看到 console.x.ai 你以为是 xAI 返回的?不一定。如果 base_url 指错了,这段话可能是 OpenAI 服务器返回的------它不认识 xai- 前缀的 Key,就给你甩了个 401。
第三步:最小复现------Bearer 格式问题
确认了 Key 和 base_url 都没问题之后,可以用 requests 手动构造请求来排查 Authorization 头格式。
可能导致 401 的写法(以下为实测结论,测试环境:Python requests,2026 年 7 月):
python
headers = {
'Authorization': f'bearer {os.environ["XAI_API_KEY"]}',
'Content-Type': 'application/json'
}
注意 bearer 是小写 b。实测 grok-4.5 可正常返回,grok-4.7 返回 401。如需自行复现,可将 model 分别设为 grok-4.5 和 grok-4.7 对比响应。
修复后的写法:
python
headers = {
'Authorization': f'Bearer {os.environ["XAI_API_KEY"]}',
'Content-Type': 'application/json'
}
就这一个字母的大小写区别。
另一个常见的隐蔽坑是多余空格 。有些人从 .env 文件读 Key 的时候,行尾会带一个 \n 或者空格,拼出来的 Authorization 头变成 Bearer xai-xxx\n,可能导致 401。
验证方法:
python
key = os.environ.get('XAI_API_KEY', '')
print(repr(key)) # 看有没有 \n 或尾部空格
如果 repr() 输出里有 \n 或者尾部空格,.strip() 一下就好了。
方案一:手动修正 Authorization 头
最直接的方案,适合用 requests / httpx 直接调用的场景:
python
import requests, os
key = os.environ['XAI_API_KEY'].strip()
headers = {'Authorization': f'Bearer {key}',
'Content-Type': 'application/json'}
python
body = {'model': 'grok-4.7',
'messages': [{'role': 'user', 'content': 'test'}]}
r = requests.post('https://api.x.ai/v1/chat/completions',
headers=headers, json=body)
print(r.status_code, r.json())
关键两点:Bearer 首字母大写,Key 做 .strip()。
方案二:用 OpenAI SDK(它帮你拼好了 Bearer)
如果你用 OpenAI Python SDK,SDK 内部会自动拼 Authorization: Bearer {api_key},格式是标准的。但前提是你别自己又在 default_headers 里覆盖了一遍。
python
from openai import OpenAI
import os
client = OpenAI(
api_key=os.environ.get('XAI_API_KEY'),
base_url='https://api.x.ai/v1')
python
resp = client.chat.completions.create(
model='grok-4.7',
messages=[{'role': 'user', 'content': 'hi'}])
print(resp.choices[0].message.content)
这样走 SDK 的标准路径,Authorization 头的格式不会出问题。如果你之前在代码里手动设置了 default_headers={'Authorization': ...},删掉它,让 SDK 自己处理。
方案三:通过聚合网关调用,绕过格式差异
我后来把 xAI 的调用也走了 API 聚合网关。像 OpenRouter 或者 ofox.io 这类平台,网关层会帮你标准化 Authorization 头的格式再转发给上游,不用操心各家 API 对 Bearer 校验松紧不一的问题。
python
client = OpenAI(
api_key='your-gateway-key',
base_url='https://api.ofox.io/v1')
python
resp = client.chat.completions.create(
model='grok-4.7',
messages=[{'role': 'user', 'content': 'hi'}])
注意 :
base_url及模型名x-ai/grok-4.7来自该平台文档,建议使用前自行查阅 ofox.io 当前文档确认接入方式和模型名格式。
据其官网声称,该平台为官方授权服务商,0% 加价对齐官方价格,xAI 的模型走官方通道。OpenRouter 也能用,具体手续费和加价规则请以 OpenRouter 官网定价页 为准,不同模型可能有所不同。
改个 base_url 就行,模型名用 x-ai/grok-4.7 这种带 provider 前缀的格式。
这个方案还有个好处------如果你团队里多个人调不同厂商的 API,不用每个人都去注册各家的 Key,聚合网关后台能看到每个人、每个模型的 Token 消耗和费用明细,月底对账不用挨个问。
常见问题 FAQ
Q: Grok 4.5 好好的,换成 Grok 4.7 就 401,代码一行没改,到底为什么?
A: 根据实测,grok-4.7 对 Authorization 头格式的容忍度低于 grok-4.5。如果你的代码里 Bearer 写成了小写 bearer,或者 Key 尾部带了换行符/空格,4.5 可能容忍但 4.7 不行。用 repr() 打印一下你实际发出去的 Authorization 头内容,逐字节检查。
Q: 我用的是 OpenAI SDK,应该不存在 Bearer 格式问题吧?
A: SDK 自身拼接是标准格式没错。但两个坑:一是 base_url 忘了设成 https://api.x.ai/v1,请求打到了 OpenAI 服务器;二是你在初始化 client 的时候通过 default_headers 手动覆盖了 Authorization 头,覆盖的那个格式可能有问题。检查一下有没有这两种情况。
Q: 报错信息说 "Invalid API key",怎么判断是 Key 真的无效还是格式问题?
A: 去 xAI 控制台看 Key 的状态。如果是 Active,那大概率不是 Key 本身的问题。再用同一个 Key 调一下 Grok 4.5(model 改成 grok-4.5),如果 4.5 能通 4.7 不行,基本就是格式校验的问题。
Q: 我用 curl 测试正常,但 Python 代码里 401,为什么?
A: curl 里 -H "Authorization: Bearer xai-xxx" 的格式是标准的。Python 代码里检查两个地方:一是 .env 文件读出来的 Key 有没有尾部 \n(用 repr() 看);二是字符串拼接时 f'Bearer {key}' 中间是不是恰好一个空格,有些编辑器会插入不可见字符。
Q: 除了 grok-4.7 还有哪些 xAI 模型可以用?
A: 按可用模型列表,xAI 当前可用的模型有:grok-4.7(最新)、grok-4.6、grok-4.5、grok-4.20、grok-4.3、grok-4.1-fast。具体哪些模型对 Bearer 格式校验做了收紧,我只验证了 grok-4.7,其他版本没逐一测------踩到了欢迎评论区补充。
小结
这个问题的根源是 grok-4.7 对 HTTP Authorization 头格式的严格格式校验,把以前可能容忍的非标准写法拒掉了(基于实测,无官方 changelog 说明)。修复方案很简单:确保 Bearer 大小写正确 + Key 做 .strip() + 别自己覆盖 SDK 的默认头。
如果你的项目里同时调多家 API,建议统一走 SDK 的标准路径或者聚合网关,别手动拼 Authorization 头,免得每家校验规则不一样还得逐个适配。