跳到正文
土土金API

开始

错误码与重试

出错时先看 HTTP 状态码:429、500、502、503 可以退避重试,400、401、403、404、413 是请求或账户的问题,重试不会成功。余额不足返回 403,不是 OpenAI 的 429。

请求出错时会返回 HTTP 错误状态码和一个 JSON 错误体。判断要不要重试,看状态码就够了:429、500、502、503 可以等一会儿再试;400、401、403、404、413 是请求本身或账户设置的问题,原样重试不会成功,只会浪费时间。

错误体格式

JSON
{
  "error": {
    "code": "",
    "message": "无效的令牌 (request id: 2026111202521796606900082...)",
    "type": "new_api_error"
  }
}
字段说明
message可读的错误描述,末尾带 request id
type错误类别
code错误码,例如 insufficient_user_quota、model_not_found;很多错误这里是空字符串

code 经常为空,程序里请以 HTTP 状态码为准,code 只作补充判断。Claude Messages 接口(/v1/messages)在转发阶段出错时,错误体是 Anthropic 的格式,即 {"type": "error", "error": {...}};认证、余额这类请求前的检查失败,所有接口都用上面的格式。

找客服排查时,把 message 里括号中的 request id 一起发过来(客服微信 JsnonoChat),我们能据此定位到这一次请求。建议在日志里完整记录错误响应。

状态码对照

状态码含义常见原因重试
400请求有误JSON 格式不对、缺少必填参数、参数取值超出范围、模型不支持某个参数、输入超过模型的上下文长度不重试,先修正请求
401认证失败Key 填错、已删除、已禁用、已过期,或这把 Key 的额度上限用完不重试
403没有权限账户余额不足(insufficient_user_quota)、IP 不在 Key 的白名单(access_denied)、模型不在 Key 的模型白名单、Key 的分组不可用、账户被禁用不重试
404不存在路径写错(比如漏了 /v1)、请求的资源不存在不重试
413请求体太大一次传入的图片、音频或文本过大不重试,压缩或拆分后再发
429请求过多触发了本站或模型服务的限流退避后重试
500服务内部错误服务端处理请求时出现异常退避后少量重试
502网关错误模型服务返回了异常响应,或连接中断退避后重试
503暂时不可用模型服务繁忙;或者 Key 所在分组没有这个模型的可用渠道(model_not_found)退避后重试;带 model_not_found 时换分组

余额不足在这里返回 403,错误码 insufficient_user_quota。OpenAI 官方接口遇到额度不足返回的是 429。如果你的代码沿用 OpenAI 的判断逻辑,请把 403 单独处理:提醒充值或切换账户,不要放进重试队列。

503 并提示 model_not_found

这表示当前 Key 所在的分组没有这个模型的可用线路,常见于模型名拼错或者分组里本来就没有这个模型。处理顺序:

  1. 核对模型名,要和模型广场里的写法完全一致。
  2. 用同一把 Key 调用模型列表接口,看返回里有没有这个模型。
  3. 没有的话,在模型详情页查这个模型有哪些分组,换一把对应分组的 Key,见选择分组。

重试策略

可以重试的错误(429、500、502、503)用指数退避加随机抖动:第 1 次等 1 秒左右,之后每次翻倍,设一个上限,最多重试 3 到 5 次。抖动能避免大量客户端在同一时刻一起重试。

OpenAI 官方 SDK 自带重试,遇到 429、5xx 和连接错误会自动退避重试,重试次数和超时都可以设置:

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://www.tutujin.com/v1",
    api_key=os.environ["TUTUJIN_API_KEY"],
    max_retries=4,   # 默认 2 次
    timeout=60.0,    # 单次请求超时,秒
)

# 单个请求临时调整
resp = client.with_options(max_retries=6, timeout=120.0).chat.completions.create(
    model="gpt-5.4-mini",
    messages=[{"role": "user", "content": "你好"}],
)

不用 SDK、直接发 HTTP 请求时,可以这样写:

import os
import random
import time

import requests

RETRYABLE = {429, 500, 502, 503}


def post_with_retry(path, payload, max_attempts=5):
    url = "https://www.tutujin.com/v1" + path
    headers = {"Authorization": f"Bearer {os.environ['TUTUJIN_API_KEY']}"}
    for attempt in range(max_attempts):
        r = requests.post(url, json=payload, headers=headers, timeout=60)
        if r.status_code not in RETRYABLE or attempt == max_attempts - 1:
            r.raise_for_status()  # 400/401/403/404/413 直接抛出
            return r.json()
        retry_after = r.headers.get("Retry-After", "")
        delay = float(retry_after) if retry_after.isdigit() else min(30, 2**attempt)
        time.sleep(delay + random.uniform(0, 1))

流式请求出错

流式请求在开始返回数据之前出错,和普通请求一样返回错误状态码。如果数据已经开始返回,HTTP 状态码已经是 200,出错表现为流提前结束、没有收到最后的 data: [DONE]。客户端要把「没收到 [DONE] 就断开」当作失败处理,已经输出的半截内容按需丢弃或提示用户重试。

排查清单

  • 401:打印请求头确认 Key 完整、没有多余空格或换行,在控制台确认 Key 未过期、未禁用、额度上限没用完。
  • 403:看 message 判断是余额、IP 白名单、模型白名单还是分组问题,对应去控制台调整。
  • 404:确认地址是 https://www.tutujin.com/v1 开头的完整路径。
  • 429:降低并发,或者把请求分散到多把 Key,见限流说明。
  • 一直 5xx:记下 request id,联系客服。

没找到答案?加客服微信 JsnonoChat,发送报错信息里的 request id,我们帮你排查。