halfoff.dev文档
获取密钥
模型 API

速率限制与故障转移

按密钥设置的请求数和并发限制、短暂的容量排队、跨渠道的自动故障转移。会遇到哪些状态码,以及如何安全地重试。

token 再便宜,请求发不出去也没用。从你的请求到模型之间隔着三样东西:你给密钥设的限额、模型背后各渠道的容量,以及这些渠道的健康状况。容量和健康方面的问题,网关大多会自己消化:短暂等待,或者换个渠道重试。实在处理不了时,你看到的是一个简短、可重试的 429 或 502。

自动故障转移#

渠道失败时,请求会在同一渠道分组的另一个渠道上重试,每个请求最多尝试三次。以下情况会触发重试——渠道:

  • 超时、无法连接,或返回 408、409、425、429 或任何 5xx;
  • 因其自身原因(而不是你的原因)拒绝请求;
  • 在流式输出的第一个 token 之前失败(见流式输出)。

新对话会分散到服务该模型的各个渠道上。错误比平时多、或响应明显比平时慢的渠道,会少分到新对话,直到恢复为止。持续失败的渠道会被暂时移出轮换,固定在它上面的对话会转到健康的渠道。

有两种情况永远不会重试:

  • 请求本身无效。 模型以 400、404、413 或 422 拒绝时,换渠道也一样会失败。你会收到该状态码和原因。
  • 已经开始输出。 第一个 token 之后,请求就绑定在该渠道上。之后的失败会以流内错误事件的形式出现。

等待容量#

每个渠道同一时间能处理的请求数有限。请求要去的渠道满了,网关会尝试服务该模型的其他渠道;全都满了,请求就排进预计最先空出来的那个渠道的队列。

总等待时间上限为 10 秒。被接纳的请求会在 x-halfoff-queued-ms 中报告等待时长。未被接纳的请求返回 429,消息为 All upstream capacity for this model is busy. Retry shortly.,并带有 retry-after 响应头。

排队不花钱:请求真正开始运行后才计费,被 429 拒绝的请求一律不计费。

给自己的密钥设限#

在 API 密钥页面创建或编辑密钥时,可以在 限制与约束 下为每个密钥设置 每分钟请求数 和 最大并发请求数。默认都不限制。

限制统计口径
每分钟请求数过去 60 秒内发起的请求,滚动窗口。
最大并发请求数正在处理的请求,未结束的流也算在内。

超出限制的请求最多等待 2 秒;仍没有空位,就返回 429,消息为 This API key has reached its rate or concurrency limit.,并带有以秒为单位的 retry-after。给每类任务配一个有独立限额的密钥,可以避免一个任务把另一个饿死。其他密钥设置见 API 密钥。

状态码#

状态码原因怎么办
429触发密钥的每分钟请求数或并发限制。带 retry-after。等待 retry-after 秒,或调高限额。
429该模型的全部容量持续繁忙 10 秒。带 retry-after。等待 retry-after 秒后重试。
429尝试过的每个渠道都返回了限流。消息 Upstream capacity for this model is busy. Retry shortly.退避后重试。
502尝试过的每个渠道都失败了。消息 The model provider returned an error. Please retry.退避后重试。
500网关意外错误。重试一两次;仍然失败,请带上 x-halfoff-request-id 联系客服。
402余额小于等于零,或密钥已达消费上限。充值或调高上限。不要重试。
400、404、413、422请求无效、过大,或模型 ID 未知。修正请求。不要重试。
401、403密钥缺失、已吊销或已过期,或无权使用该模型。修正密钥。不要重试。

错误类型随你的 API 格式而定:429 对应 rate_limit_error(Anthropic)、rate_limit_exceeded(OpenAI)或 RESOURCE_EXHAUSTED(Gemini);502 对应 api_error(Gemini 为 UNAVAILABLE)。完整列表见错误。

在代码里重试#

你看到 429 或 502 时,网关已经试过好几个渠道了。再用指数退避加随机抖动重试几次,有 retry-after 时按它来。

Anthropic 和 OpenAI 的 SDK 默认就会对 408、409、429 和 5xx 重试两次。跑大批量任务时可以调高:

Python
import os
from openai import OpenAI

client = OpenAI(base_url="https://halfoff.dev/v1", api_key=os.environ["HALFOFF_API_KEY"], max_retries=6)

直接用 HTTP 时,自己实现同样的策略:

# --retry 覆盖 429 和 5xx,并遵守 retry-after
curl --retry 6 --retry-max-time 180 https://halfoff.dev/v1/chat/completions \
  -H "Authorization: Bearer $HALFOFF_API_KEY" \
  -H "content-type: application/json" \
  -d '{"model": "gpt-5.6-luna", "messages": [{"role": "user", "content": "给这条反馈分类:\"登录时应用崩溃\""}]}'

对于流式请求,只有在收到任何内容之前出错才重试。流中途断开时,已输出的部分已经计费,你需要决定是保留它,还是重新请求完整回答。

余额与消费上限#

每个请求运行前都会检查余额和密钥的消费上限。余额小于等于零,或密钥已用完上限,会返回 402,消息为 Insufficient balance. Top up at the dashboard. 或 This API key has reached its spend limit.。错误类型为 insufficient_quota(OpenAI)、billing_error(Anthropic)或 RESOURCE_EXHAUSTED(Gemini)。

检查发生在请求开始时,扣费在请求结束几秒后结算。开始时余额为正的请求一定会执行完,因此余额可能略低于零,下次充值时会补上。见账单。