面向代码与 AI 智能体的响应与错误
所有成功响应共享同一种响应包装,所有错误共享同一种结构。本页介绍响应包装、各接口 data 的结构、缩减响应的方法、如何解读空字段,以及每个错误码的含义。
AI 智能体读到的也是同样的内容:MCP 服务器保留这些字段名、单位和约定,失败的工具调用带有同样稳定的 error.code。
#标准响应包装
{
"status": "ok",
"server_time": "2026-10-10T08:51:22.927Z",
"data_type": "basis",
"symbol": "BTCUSDT",
"data": {
"time": "2026-10-10T08:51:14.104Z",
"basis_value": -42.0048049747,
"basis_pct": -0.0507185886791,
"futures_price": 82776.0067294,
"spot_price": 82818.0115344
}
}| 字段 | 类型 | 说明 |
|---|---|---|
status | string | ok 或 error。 |
server_time | string | 响应生成的时刻,采用带毫秒的 ISO 8601 UTC 格式:即 data 中所有日期的格式。绝不是数据的时间,数据的时间由每行的 time 给出。 |
data_type | string | 返回类型的名称。无需依赖调用路径即可分发响应,适用于客户端经由队列或缓存的情况。 |
symbol | string | 请求中的交易对,在 data 之外回显。仅当接口接受 symbol 时出现。 |
timeframe | string | 你发送的 timeframe(或该路由的默认值),仅此而已。?fields= 不影响这些键。 |
step | string | 在列表上(所有 /history 路由及事件序列):行与行之间的间隔——即你请求的 timeframe,或在路由不接受 timeframe 时序列本身的频率(日线序列为 1d,资金费率为 8h,比特币手续费读数为 1m)。分桶行以分桶起点为时间;周期性读数以其自身时刻为时间。行间没有固定间隔时不出现(ETF 申报、期权最大痛点快照、全市场读数)。 |
market | string | 数据所属的市场:在提供多个市场的路由上取自路径(无后缀为 all = 现货 + 永续合约、spot、futures);在只提供一个市场的路由上为固定值(资金费率、未平仓量、爆仓和 trades/large 为 futures;跨交易所价差为 futures,稳定币为 spot)。 |
data | object | array | null | 数据主体:在“最新值”接口上为一个对象,在 /history 接口上为一个列表(按时间从新到旧),没有可提供的数据时为 null——此时由 unavailable 说明原因。 |
next | string | null | 在分页路由上:下一页的相对 URL,可直接调用(读完范围后为 null)。参见“分页读取历史”。 |
探测接口、POST 接口与数据形态
探测接口使用相同的响应包装:/v1/health(data_type: health,全部位于 data 下)和 /v1/status(data_type: status;其 status 与任何成功响应一样为 ok,数据流状态见 data.state,为 ok 或 degraded)。POST /v1/indicators 与其他接口一样使用响应包装;其 data 带有一个按你的标识符索引的 indicators 对象。“最新值”接口始终返回对象(或 null)——/v1/klines 是最近一根已收盘的 K 线——,/history 接口始终返回列表。其余的列表都是集合,每个元素一行:/v1/symbols、/v1/macro/series、/v1/macro/intraday/series、/v1/etf/fundamentals(每个发行方一行)、/v1/options/max-pain、/v1/options/expiry、/v1/options/oi/distribution,以及事件序列(/v1/trades/spot、/v1/trades/futures、/v1/trades/large、/v1/trades-kline/futures/seconds、/v1/liquidations/events)。交互式参考给出每个接口的确切 schema。
#缩减响应
?fields= 参数将 data 限制为所请求的字段:以逗号分隔的列表,嵌套字段用点号表示。在一千行的序列中如果只用两列,响应也会相应缩小。
# The full response
curl -H "X-API-KEY: $BYTNODE_API_KEY" \
"https://api.bytnode.com/v1/basis?symbol=BTCUSDT"
# Two fields only
curl -H "X-API-KEY: $BYTNODE_API_KEY" \
"https://api.bytnode.com/v1/basis?symbol=BTCUSDT&fields=time,basis_pct"
# Dotted notation for a nested field
curl -H "X-API-KEY: $BYTNODE_API_KEY" \
"https://api.bytnode.com/v1/btc/network?fields=difficulty,next_adjustment.estimated_retarget_at"未知字段会被拒绝,并建议最接近的字段,而不是默不作声:
{
"status": "error",
"server_time": "2026-09-30T16:07:27.808Z",
"error": {
"code": "unknown_field",
"message": "Unknown field 'basis_percent'. Did you mean 'basis_pct'? Accepted fields: basis_pct, basis_value, futures_price, spot_price, time.",
"param": "fields",
"details": {
"suggestion": "basis_pct",
"accepted": [
"basis_pct",
"basis_value",
"futures_price",
"spot_price",
"time"
]
}
}
}#解读空字段
null 或空列表并不能证明现象不存在:它也可能意味着该现象对这个资产根本不存在。混淆两者会导致错误结论,比如资产是没有期货市场的稳定币时,却说“没有爆仓”。
每个接口都通过 unavailable 键消除歧义:当 data 为空(null 或空列表)时,响应会说明原因——REST 接口如此,快照的每个字段也是如此。请检查 unavailable,而不是 data 是否为空:
| 原因 | 类型 | 说明 |
|---|---|---|
not_applicable | structural | 该指标在此没有意义:在现货稳定币上请求源自期货市场的字段。无论采集多少数据都不会改变。 |
no_api_key | configuration | 上游数据源未接入本安装:数据从未被采集。这是基础设施缺失,而非市场事实。 |
no_data | market | 在所请求窗口内确实没有数据。唯一可以据此得出结论的情况。 |
#错误
所有错误形式相同:status: "error"、server_time 和一个 error 对象(code 稳定且机器可读;message 为英文;有参数出错时带 param)。与方案相关的拒绝还带有 X-Deny-Reason 响应头,其值即 error.code(missing_api_key、invalid_api_key、plan_family、plan_websocket、rate_limited、quota_exceeded、history_depth);按时间的拒绝会在正文中加上 error.details.retry_after_s,并以相同的值给出 Retry-After 响应头:速率限制时为距下一次允许请求的等待时间,月度配额时为距重置的秒数。所有响应(包括错误)都带有 X-Request-Id 响应头:报告问题时请附上它。error.details 在错误带有结构化数据时出现,以机器可读方式给出相同信息:suggestion(最接近的可接受值)、accepted(可接受的值)、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
}
}
}| 状态码 | 最常见原因 | 如何处理 |
|---|---|---|
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)后重试:原样重放请求会耗费同样的时间。 |
错误码页面逐一介绍每种情况,附准确的消息,以及指标和实时市场数据流特有的错误。
#响应头
凡是使用计量密钥(控制台中创建的每把密钥都是)发出的请求,其响应都带有速率限制响应头,采用 IETF 标准格式:你的方案策略及其剩余额度,包括月度配额。控制台显示同样的状态,达到 80% 时会提醒,首次请求被拒时再次提醒。另外两个响应头只在拒绝时出现。
| 响应头 | 类型 | 说明 |
|---|---|---|
X-Deny-Reason | string | 与方案相关的拒绝原因:rate(速率限制)、quota(月度配额)、family(市场数据族不在方案内)、websocket(数据流不在方案内)、key(密钥缺失、无效或已吊销)或 depth(历史超出方案窗口)。 |
Retry-After | integer | 出现在 429 上,以及已知重试延迟时的 503 上。重试前需要等待的秒数:超出频率限制时为 1,月度配额用尽时为距离重置(每月第一天,UTC)的秒数。请遵守该值而不要自行设定延迟;若没有该字段,请逐步拉长重试间隔。 |
RateLimit-Policy | string | 出现在所有使用计量密钥的响应中。你方案的策略,名称是固定标识符:"rate"(每分钟速率的令牌桶:q 为突发容量,w 为从空桶补满所需的秒数)和 "quota"(月度配额:q 为每月请求数,w 为当前自然月的秒数)。Traders 方案在 31 天的月份中的示例:"rate";q=20;w=20, "quota";q=200000;w=2678400。 |
RateLimit | string | 出现在所有使用计量密钥的响应中。每项策略的剩余额度:r 为剩余请求数("quota" 的 r 即月度配额余量),t 为完全恢复前的秒数(对 "quota" 而言:直到下个月 1 日 00:00 UTC)。示例:"rate";r=19;t=1, "quota";r=187342;t=1641600。JSON 形式的相同数值:GET /v1/limits。 |
Cache-Control | string | 所有数据响应均带此头:最多 private, max-age=1——响应的延迟通常不超过一秒(缓存副本偶尔可达两秒)。每条路由的数据源更新频率(单笔成交为 2 秒,宏观、链上与 ETF 日度序列为 5 分钟)见交互式参考:比这更频繁地轮询会得到相同的值。X-Cache-Status 请求头(HIT、MISS、EXPIRED、UPDATING)说明响应是否来自网关的一秒缓存:仅供参考,不应在代码中据此分支。 |