API 调用失败,先看 HTTP 状态码和响应里的错误信息。401、402、429分别指向鉴权、余额和请求速率问题,处理方向完全不同。
下面按 DeepSeek 官方直连 API 说明排查;经过第三方网关时,还要确认错误来自网关还是上游,不能直接套用同一含义。
先用这张表定位
| 状态码 | 官方含义 | 先做什么 |
|---|---|---|
| 400 | 请求体格式错误 | 检查 JSON 结构及错误提示 |
| 401 | 鉴权失败 | 检查程序实际读取的 API Key |
| 402 | 余额不足 | 检查该 Key 所属账号的 API 余额 |
| 422 | 参数无效 | 按错误信息修正参数名、类型或取值 |
| 429 | 触发速率限制 | 降低请求频率和并发,稍后重试 |
| 500 / 503 | 服务错误或过载 | 短暂等待,进行有限重试 |
上图为 DeepSeek 官方 API 文档的错误码说明。
401 和 402,重复请求通常没有用
遇到401,重点看运行中的程序读到了哪份配置:环境变量是否被旧值覆盖,配置文件是否来自另一套环境,Key 是否粘贴完整。日志只记配置来源和状态码,不打印密钥。
遇到402,检查 Key 对应账号的 API 余额。网页端能正常聊天,不能证明当前 API 账号余额充足。修正前不停重试,只会重复得到同样的错误。
429:先控并发,再谈重试
DeepSeek 当前文档明确:并发按账号计算,换同一账号下的 API Key 不会得到一份新的并发额度。一个请求从发送到模型响应完成,都占用并发连接。
因此,长回答和大量同时发出的任务会持续占住连接。处理时先减小工作线程或任务池,再让失败请求延后重试;不要让每个线程立即重发,把队列越堆越长。
可以采用有上限的指数退避,例如下面这段重试策略示意。这里的 0.5 秒、8 秒和最多 3 次重试是示例选择,不是官方额度。
import random def retry_delay(status, retry_index): # retry_index 从 0 开始;返回 None 表示不自动重试 if status not in {429, 500, 503} or retry_index >= 3: return None return min(8.0, 0.5 * (2 ** retry_index)) + random.uniform(0, 0.3)如果 SDK 已经带自动重试,应先弄清它的配置,再决定是否增加外层重试。两层循环叠加,实际请求次数可能远超预期。客户端超时也不代表服务端没有执行;已经收到部分流式内容时,应单独处理,避免重发后得到重复结果和额外消耗。
连接还在,可能只是保活
DeepSeek 文档说明,等待期间非流式请求可能收到空行,流式请求可能收到: keep-alive注释。这些内容不等于模型答案,也不应直接当作无效 JSON。
自己写 SSE 解析器时,应跳过注释行,按完整事件读取数据。错误码决定处理方向,保活内容则决定解析方式,别把两类问题混在一起。