错误与速率限制

错误与速率限制

所有端点均以相同格式报告失败,并附带机器可读的错误码和请求 ID。

错误响应

调用失败时返回 success: false。code 保持稳定,供程序使用;error 是供人阅读的简短英文信息;request_id 用于在我们的日志中定位该次调用。当问题出在输入上时,param 会指出有问题的参数。

真实示例

以下为实时响应,在渲染本页面时从 API 获取。

缺少必填参数 · GET /api/sd/oi · HTTP 400
{
  "success": false,
  "error": "coin required",
  "code": "INVALID_PARAM",
  "request_id": "cb-mwefrrsx-0a5b9197d4"
}
未知端点 · HTTP 404
{
  "success": false,
  "code": "NOT_FOUND",
  "error": "unknown endpoint: GET /api/this-endpoint-does-not-exist",
  "request_id": "cb-mwefrrsy-e11c93374f"
}

错误码

错误码HTTP含义处理方式
INVALID_PARAM400缺少必填参数,或某个取值不被接受。已知是哪个参数时,`param` 会给出其名称。请勿原样重试;请修正请求。
UNAUTHORIZED401已发送 `CB-API-KEY` 请求头,但该密钥未知或已被吊销。公开数据无需密钥,请去掉该请求头或使用有效密钥。请勿原样重试。
FORBIDDEN403该路由不属于公开 API(内部或仅限账户使用)。请勿重试。
NOT_FOUND404未知路由,或所请求的币种 / 交易所 / 基金不存在。请勿重试;请对照目录检查路径和标识符。
RATE_LIMITED429请求数超过每分钟限额。`Retry-After` 给出需要等待的秒数。等待 `Retry-After` 指定的时间后,再以退避方式重试。
INTERNAL500意外的服务器错误。错误信息有意保持通用;报告问题时请附上 `request_id`。几秒后重试一次;若问题持续,请附上请求 ID 报告。
UPSTREAM_FAILED503该端点背后的数据源未及时响应。不会返回过期数据或替代数值来顶替。以退避方式重试;并查看状态页。
UNAVAILABLE501 / 503数据集暂时未就绪(缓存预热中),或该端点尚无合法的公开数据源。请稍后重试;目录会标出因设计原因而不可用的端点。
REQUEST_FAILED200部分较旧的端点会以 HTTP 200 返回 `success: false`。请务必检查 `success`,而不只是 HTTP 状态码。取决于具体信息;按请求失败处理。
对于不存在的条目,旧版端点可能返回 HTTP 200 并附带 success: false 和错误码 REQUEST_FAILED。请对每个响应都检查 success。

请求 ID

每个响应都带有 X-Request-Id 响应头。失败的响应还会在响应体中以 request_id 重复该值。由短时边缘缓存返回的响应会沿用生成该响应的原始请求的 ID,因此连续调用得到相同的 ID 说明你收到的是缓存副本。

速率限制

限制按分钟计算:匿名调用按 IP 地址计数,携带有效 CB-API-KEY 请求头时按密钥计数。上限为每分钟 1200 次请求。/api/coin-icon 下的图标请求不计入。

速率限制响应头

名称说明
X-Request-Id本次请求的标识符。提交问题报告时请附上。
X-RateLimit-Limit当前一分钟窗口内允许的请求数。
X-RateLimit-Remaining当前窗口内剩余的请求数。
X-RateLimit-Reset窗口重置的 Unix 时间,单位为秒。
Retry-After随 HTTP 429 返回:重试前需等待的秒数。
X-Cache服务端响应缓存的状态:HIT、STALE 或 MISS。

超出限制时

API 返回 HTTP 429,错误码为 RATE_LIMITED。请等待 Retry-After 给出的秒数后再重试。轮询频率高于端点的缓存 TTL 只会得到相同的缓存响应,因此请让轮询间隔与各参考页面所示的 TTL 保持一致。

退避重试

JavaScript
async function getJson(url, tries = 4) {
  for (let i = 0; i < tries; i++) {
    const res = await fetch(url);
    if (res.status === 429 || res.status >= 500) {
      const wait = Number(res.headers.get("Retry-After")) || 2 ** i;
      await new Promise((r) => setTimeout(r, wait * 1000));
      continue;
    }
    const body = await res.json();
    if (body.success === false) {
      throw new Error(body.code + ": " + body.error + " (" + body.request_id + ")");
    }
    return body;
  }
  throw new Error("gave up after " + tries + " attempts");
}

没有数据不是错误

当参数有效但该数据没有合法的公开来源时,调用仍以 HTTP 200 成功返回:数值字段为 null,meta.quality 为 UNAVAILABLE,coverage.unavailable 列出原因。请将其渲染为短横线,不要转换为 0。

过期数据

如果采集器出现滞后,端点会继续返回最后入库的数值并标记 meta.is_stale: true,直到这些数值超出可用窗口;此后它们会从聚合值中剔除,并列入 coverage.stale_excluded。