Timestamps and Pagination

Time parameters in the CoinGlass API use millisecond timestamps (UTC); most time fields in responses are in milliseconds, while a few endpoints use seconds or nanoseconds, as stated on each endpoint page. Historical endpoints control the returned range with start_time, end_time and limit; when the data you need exceeds the per-request maximum, split the time range into segments and make multiple requests.

Key Facts
ItemDetails
Time formatRequest parameters use 13-digit millisecond timestamps (UTC); most response fields are in milliseconds, a few endpoints use seconds or nanoseconds
Range parametersstart_time, end_time (milliseconds), limit
limitUsually defaults to 1000; the published maximum is at most 1000, and some endpoints have lower limits. See the endpoint page
PaginationSplit the time range into segments of interval × limit and request them one by one
Time zone conversionUse UTC throughout your code and convert to the user's time zone only for display

Time Format

ExampleMeaning
17603136000002025-10-13 00:00:00 UTC, a 13-digit millisecond timestamp
1760313600A 10-digit timestamp in seconds; multiply it by 1000 before passing it

The request parameters start_time and end_time always take milliseconds. Most times in response fields are also in milliseconds, with exceptions: endpoints such as the order book heatmap, footprint, the price candles of the liquidation heatmaps, Coinbase Premium Index, borrow interest rate, Bitfinex margin long/short positions and on-chain transfers return 10-digit timestamps in seconds, and last_updated in Bitcoin ETF Details is a 19-digit nanosecond timestamp. Check the field descriptions on each endpoint page before converting.

⚠️

Mind the time zone

A timestamp itself carries no time zone; when converting it to a date, explicitly use UTC. The same timestamp shows a different date and time in different time zones; for example, 1760313600000 shows as 2025-10-13 08:00 in UTC+8 and as 2025-10-12 19:00 in UTC−5.

Range Parameters

ParameterTypeDescription
start_timeintegerStart time, timestamp in milliseconds, optional
end_timeintegerEnd time, timestamp in milliseconds, optional
limitintegerNumber of records to return; usually defaults to 1000. The published maximum is at most 1000, or 100 for order book heatmaps. See the endpoint page

Whether an endpoint supports these parameters, and its maximum limit, are as stated in the parameter descriptions on each endpoint page.

Most historical endpoints with an interval parameter handle the time range according to the rules below (endpoints such as the financial calendar have their own rules; see the endpoint page):

CaseBehavior
start_time and end_time omittedEnds at the current time and covers interval × limit back from it
Only start_time passedThe end is start_time + interval × limit
end_time is later than the current timeTreated as the current time
start_time is not on an interval boundaryAutomatically moved forward to the start of the next full interval; e.g., for 4h, to 0:00, 4:00, 8:00 UTC
Range boundsThe start time is included and the end time is excluded; any fraction of a second is truncated
Beyond your API plan's rangestart_time is automatically changed to the earliest time your API plan can query; the span of a single query also has a maximum

The time in returned records is the start time of each interval.

Fetching Long History in Segments

When the data you need exceeds the maximum limit of a single request, split the target time range into segments of “interval × limit”, request each segment separately with start_time and end_time, and finally deduplicate and sort by time. This approach does not depend on the order in which the endpoint returns data.

Define the time range
From start to end, millisecond timestamps
Split by interval × limit
No segment exceeds the per-request limit maximum
Request each segment
Pass start_time and end_time for each segment
Deduplicate and sort by time
Get the complete history
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": 60_000, "5m": 300_000, "1h": 3_600_000, "2h": 7_200_000, "4h": 14_400_000, "1d": 86_400_000}

# Set this to your API plan's requests-per-minute limit: Hobbyist 30, Startup 80, Standard 300, Professional 1200; other programs using the same Key also use up the quota, so you can set it lower
REQUESTS_PER_MINUTE = 30

def fetch_range(path, params, interval, start_ms, end_ms, limit=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          # deduplicate by time
        time.sleep(60 / REQUESTS_PER_MINUTE)  # space requests according to the requests-per-minute limit
    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": 60e3, "5m": 300e3, "1h": 3600e3, "2h": 7200e3, "4h": 14400e3, "1d": 86400e3 };

// Set this to your API plan's requests-per-minute limit: Hobbyist 30, Startup 80, Standard 300, Professional 1200; other programs using the same Key also use up the quota, so you can set it lower
const REQUESTS_PER_MINUTE = 30;

async function fetchRange(path, params, interval, startMs, endMs, limit = 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); // deduplicate by time
    await new Promise((r) => setTimeout(r, 60000 / REQUESTS_PER_MINUTE)); // space requests according to the requests-per-minute limit
  }
  return [...rows.keys()].sort((a, b) => a - b).map((t) => rows.get(t));
}

Because the end time is excluded, each segment's end_time is simply the next segment's start_time, so adjacent segments join end to start and no data is missed. The example applies to candle-based endpoints where data is an array of objects and data[].time is in milliseconds; for endpoints that return times in seconds or use a different structure, adjust it according to the endpoint page. The example sets limit to 1000; if the endpoint's maximum is lower than 1000, use that lower maximum. If you still hit 429, wait and retry as described in Error Codes and Rate Limits.

We recommend aligning start_ms to an interval boundary (e.g., for 4h, to 0:00, 4:00, 8:00 UTC); otherwise the endpoint starts returning data from the next interval boundary. Cumulative metrics (such as the cumulative value of CVD) restart accumulating from the first record of each segment, so when downloading in segments, save each interval's raw values and continue the cumulative sum yourself; do not simply concatenate the cumulative results of the segments.

For how to write requests with automatic retries, see Error Codes and Rate Limits. The earliest time you can query is limited by your API plan; see Intervals.

FAQ

What should I do about duplicate data when paginating?

Deduplicate using the time field as a unique key. The boundary between two adjacent segments may fall on the same candle; after deduplicating, just sort by time.

Why is no more data returned before a certain point in time when paging back?

Usually you have reached the history limit allowed by your API plan, or the coin or trading pair was not yet listed before that time. You can compare with the historical data table in API Plans and Permissions.

Millisecond Timestamp Conversion and Time Zones

Convert Millisecond Timestamps to Dates in Python

Use datetime.fromtimestamp(ts / 1000, tz=timezone.utc) to get the UTC time; conversely, use int(dt.timestamp() * 1000) to produce the millisecond timestamp the endpoints require.

Convert Timestamps to Dates in JavaScript

JavaScript's Date itself uses milliseconds: new Date(ts).toISOString() returns the UTC time, and Date.now() returns the current time as a millisecond timestamp, which you can pass directly as end_time.

How Do I Convert Between Local Time and UTC?

UTC = local time − time zone offset. For example, in the UTC+8 time zone, to query data after 00:00 local time on a given day, set start_time to the millisecond timestamp for 16:00 UTC on the previous day. We recommend using UTC throughout your code and converting to the user's time zone only for display.

Related Docs