How to pull OHLCV candles from a crypto exchange
One library covers 104 venues, the venue's own endpoint is free and unkeyed, and the two disagree about what a one-minute candle is. Which to use, and when.
Use CCXT unless you have a reason not to: one method, fetchOHLCV, over 104 exchanges, MIT licensed, with your keys in your own process. Go direct to the venue when you need one exchange and the fields the unified surface does not carry. Neither gives you comparable candles across venues, and neither lets you redistribute what comes back.
The short way
CCXT and fetchOHLCV. It is an MIT-licensed client library, it normalises 104
exchange APIs to one set of method names, and it is what several of the bots and backtesters in
this catalogue already reach exchanges through — Freqtrade fetches its candles
that way.
import ccxt
exchange = ccxt.binance()
bars = exchange.fetch_ohlcv('BTC/USDT', timeframe='1m', limit=1000)
print(bars[0]) # [timestamp_ms, open, high, low, close, volume]
No key, no account, no vendor in the path: it is a client-side library, so your process talks to
the exchange directly. Swap ccxt.binance() for ccxt.okx() and the rest of the file is
unchanged, which is the whole reason to start here rather than with an HTTP client and one
venue's parameter names.
For a backfill, page backwards with the since parameter and keep the venue's per-call ceiling in
mind: Binance returns at most 1,000 klines per call and defaults to 500. Anything longer than a
few thousand bars is a loop with a sleep in it, not one request.
What the options are
One library, many venues. CCXT, as above. Use it when the venue is a parameter rather than a decision — a screener, a backtest across exchanges, anything where you will want a second venue within a month.
The venue's own endpoint. Binance's market data API is free, unkeyed and deeper than the library exposes: daily BTCUSDT klines from 17 August 2017, and underneath the REST surface a plain HTTP directory of zipped monthly and daily CSV dumps with no account and no rate limit beyond the ordinary. For a multi-year backfill of one venue, downloading the archive beats paging an API by an order of magnitude. Go direct when you need one exchange, or when you need a field the unified surface does not carry.
An aggregate across venues. The CoinGecko API answers a different question — what did this asset do, rather than what did this order book do. Granularity is chosen for you from the range you request, the Demo plan is capped at 365 days of history and the Basic plan at two years, and minute-level granularity is an enterprise plan. Use it when the asset is the subject and the venue is noise.
One symbol scheme over everything. CoinAPI exists for the case where
reconciling BTCUSDT, BTC-USD and XBT/USD across 400+ venues is the actual work. Watch the
meter: a credit is one call except where the limit parameter is used, and then every 100 data
points counts as one, so 10,000 bars in a single call is 100 credits.
The prints themselves. Tardis.dev records exchange websocket feeds and replays the raw messages, so you can build the candle you actually want instead of accepting one. Its own aggregated candles start at ten minutes; below that you are working from ticks, which is the point of buying it.
Where this breaks
The rate limit is not a number of requests. Binance's spot budget is 6,000 weight a minute
counted per IP, not per key, and the weights are not uniform: a klines call costs 2, a trades
call costs 25, an order book costs 5 at depth 100 and 250 at depth 5,000. Six thousand weight is
three thousand candle pulls or twenty-four deep book snapshots. Per-IP is the part that decides
your architecture — a second key does not buy a second budget, and a serverless deployment shares
one with strangers. The families of limit in use across this catalogue, and what each one is
really counting, are in rate limit.
An exchange's own endpoint and a normalising library are not the same product. The library gives you one method and one returned shape; what it cannot give you is a venue's non-standard extras, and it cannot give you history the venue does not serve. CCXT ships no data of its own — "the history you can fetch is the history that venue's API serves and no deeper", with no storage layer and no cross-venue backfill. And the pacing is client-side, computed against constants compiled into each exchange class rather than against anything the venue reports back. When a venue changes a limit, the library finds out when you get banned.
There is also a per-venue trap the unified surface hides. OKX's regular candles endpoint returns
only the latest 1,440 entries — on a one-minute bar, a single day — and everything older comes
from a second endpoint, history-candles, with a different rate limit and 300 results per
request. Kraken's is blunter: up to 720 of the most recent entries, and the documentation says
older data cannot be retrieved regardless of what you pass as since.
A one-minute candle is not a one-minute candle. Two feeds labelled the same way for the same
pair are routinely different series, and the reasons are all publisher's choices: which trades
went into the bucket, where the boundary falls, whether late or corrected prints were folded back
in, whether volume is counted in the base asset or the quote. Binance's klines endpoint takes a
timeZone parameter that shifts the interval boundaries while leaving startTime and endTime
in UTC — so the same request with one parameter changed returns differently-bucketed bars with
identically-formatted timestamps. Both series are correct. OHLCV is the longer
version of why.
The last bar is open. Kraken documents that the final entry is the current, not-yet-committed timeframe and is always present. Most venues do the same and do not mention it. If your loader does not drop the newest bar, your store will hold two different rows for one timestamp and your backtest will read a close that had not happened yet.
Free to fetch is not free to publish. Every public endpoint here has a redistribution clause behind it, and the ones that are easy to read are the strictest: Binance's dataset terms bind you on downloading, caching or querying, and bar hosting or commercially exploiting the data or any derivative feed. Personal, internal research is granted everywhere. What exchange API terms actually let you do is the whole of it, and redistribution is the word to know before you ask a vendor for a quote.
If you outgrow this
Three exits, and they are different products rather than bigger plans.
If the problem is depth of history, the flat-file archives come first — free, and enough for most backtests on a major venue — and Kaiko is the paid end, with tick trades and OHLCV back to 2010 at granularities from one second to one day, on a price quoted per instrument count and never published.
If the problem is comparability across venues, no amount of candle-pulling fixes it, because the disagreement is in the definitions rather than in the data. That is a reference-rate purchase: Coin Metrics for the institutional version, where the Bitcoin price comes from for why one number is not simply available.
If the problem is that a candle is the wrong object — you are testing anything that touches the book, the spread or the queue — stop aggregating and buy the prints. Tardis.dev records the raw websocket messages; what a crypto backtest assumes is the argument for when that matters.
The tools named above
In the order this page puts them in, which is an editorial judgement and not a ranking anyone paid for.
CCXT
One method name over 104 exchanges, so the backfill you write for one venue runs against the rest without a second adapter.
One MIT client for 104 crypto exchanges, 76 of them over websocket.
FreeFree tierOpen source
Binance Market Data API
The venue's own endpoint, free and unkeyed, with a flat-file archive underneath it for anything longer than a few thousand bars.
One venue's own tape — prices, books, trades and funding, free and unkeyed.
FreeFree tier
CoinGecko API
Cross-venue aggregate when you want the asset rather than one exchange's view of it, with granularity chosen for you from the range.
Prices, market data and onchain DEX data for 18,000+ coins, via REST, websocket or MCP.
$35/moFree tier
CoinAPI Market Data API
One symbol scheme over 400+ venues when reconciling exchange-specific tickers is the actual job, billed per 100 data points.
One symbol scheme over 400+ venues, metered in REST credits and WebSocket gigabytes.
$79/mo
Tardis.dev
Raw recorded websocket messages when the candle itself is the problem and you need to build your own from the prints.
Tick-by-tick order books, trades and options chains from 64 exchange feeds since 2019.
$350/moFree tier
FAQ
Do I need an API key to pull candles?
On most venues, no. Binance, OKX and Bybit all answer candle requests from an unauthenticated client, and a key buys you nothing on the public endpoints except, on some venues, a separate rate-limit bucket. What a key does change is who is bound by which document — the key agreement governs the credential, and the market-data terms govern the numbers whether you hold a key or not.
Is CCXT slower than calling the exchange directly?
Marginally, and the part that matters is not speed. CCXT paces requests client-side against constants compiled into each exchange class rather than against anything the venue reports, so a venue that quietly tightens a limit will ban you before the library knows. On a backfill that runs for hours, that is the failure to plan for, not the microseconds of normalisation.
Can I publish the candles I pulled?
Almost certainly not without asking. Binance's dataset terms license the flat-file archive under CC BY-NC-SA 4.0 and bar sub-licensing, hosting or commercially exploiting the datasets "or any derivative real-time or historical data feeds" — and they bind you on downloading, caching or querying. OKX and Coinbase confine market data to personal or internal use and require written consent before redistribution. Personal backtesting is fine everywhere; showing the numbers to somebody who is not you is a separate question.
Why does my last candle keep changing?
Because it has not closed yet. Kraken says so in the documentation — "the last entry in the OHLC array is for the current, not-yet-committed timeframe, and will always be present, regardless of the value of since" — and most venues behave the same way without saying it. Store it and re-poll and you will have two different rows for one timestamp. Drop the newest bar on every pull unless you can prove the interval has ended.
Sources
- Kline/Candlestick Data, Spot REST API market data endpoints — Binance, read
- Get OHLC Data, Spot REST API — Kraken, read
The catalogue next door
This page names a handful of products. The rest of them are in Crypto Market Data APIs, each filled in against the same schema, with the fields to narrow it yourself.
Last updated . Corrected in place — an endpoint that moves is a bug on this page, not a new post.