时间戳与分页

CoinGlass API 的时间参数使用毫秒时间戳(UTC);返回字段中的时间多数是毫秒,少数接口是秒或纳秒,以接口页为准。历史类接口用 start_time、end_time 和 limit 控制返回范围,需要的数据超过单次上限时,按时间分段多次请求。

本页要点
项目内容
时间格式请求参数为 13 位毫秒时间戳(UTC);返回字段多数为毫秒,少数接口为秒或纳秒
范围参数start_time、end_time(毫秒)、limit
limit默认值多为 1000;公开上限不超过 1000,部分接口更小,以接口页为准
分页方法按 interval × limit 把时间区间切成多段,逐段请求
时区换算代码中统一用 UTC 处理,展示时再按用户所在时区换算

时间格式

示例含义
17603136000002025-10-13 00:00:00 UTC,13 位毫秒时间戳
176031360010 位秒级时间戳,需要乘以 1000 后再传入

请求参数 start_time、end_time 一律传毫秒。返回字段中的时间多数也是毫秒,但有例外:订单簿热力图、足迹图、清算热力图的价格 K 线、Coinbase 溢价指数、借贷利率、Bitfinex 杠杆多空仓位和链上转账等接口返回 10 位秒级时间戳,比特币 ETF 详情的 last_updated 是 19 位纳秒时间戳。换算前请先看各接口页的字段说明。

⚠️

注意时区

时间戳本身不带时区,换算成日期时请明确使用 UTC。同一个时间戳在不同时区显示的日期和时刻不同,例如 1760313600000 在 UTC+8 显示为 2025-10-13 08:00,在 UTC−5 显示为 2025-10-12 19:00。

范围参数

参数类型说明
start_timeinteger起始时间,毫秒时间戳,可选
end_timeinteger结束时间,毫秒时间戳,可选
limitinteger返回条数,默认值多为 1000;公开上限不超过 1000,订单簿热力图为 100,以接口页为准

是否支持这些参数、limit 的上限,以各接口页的参数说明为准。

多数带 interval 的历史类接口按以下规则处理时间范围(财经日历等接口另有规则,以接口页为准):

情况处理方式
不传 start_time、end_time以当前时间为终点,向前取 interval × limit 的范围
只传 start_time终点为 start_time + interval × limit
end_time 晚于当前时间按当前时间处理
start_time 不在周期边界上自动推到下一个整周期开始,例如 4h 推到 UTC 0、4、8 点
区间边界包含开始时间,不包含结束时间;不足 1 秒的部分会被截断
超出 API 等级范围start_time 会自动改成你的 API 等级能查到的最早时间,单次查询的跨度也有上限

返回记录中的 time 是每个周期的开始时间。

分段获取长历史

需要的数据超过单次 limit 上限时,把目标时间区间按「interval × limit」切成若干段,每段用 start_time、end_time 单独请求,最后按 time 去重并排序。这种方式不依赖接口返回数据的排列顺序。

确定时间区间
start 至 end,毫秒时间戳
按 interval × limit 切段
每段不超过单次 limit 上限
逐段请求
每段传 start_time、end_time
按 time 去重排序
得到完整的历史数据
import os
import time
import requests

BASE_URL = "https://open-api-v4.coinglass.com"
HEADERS = {"CG-API-KEY": os.environ["COINGLASS_API_KEY"]}
INTERVAL_MS = {'1m': 60000, '3m': 180000, '5m': 300000, '15m': 900000, '30m': 1800000, '1h': 3600000, '2h': 7200000, '4h': 14400000, '6h': 21600000, '8h': 28800000, '12h': 43200000, '1d': 86400000, '1w': 604800000}

# 按你的 API 等级填写每分钟请求上限:业余 30、初创 80、标准 300、专业 1200;同一个 Key 的其他程序也会占用额度,可以再调低
REQUESTS_PER_MINUTE = 30

def fetch_range(path, params, interval, start_ms, end_ms, limit=1000):
    if interval not in INTERVAL_MS:
        raise ValueError(f"不支持的 interval: {interval}")
    if isinstance(limit, bool) or not isinstance(limit, int) or not 1 <= limit <= 1000:
        raise ValueError("limit 必须是 1~1000 的整数")
    step = INTERVAL_MS[interval] * limit
    rows = {}
    for s in range(start_ms, end_ms, step):
        query = dict(params, interval=interval, limit=limit,
                     start_time=s, end_time=min(s + step, end_ms))
        resp = requests.get(BASE_URL + path, headers=HEADERS, params=query, timeout=10)
        body = resp.json()
        if body.get("code") != "0":
            raise RuntimeError(body.get("msg"))
        for r in body["data"]:
            rows[r["time"]] = r          # 以 time 去重
        time.sleep(60 / REQUESTS_PER_MINUTE)  # 按每分钟请求上限控制间隔
    return [rows[t] for t in sorted(rows)]
