> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bettertoken.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# BetterToken API 错误排查：状态码、限流与重试

> 结合 HTTP 状态码、响应正文和请求 ID 排查 BetterToken API 错误，处理认证失败、限流与超时，按请求完成状态决定是否重试，并配置有限次数的退避策略。

排查 BetterToken API 错误时，同时查看 HTTP 状态码和响应正文。记录 `error.message` 中的请求 ID，修复认证或配置错误后再发送；只对已确认的临时故障重试。

## 排查前准备

准备好 Model ID、Base URL 和失败响应。在 [BetterToken 控制台](https://bettertoken.ai/workspace)检查 Key 状态和当前余额。如果你还未成功完成模型请求，请先查看 [SDK 示例](/zh/guides/sdk-examples.md)或[工具配置指南](/zh/ai-tools/overview.md)。

## 读取错误响应

BetterToken 使用 JSON 对象返回 API 错误。例如，未提供 Key 的请求可能返回：

```json theme={null}
{
  "error": {
    "code": "",
    "message": "Token not provided (request id: …)",
    "type": "new_api_error"
  }
}
```

即使 `error.code` 为空，也要读取 `error.message`。消息中的请求 ID 可用于定位这次失败尝试。未携带 Key 请求 `/v1/models` 会返回 `401`，不能用来验证模型能否生成回答。

联系支持前，请记录以下信息：

| 信息 | 记录内容 |
| - | - |
| HTTP 状态 | 响应状态码；如果未收到 HTTP 响应，则记录客户端的超时或网络异常 |
| 响应正文 | 错误 JSON 或其他返回内容，先移除敏感信息 |
| 请求 ID | `error.message` 中的标识符（如有） |
| 时间 | 请求的日期、时间和时区 |
| Model ID | 请求中 `model` 的准确值 |
| Base URL | 配置的 Base URL 和最终请求路径 |

不要在截图、支持消息或共享日志中包含 BetterToken API Key。

## 检查 HTTP 状态码

下表按 HTTP/API 的通用含义和相关配置指南说明排查方向，并非 BetterToken 专有错误码清单。请根据实际响应选择处理方式。

| 状态码 | 常见含义 | 优先检查 | 是否重试 |
| - | - | - | - |
| `400` | 请求、字段或参数组合无效 | JSON 结构、必填字段、模型及接口要求 | 先修正请求 |
| `401` | 凭据缺失、无效、过期或已撤销 | Key 状态、多余空格及协议所需的认证请求头 | 先修正认证 |
| `403` | 请求被拒绝 | 响应消息、账户余额、Key 状态和客户端配置 | 先解决拒绝原因 |
| `404` | 路径或请求的资源不存在 | Base URL、重复或缺失的 `/v1`、最终路径和准确 Model ID | 先修正 URL 或资源 |
| `408` / 客户端超时 | 服务端未及时收到完整请求，或客户端停止等待 | 是否收到 HTTP 状态码、超时配置、网络和控制台请求状态 | 先检查请求是否已完成 |
| `409` / `422`（如返回） | 通常表示冲突或内容无法处理 | 响应详情、提交的状态或字段 | 先解决原因 |
| `429` | 达到某项限制 | 响应正文及重试相关响应头；参阅[限额排查常见问题](/zh/faq/codex/usage-limit-vs-api-rate-limit.md) | 仅在确认是临时限流时重试 |
| `500` | 服务端故障 | 请求记录、消息及故障是否持续 | 临时故障可有限次重试 |
| `502` / `503` | 网关或上游故障，或服务暂时不可用 | 响应正文和 `Retry-After` 响应头（如有） | 临时故障可有限次重试 |
| `504` | 网关等待上游响应超时 | 控制台记录及请求是否完成 | 重试前检查完成状态 |

协议与路径后缀错误请参阅 [Base URL 常见问题](/zh/faq/model-calling/base-url-config.md)。缓存相关问题请参阅[缓存计费常见问题](/zh/faq/service-assurance/cache-billing.md)。

## 重试临时故障

确认 `429` 或服务端故障属于临时问题后，采用带随机抖动的指数退避，并限制尝试次数。如果返回了 `Retry-After`，按其要求等待；若超出重试预算，则退出循环，在循环外等待。SDK 重试与自建循环共享总尝试次数预算，因此实现自己的策略时应关闭 SDK 自动重试。

不要原样重试 `400`、`401`、`403`、余额或配置错误。客户端超时或流中断，不能证明请求在处理前就已失败。先检查**控制台**，尤其是图片生成或可能重复执行操作的工作流。

以下示例发送非流式 Chat Completions 请求，最多尝试四次。它们会重试 HTTP `500`、`502` 和 `503`；在你确认 `429` 是临时限流前，默认关闭其重试。遇到超时、网络异常或 `504` 时停止，供你检查请求是否完成。每次重试前最多等待 30 秒。

### 准备请求

从[模型广场](https://bettertoken.ai/pricing)复制当前可用的 Model ID，并替换两个占位值。请选择支持 [Chat Completions](/zh/api-reference/chat-completions.md) 的文本模型。

```bash theme={null}
export BETTERTOKEN_API_KEY="YOUR_API_KEY"
export BETTERTOKEN_MODEL_ID="YOUR_MODEL_ID"
```

Python 示例仅使用标准库。将 JavaScript 示例保存为 `.mjs` 文件，并用支持 `fetch` 和 `AbortSignal.timeout` 的 Node.js 版本运行。

**Python**

```python theme={null}
import json
import os
import random
import time
import urllib.error
import urllib.request
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime

def retry_after_seconds(value):
    try:
        return max(0.0, float(value))
    except ValueError:
        return max(0.0, (
            parsedate_to_datetime(value) - datetime.now(timezone.utc)
        ).total_seconds())

retry_temporary_429 = False  # Enable only after diagnosing the returned limit.
payload = json.dumps({
    "model": os.environ["BETTERTOKEN_MODEL_ID"],
    "messages": [{"role": "user", "content": "Reply with one word: OK"}],
}).encode()
for attempt in range(4):
    request = urllib.request.Request(
        "https://bettertoken.ai/v1/chat/completions", data=payload,
        headers={
            "Authorization": "Bearer " + os.environ["BETTERTOKEN_API_KEY"],
            "Content-Type": "application/json",
        },
    )
    try:
        with urllib.request.urlopen(request, timeout=120) as response:
            print(json.load(response)["choices"][0]["message"]["content"])
        break
    except urllib.error.HTTPError as error:
        body = error.read().decode("utf-8", errors="replace")
        retryable = error.code in (500, 502, 503) or (
            error.code == 429 and retry_temporary_429
        )
        if not retryable or attempt == 3:
            raise RuntimeError(f"HTTP {error.code}: {body}") from error
        delay = random.uniform(0, min(8.0, 2.0 ** attempt))
        retry_after = error.headers.get("Retry-After")
        if retry_after:
            delay = max(delay, retry_after_seconds(retry_after))
        if delay > 30:
            raise RuntimeError("Retry-After exceeds this retry budget") from error
        time.sleep(delay)
```

**JavaScript**

```javascript theme={null}
const retryTemporary429 = false; // Enable only after diagnosing the returned limit.
const payload = {
  model: process.env.BETTERTOKEN_MODEL_ID,
  messages: [{ role: "user", content: "Reply with one word: OK" }],
};
for (let attempt = 0; attempt < 4; attempt++) {
  const response = await fetch("https://bettertoken.ai/v1/chat/completions", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.BETTERTOKEN_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(payload),
    signal: AbortSignal.timeout(120000),
  });
  const body = await response.text();
  if (response.ok) {
    console.log(JSON.parse(body).choices[0].message.content);
    break;
  }
  const retryable = [500, 502, 503].includes(response.status) ||
    (response.status === 429 && retryTemporary429);
  if (!retryable || attempt === 3) {
    throw new Error(`HTTP ${response.status}: ${body}`);
  }
  let delay = Math.random() * Math.min(8000, 1000 * 2 ** attempt);
  const retryAfter = response.headers.get("Retry-After");
  if (retryAfter !== null) {
    const seconds = Number(retryAfter);
    const wait = Number.isFinite(seconds)
      ? seconds * 1000 : Date.parse(retryAfter) - Date.now();
    if (!Number.isFinite(wait)) throw new Error("Invalid Retry-After header");
    delay = Math.max(delay, wait, 0);
  }
  if (delay > 30000) throw new Error("Retry-After exceeds this retry budget");
  await new Promise(resolve => setTimeout(resolve, delay));
}
```

如果响应正文表明存在持续的配置、余额或访问问题，即使状态码本身看似可以重试，也应停止循环并修复问题。将这个简短文本示例用于其他操作前，请按应用需要补充完成状态检查。

## 按现象查找指南

| 现象 | 参考文档 |
| - | - |
| Claude Code 认证、URL 或模型错误 | [Claude Code 常见错误](/zh/ai-tools/claude-code.md#常见错误) |
| Codex 要求登录或忽略 provider 修改 | [Codex CLI / App 常见错误](/zh/ai-tools/codex.md#常见错误) |
| Cursor Key 无法启用或内置模型停止工作 | [Cursor 常见错误](/zh/ai-tools/cursor.md#常见错误) |
| Cline 认证、`404` 或模型错误 | [Cline 常见错误](/zh/ai-tools/cline.md#常见错误) |
| OpenCode provider、模型或项目覆盖配置错误 | [OpenCode 常见错误](/zh/ai-tools/opencode.md#常见错误) |
| Claude Desktop Gateway 无法连接 | [Claude Desktop 常见错误](/zh/faq/claude-desktop-bettertoken-api.md#常见错误) |
| Codex 官方用量提示或 API `429` | [用量限额与 API 限流的区别](/zh/faq/codex/usage-limit-vs-api-rate-limit.md) |
| 修改似乎未生效 | [清除冲突的环境变量](/zh/best-practices.md#清除冲突的环境变量) |
| 输出被截断或报输出上限错误 | [输出 Token 上限](/zh/faq/model-calling/max-tokens.md) |

## 验证修复结果

使用之前出错的客户端、Base URL 和 Model ID，发送一个简短、非关键的请求。确认响应正常后，在**控制台**找到对应记录，核对模型、状态、Token 用量和费用，再继续较大的工作流。

## 常见问题

### 每个 429 都应该等待后重试吗？

请按[用量限额排查常见问题](/zh/faq/codex/usage-limit-vs-api-rate-limit.md)判断需要检查什么，以及当前响应是否适合重试。

### 失败一次就该增加超时时间吗？

先确认是客户端、代理还是上游请求停止了等待。按[超时配置](/zh/best-practices.md#超时配置)调整，再用小请求验证。

## 相关文档

* [SDK 请求示例](/zh/guides/sdk-examples.md)
* [流式输出与工具调用](/zh/guides/streaming-and-tool-calling.md)
* [API 参考](/zh/api-reference/introduction.md)
* [支持的工具](/zh/ai-tools/overview.md)
* [缓存计费常见问题](/zh/faq/service-assurance/cache-billing.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.