CoinGlass API 的时间参数使用毫秒时间戳(UTC);返回字段中的时间多数是毫秒,少数接口是秒或纳秒,以接口页为准。历史类接口用 start_time、end_time 和 limit 控制返回范围,需要的数据超过单次上限时,按时间分段多次请求。
| 项目 | 内容 |
|---|---|
| 时间格式 | 请求参数为 13 位毫秒时间戳(UTC);返回字段多数为毫秒,少数接口为秒或纳秒 |
| 范围参数 | start_time、end_time(毫秒)、limit |
| limit | 默认值多为 1000;公开上限不超过 1000,部分接口更小,以接口页为准 |
| 分页方法 | 按 interval × limit 把时间区间切成多段,逐段请求 |
| 时区换算 | 代码中统一用 UTC 处理,展示时再按用户所在时区换算 |
时间格式
| 示例 | 含义 |
|---|---|
1760313600000 | 2025-10-13 00:00:00 UTC,13 位毫秒时间戳 |
1760313600 | 10 位秒级时间戳,需要乘以 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_time | integer | 起始时间,毫秒时间戳,可选 |
end_time | integer | 结束时间,毫秒时间戳,可选 |
limit | integer | 返回条数,默认值多为 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 去重并排序。这种方式不依赖接口返回数据的排列顺序。
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 处理,展示时再转换为用户所在时区。
相关文档
- 时间周期与 interval:K 线周期与历史长度
- 错误码与频率限制:批量请求与限流
- 聚合持仓历史(K 线):比特币持仓历史 API
- WebSocket 基础介绍:实时爆仓数据推送
