REST API

面向开发者与 AI 智能体的 REST API

一次请求,一个已聚合好的响应。所有接口共享相同的结构、相同的响应包装和相同的通用参数:适用于一个市场数据族的规则,同样适用于其他市场数据族。

AI 智能体无需构造 URL 即可访问同样的接口:MCP 服务器的工具用你的密钥调用它们,并返回相同的字段。

#请求的结构

结构
GET https://api.bytnode.com/v1/<family>/<metric>
  ?symbol=BTCUSDT       # required on per-asset routes
  &timeframe=1h         # on bucketed routes
  &limit=100            # on /history routes
  &fields=time,rate_pct

X-API-KEY: your_key

路径表明市场数据族和指标。参数表明资产、周期和深度。请求头携带密钥。调用任何接口只需要知道这些。

只有一个端点不遵循这种结构:POST /v1/indicators,它接收 JSON 正文,因为它描述的是一次计算,而不是一次读取。

#最新值还是历史

大多数指标有两个版本。不带后缀的接口返回最新值:一个对象,或 null。/history 后缀返回一个仅包含已收盘窗口的列表,按时间从新到旧排列。

curl
# The latest value
GET /v1/basis?symbol=BTCUSDT

# The last 24, from newest to oldest
GET /v1/basis/history?symbol=BTCUSDT&timeframe=1h&limit=24

# A dated window, in epoch milliseconds (here the last 12 hours, computed by the shell)
NOW=$(( $(date +%s) * 1000 ))
curl -H "X-API-KEY: $BYTNODE_API_KEY" \
  "https://api.bytnode.com/v1/basis/history?symbol=BTCUSDT&timeframe=1h&since_ms=$(( NOW - 43200000 ))&until_ms=$NOW"

#通用参数

参数类型默认值说明
symbolstring必填资产,使用目录格式(BTCUSDT),不区分大小写。缺失:422。未知:422,并附列表。全市场接口不接受此参数。
timeframestring必填按区间的接口上为必填:1m、5m、15m、30m、1h、4h、1d(K 线另有 1w)。缺失或不在列表中:422。逐分钟一行即 timeframe=1m。
limitinteger—/history 与序列接口返回的行数,最新在前:所有接口默认 100、最多 1000;单个事件(现货与期货逐笔、大额成交、清算)最多 10 000。超过一页时请沿 next 翻页。
since_ms / until_msinteger—以 Unix 毫秒表示的时间窗口,仅限支持该参数的接口。过滤先于 limit 生效。
livebooleanfalse用于按区间的最新值接口:返回进行中的周期,标记为 is_closed: false。
fieldsstring—data 的 CSV 投影,嵌套字段用点号表示。未知字段:422,并给出建议。

不存在的东西

任何接口都没有 exchange 参数:响应是聚合的,不标明交易所,唯一的例外是交易所间价差。传入该参数会返回 422;传入接口未声明的参数或重复的参数也同样如此,错误信息会建议最接近的名称。

#历史分页

每个 /history 路由都通过 next 分页:每个响应都带有下一页的 URL,读完范围后为 null。默认从最新到最旧排列;使用 order=asc(并带 since_ms)时从最旧开始。每一行都带有 time 和 is_closed。

  1. 请求第一页:limit 行;不设边界即获取最新的行,或用 since_ms 加 order=asc 从某个日期向后读取。
  2. 直接调用 next:它是下一页的相对 URL(/v1/klines/history?symbol=BTCUSDT&timeframe=1h&limit=500&before_ms=…),已包含你的参数——在前面加上 API 主机即可。向过去翻页时使用 before_ms,这是一个不包含的边界:任何一行都不会重复出现,无需做减法。
  3. 当 next 为 null 时停止:范围已读完,或者下一页会超出你方案的历史窗口(此时最后一页只请求仍在窗口内的行)。只有当你自己写入超出该窗口的边界时才会返回 403 history_depth;GET /v1/limits 给出你方案的各个窗口。对于逐笔成交(/v1/trades/*),每页最多覆盖 15 分钟,即使 next 不为 null,该页也可能为空或不完整:只有 next: null 才表示范围结束。所有分页接口都是如此:历史读取遇到数据密集的时段时,会在其时间预算内返回已读取的行——一页较短但带有 next,绝不会超时。官方 SDK 会替您跟随 next。
import os

import requests  # pip install requests


def history(path: str, params: dict, max_pages: int = 20) -> list[dict]:
    """Every row of a /history route, following `next` until the range is read."""
    rows: list[dict] = []
    url, query = f"https://api.bytnode.com{path}", params
    for _ in range(max_pages):
        body = requests.get(
            url,
            params=query,
            headers={"X-API-KEY": os.environ["BYTNODE_API_KEY"]},
            timeout=30,
        ).json()
        if body["status"] == "error":
            # history_depth: the range asked goes further back than your plan's window;
            # error.message gives the largest limit that fits. Never swallow it.
            raise RuntimeError(f'{body["error"]["code"]}: {body["error"]["message"]}')
        page = body["data"] or []
        rows += page
        if body.get("next") is None:
            break  # the range is read
        # `next` is the relative URL of the following page, your parameters
        # included: call it as is. It never repeats a row.
        url, query = f"https://api.bytnode.com{body['next']}", None
    return rows


# Without `limit`, each page stops at your plan's history window: no 403.
candles = history("/v1/klines/history", {"symbol": "BTCUSDT", "timeframe": "1h"})

#市场数据族

所有接口都汇集在一页的端点目录中;每个接口的每个字段,连同其单位和真实响应,都在交互式参考中。