错误码
错误总会说明哪里出了问题,并在可能时说明正确的写法。本页按通道列出各种情况,并区分哪些值得重试、哪些不值得。
通过 MCP 连接的 AI 智能体会收到同样的错误码:失败的工具调用以 Error (HTTP NNN) 开头,以稳定的错误码结尾,AI 智能体可据此自行修正调用。见下文 MCP 服务器。
#错误的结构
所有错误只有一种形式,无论是哪一层拒绝(网关、服务或校验):status: "error"、server_time 和一个 error 对象,包含稳定的 code、英文 message,以及有参数出错时的 param。error.details 是错误带有结构化数据时出现的对象,以机器可读的方式给出相同信息:suggestion、accepted、history_depth 时你的方案窗口、按时间的拒绝中的 retry_after_s。error 旁边没有其他键。
{
"status": "error",
"server_time": "2026-09-30T16:07:27.808Z",
"error": {
"code": "invalid_symbol",
"message": "Unknown symbol 'BTCUSD'. Did you mean 'BTCUSDT'? Accepted values: ADAUSDT, AVAXUSDT, BNBUSDT, BTCUSDT, DOGEUSDT, ETHUSDT, GRAMUSDT, HBARUSDT, HYPEUSDT, LINKUSDT, LTCUSDT, NEARUSDT, SOLUSDT, SUIUSDT, TAOUSDT, TRXUSDT, UNIUSDT, USDCUSDT, USDTUSDC, XLMUSDT, XMRUSDT, XRPUSDT.",
"param": "symbol",
"details": {
"suggestion": "BTCUSDT",
"accepted": [
"ADAUSDT",
"AVAXUSDT",
"BNBUSDT",
"BTCUSDT",
"DOGEUSDT",
"ETHUSDT",
"GRAMUSDT",
"HBARUSDT",
"HYPEUSDT",
"LINKUSDT",
"LTCUSDT",
"NEARUSDT",
"SOLUSDT",
"SUIUSDT",
"TAOUSDT",
"TRXUSDT",
"UNIUSDT",
"USDCUSDT",
"USDTUSDC",
"XLMUSDT",
"XMRUSDT",
"XRPUSDT"
]
}
}
}与方案相关的拒绝具有相同的结构,基于时间的拒绝还会加上 error.details.retry_after_s(同样出现在 Retry-After 响应头中)。X-Deny-Reason 响应头指明原因:rate_limited、quota_exceeded、plan_family、plan_websocket、missing_api_key、invalid_api_key 或 history_depth。
{
"status": "error",
"server_time": "2026-09-30T16:07:27.808Z",
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded: your plan allows a limited number of requests per minute. Retry after the Retry-After delay.",
"details": {
"retry_after_s": 2
}
}
}深度相关的 403 与其他方案相关的拒绝一样,带有 X-Deny-Reason: history_depth 和 error.code: history_depth;其消息会给出该周期下可使用的最大 limit。各方案的窗口见使用限制页面。
错误码列表
请依据 error.code 进行分支处理,绝不要依据消息文本:错误码是契约,消息可能会改写。新增错误码不属于破坏性变更;重命名或删除错误码才是。
| error.code | 原因 |
|---|---|
depth_too_large | 某个快照字段的深度超过了其文档规定的上限。 |
edge_rate_limited | 来自该 IP 地址的请求在边缘网关处过多(防洪:每个地址每秒 20 个,突发 40 个)。请在 1 秒后重试。 |
history_depth | 所请求的历史早于你的方案所允许的范围。 |
https_required | Plain HTTP is refused: call the same URL over https://. A key sent over http:// travelled unencrypted: rotate it. |
insufficient_data | 历史数据不足,无法计算所请求的指标。 |
internal_error | 意外的服务端错误。以递增间隔重试可能成功;若持续出现,请附上响应的 X-Request-Id 头报告。 |
invalid_api_key | API 密钥缺失、无效或已吊销。 |
invalid_asset | 该市场数据族不提供此资产。错误会列出可接受的值。 |
invalid_channel | WebSocket:频道名称格式错误或未知;错误信息会列出可接受的格式。 |
invalid_depth | 某个快照字段的深度不是正整数。 |
invalid_indicator | POST /v1/indicators 的正文无效(id、type 或参数)。 |
invalid_message | WebSocket:消息不是可读的 JSON 对象。 |
invalid_parameter | 某个参数的类型或取值无效。 |
invalid_series | 该宏观序列不存在。请查看该市场数据族的 /series 目录。 |
invalid_symbol | 不提供该交易对。错误会列出可接受的值(见 /v1/symbols)。 |
invalid_time_range | since_ms / until_ms 不是有效的 Unix 毫秒时间戳,或 since_ms > until_ms(until_ms 含边界),或 since_ms >= before_ms。 |
invalid_timeframe | 该接口不接受此周期。 |
method_not_allowed | 该接口存在,但不支持此 HTTP 方法。 |
missing_api_key | No API key was sent: put it in the X-API-KEY header (or api_key in the WebSocket URL). |
missing_parameter | 缺少必填参数。 |
not_applicable | WebSocket:该频道对此资产没有意义(稳定币的期货频道)。REST API 对此类请求返回 200 和 unavailable: not_applicable。 |
not_authenticated | WebSocket:在尚未认证的会话上收到了 auth 以外的消息(少见:密钥在握手时校验,缺少密钥时返回 401 invalid_api_key)。 |
not_found | 未知接口。 |
plan_family | 你的方案不包含该市场数据族:消息中会给出其名称,GET /v1/limits 会列出你的密钥可用的市场数据族。 |
plan_websocket | 你的方案不包含 WebSocket 数据流。 |
quota_exceeded | 你的方案的月度配额已用尽。Retry-After(以及 error.details.retry_after_s)给出距离重置的秒数,重置时间为每月第一天(UTC)。 |
rate_limited | 超出你的方案的每分钟速率。请在 Retry-After 给出的秒数后重试。 |
repeated_parameter | 某个参数出现了不止一次。 |
service_unavailable | 暂时不可用。请在 Retry-After 给出的秒数后重试。 |
timeframe_not_served | 该序列存在,但不提供此周期(例如仅有日线的序列被以 1h 请求)。接口从不提供的周期返回 invalid_timeframe。 |
unknown_field | ?fields= 或快照中的字段名不存在。 |
unknown_parameter | 该接口未声明此参数。 |
upstream_timeout | 请求耗时过长。请缩小时间窗口或深度后重试。 |
const response = await fetch(url, { headers: { "X-API-KEY": key } });
const body = await response.json();
if (body.status === "error") {
switch (body.error.code) {
case "rate_limited":
case "service_unavailable":
// Transient: wait Retry-After seconds, then retry.
break;
case "history_depth":
// Ask for less history, or upgrade: error.message gives the max limit.
break;
case "invalid_symbol":
case "unknown_parameter":
// Fix the request: error.param names the culprit, error.message the fix.
break;
default:
throw new Error(body.error.code + ": " + body.error.message);
}
}#HTTP 状态码
| 状态码 | 原因 | 修复 |
|---|---|---|
401API 密钥被拒绝 | API 密钥缺失、无效或已吊销(error.code:invalid_api_key)。响应带有 WWW-Authenticate: ApiKey header="X-API-KEY"。 | 请确认 X-API-KEY 请求头确实已发送:部分 HTTP 客户端在重定向后会丢失它。可在控制台创建或复制密钥。 |
403被方案拒绝 | 密钥有效,但方案不涵盖该请求;X-Deny-Reason 响应头带有与 error.code 相同的代码:plan_family(市场数据族不在方案内)、plan_websocket(WebSocket 数据流不在方案内)或 history_depth(历史深度超出方案窗口)。 | 对于 plan_family 和 plan_websocket,数据存在但不在你的方案中。对于 history_depth,error.message 会给出可使用的最大 limit。 |
404未知接口 | 该路径不存在(error.code: not_found)。如有最接近的接口,消息会给出。 | 请检查路径:如有最接近的接口,消息会给出;所有接口都在合约中。 |
405不允许的方法 | 接口存在,但不支持该方法(error.code: method_not_allowed);Allow 响应头列出支持的方法。 | 使用 GET(仅 /v1/indicators 使用 POST);HEAD 可用于所有 GET 接口,OPTIONS(CORS 预检)可用于所有接口。 |
422参数缺失、未知或无效 | 交易对、资产或序列未知或已停用、深度超过该字段上限、指标参数越界,缺少 symbol、周期不在列表中、请求多周期字段时缺少 @tf 后缀、?fields= 中有未知字段、传入路由未声明的参数或重复的参数、since_ms 或 until_ms 以秒为单位或超出范围、JSON 正文无效。 | 对于未知的字段或参数,响应会建议最接近的名称。对于周期,会列出可接受的值。 |
429超出速率或配额 | 每分钟请求数超过方案允许,或突发过于密集(X-Deny-Reason: rate_limited,Retry-After:距下一个令牌的等待时间);或账户月度配额已用尽(X-Deny-Reason: quota_exceeded,Retry-After:距离重置的秒数)。 | 对于 rate_limited,等待 Retry-After 给出的秒数。对于 quota_exceeded,计数在每月第一天(UTC)重置;你也可以在控制台更换方案。 |
503暂时不可用 | 某个读取依赖正在恢复。在 WebSocket 握手时,表示数据流服务已达到其总容量(所有账户合计):这不是你方案的连接数上限,后者以 4003 关闭。 | 在 Retry-After 给出的延迟后重试;若无该响应头,则以递增的间隔重试。此错误本质上是暂时的。 |
500意外错误 | 服务端故障,绝不是由请求引起。 | 请重试;若问题持续,请附上响应中的 X-Request-Id 响应头报告给我们。 |
504请求耗时过长 | 所请求的时间窗口或深度导致查询耗时过长(error.code: upstream_timeout)。 | 减小 limit 或时间范围(since_ms / until_ms)后重试:原样重放请求会耗费同样的时间。 |
最常见的混淆
| 症状 | 真实原因 |
|---|---|
| 密钥有效却返回 403 | 查看 X-Deny-Reason(即 error.code)。plan_family:该端点的市场数据族不在你的方案中。plan_websocket:方案不含数据流。history_depth:请求超出方案的回溯窗口,error.details 给出允许的最大 limit。无明显原因的 invalid_api_key:HTTP 客户端在重定向后丢弃了自定义请求头;请直接调用最终 URL。 |
| 请求频率不高却返回 429 | 用尽的是账户的月度配额(X-Deny-Reason: quota_exceeded,Retry-After:距离重置的秒数),而不是速率限制。配额在每月第一天(UTC)重置,或在更换方案后立即恢复。 |
| 快照返回 422 但没有明确信息 | 请求多周期字段时缺少 @tf 后缀,或在单一形式的字段上加了后缀。 |
| 看似合理的深度返回 422 | 快照的每个字段都有自己的上限和单位。CVD 最多 200 个窗口,交易所间价差最多 50 个。 |
| 在别处有效的周期返回 422 | 日内宏观品种不接受 30m,周线只存在于 K 线和指标中。 |
| 不是错误的空响应 | 在稳定币上请求期货指标:unavailable 键会说明适用三种原因中的哪一种。(期权和 ETF 接口只提供 BTC 和 ETH:其他资产返回 422 invalid_asset,并列出可用值。) |
| 看似无害的参数却返回 422 | 每个路由都会拒绝未声明的参数和重复传入的参数;快照对未知键也同样处理。响应会建议最接近的名称:拼错的参数绝不会被悄悄忽略。 |
| 看似正确的 since_ms 却返回 422 | since_ms 与 until_ms 的单位为毫秒。以秒、微秒或纳秒表示的时间戳会在执行前被拒绝,负数、颠倒或早于 2009-01-03 的边界也一样——since_ms=0 除外,它表示数据的起点。 |
#指标
校验按固定顺序进行(timeframe、limit、交易对、解析指标、上限规则、读取 K 线),以第一个失败为准。因此修正后的请求体可能暴露第二个错误。
| 情况 | 状态码 |
|---|---|
| 周期无效、limit 不在 1–1000 之间、交易对未知 | 422 |
| indicators 列表为空,或某个条目不是对象 | 422 |
| 缺少 id 或 type 键,id 无效,或请求体字段未知 | 422 |
| 不支持的类型 | 422 |
| 该类型的未知参数(例如带 period 的 obv) | 422 |
| 参数类型错误,或低于最小值 | 422 |
| macd 或 adosc 的 fast 大于或等于 slow | 422 |
| sar 的 acceleration 大于 maximum | 422 |
| 标识符重复 | 422 |
| 正文格式错误、超过 50 个实例、字段过长 | 422 |
| limit 加预热超过 1000 | 422 |
| 该周期数据不足,或成交量历史太短,不足以计算成交量类指标 | 422 |
#WebSocket 数据流
每个无法满足的请求都会收到 op: error 消息,说明期望的内容:有效交易对列表、频道列表、depth 的范围。有三种情况不会产生错误,而是关闭连接:
| 代码 | 原因 |
|---|---|
| 4003 · 上限 | 已达到方案的同时连接数上限。原因中会给出上限。断开连接会立即释放名额。 |
| 4005 · 客户端过慢 | 发送队列已溢出十次。请加快消费速度,或减少频道数量。原因会区分这两种情况。 |
| 4001 | 实际上不再发送:密钥在握手时校验(401),在已打开的会话上发送 auth 帧会返回 authenticated。 |
| 4002 | 会话在五秒内始终未认证。由于密钥已在握手时校验,这种情况很少见。 |
密钥在握手时(连接打开前)校验:缺少密钥返回 401 missing_api_key,密钥未知或已吊销返回 401 invalid_api_key(带 WWW-Authenticate),不含数据流的方案(Free)返回 403 plan_websocket——每次都是 X-Deny-Reason 的值。此时的 429 表示打开连接过于频繁,503 表示数据流服务已达容量上限(所有账户合计):请稍后重试。连接后,若频道所属市场数据族不在方案内,会收到带 error.code: plan_family 的 op: error,整个订阅被拒绝。参见使用限制。
#MCP 服务器
错误遵循同一种格式 Error (HTTP NNN): message [code: …],失败的工具调用会被标记为 MCP 错误(isError),同一错误也放在 structuredContent.error 中:status, code, message,适用时还有 param, hint, retry_after_s。每次工具调用都按使用客户端密钥的 REST 请求来判断:方案外数据族的 403 或配额相关的 429 会原样返回,并附带其消息和 error.code。有两类错误是它特有的:
- 请求无效:未知字段;缺少目标(
symbol、asset、series),或提供了所请求字段都不需要的目标;所选路由不接受的参数;没有任何字段使用的timeframe;对不支持日期窗口的字段指定窗口。所有问题在一条消息中列出,并且在任何网络调用之前返回。 - 部分响应:在五个 MCP 数据工具(
get_pair_data、get_options_data…)中,某个字段出错不会影响其他字段。它会列在errors下,响应带有partial: true;只有所有字段都失败时,调用本身才会失败。
只有在上游没有响应时(502、503、504 或网络故障)才会重试:500 不会重放,因为每个请求都计入客户端配额。超时会在三十秒后返回 Error (HTTP 504)。
#重试策略
并非所有请求都可以重放。重放错误的请求只会得到完全相同的响应,白白消耗配额。
import httpx # pip install httpx
def should_retry(response: httpx.Response) -> bool:
"""What is transient, and what never will be (the official SDKs do the same)."""
# A spent monthly quota only resets on the 1st of the month: fail fast.
if response.status_code == 429 and response.headers.get("X-Deny-Reason") == "quota_exceeded":
return False
# 429 (rate) and 503: the server says when, in Retry-After. 502 and 504:
# a gateway hiccup or a slow query. Retry with an increasing delay.
if response.status_code in (429, 502, 503, 504):
return True
# 500: a fault on the service side, retry once.
if response.status_code == 500:
return True
# 401, 403, 404, 405, 422: the request is at fault. Replaying it as is
# produces exactly the same response.
return False- 遵守
Retry-After,不要自行设定延迟:服务器知道自己何时就绪。 - 限制重试次数。三到四次就够了:再多说明问题不是暂时的。
- 不要重放 4xx(429 除外)。请修正请求。
- 在数据流上,请以递增的延迟重连,并查看关闭代码:
4003表示已达方案上限,需先关闭账户的另一个连接;4005(client too slow)表示客户端过载,不改变消费方式而重连会再次被断开。握手时的503只需等待。