# Crypto historical data download: sources, caps, licenses

URL: https://fincodelabs.com/en/blog/crypto-historical-data-download · Publisher: Fincode (Fincode Yazılım ve Teknoloji A.Ş.), Istanbul, Turkey · Category: Exchange APIs · Published: 2026-11-06 · Updated: 2026-11-06

Crypto historical data download: exchange candle caps and bulk files tested 6 Nov 2026, the license each source states, and Python that writes a checked CSV.

**Short answer:** **Crypto historical data** comes from three places: exchange candle endpoints paged 300 to 1,000 rows at a time, bulk files some exchanges publish, and paid vendors under contract. Downloading is the easy part. As read on 6 November 2026, Binance's bulk files are licensed for non-commercial use only, and OKX's API agreement limits its market data, public endpoints included, to personal non-commercial use, so check the license before the data reaches a product.

Figures come from each exchange's documentation and keyless calls to public endpoints, read and run on 6 November 2026, Istanbul time (5 November in UTC); timestamps are UTC. Exchanges are named because their documentation was read, not as a recommendation, and nothing here is a view on any asset or strategy. Rules depend on your jurisdiction and each platform's terms. For readers in Turkey: Article 99/A of Capital Markets Law No. 6362 treats activity by foreign crypto-asset platforms directed at Turkish residents as unauthorized.

## Free crypto market data API limits, tested

Each exchange caps rows per call differently, and in tests on 6 November 2026 only Coinbase rejected an oversized request; the others quietly returned less than asked.

**Candle endpoints, documented and tested 6 November 2026 (1-minute bars; Binance on data-api.binance.vision; 525,600 bars a year)**

| Endpoint | Rows per call (docs) | Order | Asking for more (tested) | Calls for a year |
| --- | --- | --- | --- | --- |
| Binance Spot `GET /api/v3/klines` | 1,000 (default 500) | Oldest first | `limit=1001` returned 1,000, HTTP 200 | 526 |
| Bybit `GET /v5/market/kline` | 1,000 (default 200) | Newest first | `limit=1001` returned 1,000, retCode 0 | 526 |
| OKX `GET /api/v5/market/history-candles` | 300 (default 100) | Newest first | `limit=301` returned 300, code 0 | 1,752 |
| Coinbase `GET /api/v3/brokerage/market/products/{id}/candles` | 350 | Newest first | A 351-minute window: HTTP 400 `INVALID_ARGUMENT` | 1,502 |
| Kraken `GET /0/public/OHLC` | 720 most recent, in total | Oldest first | `since` set in 2020 returned only the latest 721 rows | Not possible |

