HTTP 错误排查
HTTP 错误告诉你请求失败了,但通常并不告诉你原因。 从 403 错误得出“密钥失效”的结论,那只是穿着数字外衣的猜测。
源于一起真实事件(2026-07-30):一个第三方联系人增强 API 连续六天对每次调用返回 403 error code: 1010。
这被解读为“密钥无权访问”、“积分耗尽”、“月度配额已达”。
基于这个错误信念,构建了一个 756 行的浏览器自动化变通方案,并且差点要求客户检查一个其实正常的套餐。
实际原因是缺少 User-Agent,导致 CDN 在边缘层封禁了客户端。
一个标头就修复了:200 OK,完整数据,而积分余额一直都在。
在诊断之前,先执行这三个检查
1. 无效凭证控制测试。 分别使用一个故意伪造的凭证和完全不带凭证发送相同的请求。
for label, key in [("real", REAL_KEY), ("garbage", "0"*36), ("empty", "")]:
... # 相同的 URL、相同的方法、相同的标头
如果三个请求返回相同的状态码和响应体,那么错误与你的凭证无关。 它不携带任何关于权限、配额或有效性的信息。停止推理密钥问题。
2. 无意义路径控制测试。 请求一个肯定不存在的路径以及站点根路径。
GET https://api.example.com/definitely-not-a-real-path-12345
GET https://api.example.com/
GET https://www.example.com/ # 他们的营销站点
如果 API 错误也出现在营销站点上,那么你被在原始服务器前面拦截了。分析 API 调用本身不会有帮助。
3. 读取响应标头,而不仅仅是响应体。
except urllib.error.HTTPError as e:
print(e.code, e.headers.get("server"), e.read()[:200])
在“认证”错误中出现 server: cloudflare 就是显著的线索。同样还有 cf-ray、x-amzn-waf、akamai。这些是基础设施层面的信息,不是 API 层面的。
已知特征模式
| 症状 | 实际原因 | 修复方法 |
|---|---|---|
403 + 响应体 error code: 1010、server: cloudflare | Cloudflare 封禁了客户端签名 | 发送真实的浏览器 User-Agent |
所有路径(包括 /)返回相同错误 | 边缘层/WAF 拦截或 IP 封禁 | 更换 UA,然后更换 IP/代理 |
402 | 确实积分不足:请求已通过认证 | 充值;密钥没问题 |
429 紧随一次成功调用之后 | 真正的速率限制 | 控制请求节奏,添加退避策略 |
400 并包含字段错误消息 | 请求到达了 API 且认证通过 | 修正载荷;这是个好消息 |
对于排查而言,400 验证错误是一个成功信号:它证明认证已被接受。
任何服务端 API 调用的默认标头
Python 的 urllib 会发送 User-Agent: Python-urllib/3.x,许多 WAF 会直接封禁它。始终设置:
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0 Safari/537.36",
"Accept": "application/json",
"Content-Type": "application/json",
}
然后在进行任何全面操作之前先测量成本
一旦它正常工作,不要估算花费。在单次调用前后分别读取使用量,并读取提供商的账单字段(位于响应体中)。
before = usage(); result = call(); after = usage()
print("charged:", result.get("billing"), "| measured:", before - after)
如何报告
陈述你测试了哪些内容以及哪些仍未知。“两个密钥都返回 403”并非有效发现,如果空密钥也返回相同的 403。 改为这样说:“此处的 403 无信息价值,空凭证也返回它;原因在认证之上游。”