const BASE_URL = "https://open-api-v4.coinglass.com";
const HEADERS = { "CG-API-KEY": process.env.COINGLASS_API_KEY };
const INTERVAL_MS = {"1m": 60000, "3m": 180000, "5m": 300000, "15m": 900000, "30m": 1800000, "1h": 3600000, "2h": 7200000, "4h": 14400000, "6h": 21600000, "8h": 28800000, "12h": 43200000, "1d": 86400000, "1w": 604800000};

// 按你的 API 等级填写每分钟请求上限:业余 30、初创 80、标准 300、专业 1200;同一个 Key 的其他程序也会占用额度,可以再调低
const REQUESTS_PER_MINUTE = 30;

async function fetchRange(path, params, interval, startMs, endMs, limit = 1000) {
  if (!Object.prototype.hasOwnProperty.call(INTERVAL_MS, interval)) {
    throw new RangeError(`不支持的 interval: ${interval}`);
  }
  if (!Number.isInteger(limit) || limit < 1 || limit > 1000) {
    throw new RangeError("limit 必须是 1~1000 的整数");
  }
  const step = INTERVAL_MS[interval] * limit;
  const rows = new Map();
  for (let s = startMs; s < endMs; s += step) {
    const query = new URLSearchParams({
      ...params, interval, limit,
      start_time: s, end_time: Math.min(s + step, endMs),
    });
    const body = await (await fetch(`${BASE_URL}${path}?${query}`, { headers: HEADERS })).json();
    if (body.code !== "0") throw new Error(body.msg);
    for (const r of body.data) rows.set(r.time, r); // 以 time 去重
    await new Promise((r) => setTimeout(r, 60000 / REQUESTS_PER_MINUTE)); // 按每分钟请求上限控制间隔
  }
  return [...rows.keys()].sort((a, b) => a - b).map((t) => rows.get(t));
}

因为结束时间不包含在内,每段的 end_time 直接取下一段的 start_time,相邻两段首尾相接、不会漏掉数据。示例适用于 data 为对象数组、data[].time 为毫秒的 K 线类接口;返回秒级时间或其他结构的接口,请按接口页调整。示例中的 limit 取 1000;接口上限小于 1000 时,请按该接口的上限调整。仍然触发 429 时,按错误码与频率限制中的方法等待后重试。

start_ms 建议对齐到周期边界(例如 4h 对齐到 UTC 0、4、8 点),否则接口会从下一个周期边界开始返回。累计类指标(如 CVD 的累计值)会以每段的第一条记录为起点重新累加,分段下载时请保存每个周期的原始值,自己续算累计值,不要直接拼接各段的累计结果。

带自动重试的请求写法见错误码与频率限制。可查询的最早时间受 API 等级限制,见时间周期与 interval。

常见问题

分页时出现重复的数据怎么办?

以 time 字段作为唯一键去重。相邻两段的边界可能落在同一根 K 线上,去重后再按 time 排序即可。

翻到某个时间点之前不再返回数据,是什么原因?

通常是已经到达 API 等级允许的历史边界,或该币种、交易对在那之前尚未上线。可以对照API 等级与权限中的历史数据表。

毫秒时间戳转换与时区

Python 毫秒时间戳转日期

使用 datetime.fromtimestamp(ts / 1000, tz=timezone.utc) 得到 UTC 时间;反过来用 int(dt.timestamp() * 1000) 生成接口需要的毫秒时间戳。

JavaScript 时间戳转日期

JavaScript 的 Date 本身使用毫秒:new Date(ts).toISOString() 返回 UTC 时间,Date.now() 返回当前时间的毫秒时间戳,可以直接作为 end_time 传入。

本地时间与 UTC 如何换算?

UTC = 本地时间 − 时区偏移。例如在 UTC+8 时区,要查询本地某天 0 点之后的数据,start_time 应设为前一天 UTC 16:00 对应的毫秒时间戳。建议代码中统一用 UTC 处理,展示时再转换为用户所在时区。

相关文档