- **Silent clamping skips data.** Advance from the last timestamp received, not the one requested.
- **The unfinished candle.** All five returned the current minute: Binance and Kraken as the last row (Kraken's documentation says it is always present); Bybit, Coinbase and OKX (with `confirm` 0) as the first. Drop it before saving.
- **Depth is mostly undocumented.** Binance's earliest BTCUSDT daily candle opened on 17 August 2017; OKX says its history covers recent years, with 1-second bars for 3 months; the Bybit and Coinbase pages give no start date.
- **Budgets differ.** Binance documents weight 2 per klines call and OKX 20 history requests per 2 seconds per IP; Coinbase's candles page states no rate limit. See the [Binance API rate limit](https://fincodelabs.com/en/blog/binance-api-rate-limit) guide.

## Download crypto historical data as CSV from bulk files

Binance, OKX and Kraken publish candle files you can fetch without paging, and each has a quirk that changes how you read them.

**Official bulk files, read 6 November 2026**

| Source | Contents | Publication | Quirk |
| --- | --- | --- | --- |
| Binance, data.binance.vision | Spot klines (1s to 1mo), futures klines, trades, aggregate trades | Daily files the next day, monthly on the first Monday | Spot timestamps in microseconds from 1 January 2025; a CHECKSUM per zip; archived files can be replaced, with an update list |
| OKX, `GET /api/v5/public/market-data-history` | File links for trades, 1-minute candles, funding, order books, borrowing rates | Mostly T+2, order books T+3; backfill in progress | Days in UTC+8 except order books (UTC+0); 5 requests per 2 seconds |
| Kraken, OHLCVT downloads | OHLCVT at 1 to 1,440 minutes, no header row | Quarterly; complete through 30 September 2026 (page updated 2 November 2026) | Only intervals with trades are included |
| Bybit, public.bybit.com | Folders kline_for_metatrader4, premium_index, spot_index, trading, spot | Not stated | A bare listing, with no description or license statement |

The OKX day boundary is easy to miss: its BTC-USDT 1-minute file named 2026-10-01, downloaded on 6 November 2026, held 1,440 rows from 30 September 16:00 to 1 October 15:59 UTC, so one UTC day needs two files. The documentation also names one field two ways, `dataTs` in its table and `dateTs` in its example; the live response used `dateTs`. Binance's microsecond change is covered in the [Binance historical data API](https://fincodelabs.com/en/blog/binance-historical-data-api) guide, and trades and order books in [crypto tick data](https://fincodelabs.com/en/blog/crypto-tick-data).

## Crypto data licenses: what each official page says

Free to download is not free to use: the two exchanges whose data terms could be read here both exclude commercial use without a separate written agreement.

**Data terms on each official page, read 6 November 2026**

| Source | Document | What it says |
| --- | --- | --- |
| Binance Vision files | Vision Dataset Terms 1.0, updated 26 August 2026 | Covers data.binance.vision and undefined "associated endpoints". CC BY-NC-SA 4.0. Permitted uses are limited to academic research, non-monetized open education, personal non-production backtesting and non-commercial open-source evaluation; no live proprietary trading, commercial bot platforms or paid newsletters; commercial use needs a written enterprise license. |
| OKX endpoints and files | API Agreement section 9.4, updated 28 July 2026 | Personal, non-commercial trading and account management only; no redistribution, publication or display to third parties without written consent; no competing data product or training models to sell or share. It applies equally to public endpoints and bars scraping beyond personal need. |
| Kraken files | OHLCVT support article | No license or use terms on the page. |
| Coinbase candles | API reference page | No license terms on the page; Coinbase's developer terms page could not be opened, so it is not summarized. |
| Bybit files | Public listing | No license statement; Bybit's general terms were not reviewed. |

Binance's terms say an amendment applies only to datasets made available after it takes effect, so record the download date and terms version with each file; the script below writes the source and download time to a provenance file. The repository README ends with an MIT license line but points to the terms document for how the dataset may be used. A page with no license statement is not permission: the platform's general terms still apply. This is general information, not legal advice.

## A missing candle is not always missing data

Exchanges disagree on what a minute with no trades looks like, so one quiet market appears as a gap on one venue and as zero-volume rows on another.

> **Tested on 6 November 2026, 1-minute candles for the UTC day of 4 November 2026.** Coinbase returned all 1,440 candles for BTC-USD but only 360 for ADA-EUR, leaving 1,080 minutes without a row and no zero-volume candles. Binance returned 1,440 candles for ADAEUR, of which 1,156 had zero volume and zero trades.

- Kraken documents this convention for its files: only intervals with trades are included and gaps are not zero-filled. The Coinbase candles page says nothing either way.
- Count rows against the expected grid; on a venue that zero-fills, a real gap more likely means an outage or a missing file. Never forward-fill silently: fill in a logged step with a flag column, or a [backtest](https://fincodelabs.com/en/blog/backtesting-trading-strategy-pitfalls) may simulate fills at prices nobody traded.
- Live capture differs from backfill. Tardis.dev's data FAQ, read 6 November 2026, says its raw data comes mainly from real-time WebSocket feeds, that outage gaps are permanent, and that a REST endpoint "may return a cleaner history after the fact".

## Tested Python: page candles, check gaps and duplicates, write CSV

The script pages Coinbase's public candle endpoint without a key, rejects impossible rows, refuses conflicting duplicates, counts minutes without a candle and writes a CSV plus a provenance file.

It ran on 6 November 2026 with Python 3.11 and requests 2.31, for the UTC day of 4 November 2026.

```python
import csv, json, math, time
from decimal import Decimal, InvalidOperation
import requests

URL = "https://api.coinbase.com/api/v3/brokerage/market/products/{}/candles"
STEP = {"ONE_MINUTE": 60, "ONE_HOUR": 3600, "ONE_DAY": 86400}
CAP = 350                      # buckets per call; both window ends count
FIELDS = ("open", "high", "low", "close", "volume")

class DataError(Exception):
    pass

def fetch(product, gran, start, end, http):
    r = http.get(URL.format(product), timeout=10,
                 params={"start": start, "end": end, "granularity": gran})
    try:
        body = r.json()
    except ValueError:
        body = None
    rows = body.get("candles") if isinstance(body, dict) else None
    if r.status_code == 200 and isinstance(rows, list):
        return rows
    msg = body.get("message") or body.get("error") if isinstance(body, dict) else None
    raise DataError(f"HTTP {r.status_code}: {msg or r.text[:120]}")

def clean(raw):
    t = str(raw.get("start", "") if isinstance(raw, dict) else "").strip()
    if not t.isdecimal():      # an empty ID is an error, not a duplicate
        raise DataError(f"no start time: {raw}")
    try:
        o, h, l, c, v = (Decimal(raw[k]) for k in FIELDS)
        f = [float(x) for x in (o, h, l, c, v)]
    except (KeyError, TypeError, ValueError, InvalidOperation) as e:
        raise DataError(f"bad number at {t}: {repr(e)}")
    if (not all(map(math.isfinite, f)) or min(f[:4]) <= 0 or f[4] < 0
            or (v and not f[4]) or h < max(o, l, c) or l > min(o, h, c)):
        raise DataError(f"impossible candle at {t}: {raw}")
    return int(t), [raw[k] for k in FIELDS]

def download(product, gran, start, end, path, http=None, now=None):
    http, step = http or requests.Session(), STEP[gran]
    now = time.time() if now is None else now
    start -= start % step
    last = (end - 1) // step * step
    rows, dupes, t = {}, 0, start
    while t <= last:
        stop = min(t + step * (CAP - 1), last)
        for raw in fetch(product, gran, t, stop, http):
            ts, vals = clean(raw)
            if ts + step > now or not start <= ts <= last:
                continue
            if ts not in rows:
                rows[ts] = vals
            elif rows[ts] == vals:
                dupes += 1
            else:
                raise DataError(f"conflicting candles at {ts}")
        t = stop + step
        time.sleep(0.2)
    missing = [x for x in range(start, last + 1, step)
               if x + step <= now and x not in rows]
    with open(path, "w", newline="") as fh:
        w = csv.writer(fh)
        w.writerow(("start",) + FIELDS)
        w.writerows([k] + rows[k] for k in sorted(rows))
    meta = {"source": URL.format(product), "granularity": gran, "start": start,
            "end": end, "rows": len(rows), "duplicates": dupes,
            "missing": len(missing), "downloaded_at": int(time.time())}
    with open(path + ".json", "w") as fh:
        json.dump(meta, fh)
    return meta

if __name__ == "__main__":
    day = 1793750400                   # 4 November 2026 00:00 UTC
    for product in ("BTC-USD", "ADA-EUR"):
        m = download(product, "ONE_MINUTE", day, day + 86400, product + ".csv")
        print(product, m["rows"], "rows,", m["duplicates"], "duplicates dropped,",
              m["missing"], "minutes without a candle")
```

Output:

```
BTC-USD 1440 rows, 0 duplicates dropped, 0 minutes without a candle
ADA-EUR 360 rows, 0 duplicates dropped, 1080 minutes without a candle
```

- **Both window ends are inclusive.** A 10-minute window returned 11 candles, and a window of exactly 350 intervals returned the newest 350, dropping the oldest without an error. A first version stepping 350 intervals reported 1,439 BTC-USD rows: midnight lost, 23:20 counted twice. Hence 349-interval windows, each starting one interval after the last.
- **Failure paths, tested with synthetic responses:** error bodies without the expected fields, HTML and empty bodies, a null candle list, empty and null start times, "1e400" and "1e-400" in any field, "NaN", "sNaN", impossible highs and lows, identical and conflicting duplicates, an unfinished candle and a connection error. Each was rejected or handled as intended; the connection error left no CSV.
- **The float check.** `float("1e400")` is `inf` and `float("1e-400")` is `0.0` in any float64 reader, so the script rejects such values and keeps the exchange's original strings.
- **Adapting it.** Rows keyed by start time make return order irrelevant. For OKX, page backward with `after` and drop `confirm` 0 rows. On HTTP 429, retry later with [exponential backoff with jitter](https://fincodelabs.com/en/blog/exponential-backoff-jitter-python).

## Paid crypto data vendors: questions before you buy

A vendor sells under its own contract, so ask about license scope and how the history was built.

- May you display, redistribute or train models on the data, and keep it after the contract ends?
- Was it captured live or backfilled, and how are outages marked?
- Are days in UTC, are delisted pairs included, and how are corrections announced?

Vendor prices read on 4 November 2026 are in the [crypto price API](https://fincodelabs.com/en/blog/crypto-price-api) guide, and storage in [ClickHouse for market data](https://fincodelabs.com/en/blog/clickhouse-market-data). For integration work in general, see Fincode's [market data pipelines](https://fincodelabs.com/en/services/market-data-pipelines) page; naming Binance, Bybit, Coinbase, Kraken, OKX or Tardis.dev here does not mean Fincode integrates them or has built on them.

## Frequently asked questions

### Where can I download crypto historical data for free?

As read on 6 November 2026, Binance, OKX and Kraken publish candle files, and all five exchanges tested here serve candles through public endpoints without a key. Free to download is not free to use, so read each source's terms first.

### Can I use free exchange data in a commercial product?

Not under the Binance Vision Dataset Terms or the OKX API Agreement as read on 6 November 2026; both require a separate written agreement. Check other sources' terms too.

### Why does Kraken's OHLC endpoint ignore my since parameter?

Kraken's documentation says it returns up to 720 of the most recent entries and older data cannot be retrieved regardless of since. Use Kraken's quarterly OHLCVT files for older history.

### Why does my downloaded CSV have missing minutes?

Kraken's files omit intervals without trades, and Coinbase returned no row for 1,080 ADA-EUR minutes on 4 November 2026, while Binance returned zero-volume candles. Check the venue's convention before calling it lost data.

## Sources

- [Binance Vision Dataset Terms, version 1.0, last updated 26 August 2026](https://github.com/binance/binance-public-data/blob/master/TERMS_AND_CONDITIONS.md)
- [Binance Public Data: README (file layout, intervals, CHECKSUM, updates)](https://github.com/binance/binance-public-data/blob/master/README.md)
- [Binance Spot API documentation: REST API, Kline/Candlestick data](https://github.com/binance/binance-spot-api-docs/blob/master/rest-api.md)
- [Bybit V5 API: Get Kline](https://bybit-exchange.github.io/docs/v5/market/kline)
- [Bybit public data listing](https://public.bybit.com/)
- [OKX API v5 documentation: candlesticks history and historical market data](https://www.okx.com/docs-v5/en/)
- [OKX API Agreement, section 9.4, last updated 28 July 2026](https://www.okx.com/help/okx-api-agreement)
- [Coinbase Advanced Trade API: Get public product candles](https://docs.cdp.coinbase.com/api-reference/advanced-trade-api/rest-api/public/get-public-product-candles)
- [Kraken API: Get OHLC data](https://docs.kraken.com/api-reference/market-data/get-ohlc-data)
- [Kraken Support: Downloadable historical OHLCVT data, last updated 2 November 2026](https://support.kraken.com/hc/en-us/articles/360047124832-Downloadable-historical-OHLCVT-Open-High-Low-Close-Volume-Trades-data)
- [Tardis.dev: Data FAQ](https://docs.tardis.dev/faq/data)
- [Capital Markets Law No. 6362, consolidated text, Article 99/A (Turkish)](https://www.mevzuat.gov.tr/mevzuatmetin/1.5.6362.pdf)

> This guide is general information about software and markets, not investment, legal or tax advice. Automated trading can lose money, past or backtested performance does not guarantee future results, and the rules that apply depend on your jurisdiction and your exchange's terms. Sources, checks and corrections: see the [editorial policy](https://fincodelabs.com/en/editorial-policy).

---

Published by Fincode (Fincode Yazılım ve Teknoloji A.Ş.), a software company in Istanbul, Turkey: https://fincodelabs.com/en/blog/crypto-historical-data-download

Cite as: Fincode, "Crypto historical data download: sources, caps, licenses", updated November 6, 2026, https://fincodelabs.com/en/blog/crypto-historical-data-download
