Cryptocurrency traders, data analysts, and algorithmic developers often require access to high-resolution historical market data to backtest strategies, analyze price movements, or build predictive models. The Binance API provides a powerful and free way to retrieve this data programmatically. In this guide, you’ll learn how to download daily historical candlestick data from Binance using Python, even when the API limits each request to 1,000 records.
We’ll walk through the technical implementation step by step, explain key constraints, and provide a clean, reusable code example that saves data in both JSON and CSV formats.
Understanding Binance Kline/Candlestick Data
The Binance REST API offers access to kline (candlestick) data via the endpoint:
/api/v1/klinesThis endpoint returns open, high, low, close (OHLC) prices along with volume and other metadata for specified time intervals.
Key Parameters
symbol: Trading pair (e.g.,BTCUSDT,ETHUSDT)interval: Time frame (1m,5m,1h, etc.)limit: Maximum number of data points per call (max: 1000)startTimeandendTime: Unix timestamps in milliseconds
👉 Get started with real-time and historical crypto data using a reliable platform.
Why 1,000 Records Isn’t Enough for Daily Data
A single day contains 1,440 minutes. Since the smallest interval is 1 minute, retrieving full daily data would require 1,440 records. However, Binance limits each API call to 1,000 data points — meaning one request isn’t sufficient.
To overcome this limitation, we split the day into two 12-hour chunks:
- First half: 00:00 to 11:59
- Second half: 12:00 to 23:59
Each 12-hour period generates 720 records (720 = 60 minutes × 12 hours), well within the 1,000-record limit.
We make two separate API calls and merge the results.
Complete Python Script to Download Daily Data
Below is a fully functional script that downloads 1-minute candlestick data for any given date and symbol.
import datetime
import json
import requests
from urllib.parse import urljoin
class BinanceException(Exception):
def __init__(self, status_code, data=None):
self.status_code = status_code
if data:
self.code = data.get('code')
self.msg = data.get('msg')
message = f"{status_code} [{self.code}] {self.msg}"
else:
self.code = None
self.msg = None
message = f"status_code={status_code}"
super().__init__(message)
class BinanceClient:
BASE_URL = "https://api.binance.com"
def __init__(self, api_key=None):
self.API_KEY = api_key
def download_12h_candles(self, symbol: str, start_time: datetime.datetime, end_time: datetime.datetime):
PATH = '/api/v1/klines'
params = {
'symbol': symbol.upper(),
'interval': '1m',
'limit': 1000,
'startTime': int(start_time.timestamp() * 1000),
'endTime': int(end_time.timestamp() * 1000)
}
headers = {'X-MBX-APIKEY': self.API_KEY} if self.API_KEY else {}
url = urljoin(self.BASE_URL, PATH)
response = requests.get(url, headers=headers, params=params)
if response.status_code == 200:
return response.json()
else:
if response.headers.get('Content-Type') == 'application/json':
raise BinanceException(status_code=response.status_code, data=response.json())
else:
raise BinanceException(status_code=response.status_code)
def download_historical_data(symbol: str, date_str: str):
"""
Downloads 1-minute kline data for a full day by combining two 12-hour requests.
Saves output as both .json and .csv (open prices only).
"""
# Parse input date
start_date = datetime.datetime.strptime(date_str, '%Y-%m-%d')
# First 12 hours: 00:00 - 11:59:59
time1_start = start_date
time1_end = start_date + datetime.timedelta(hours=12) - datetime.timedelta(seconds=1)
# Second 12 hours: 12:00 - 23:59:59
time2_start = start_date + datetime.timedelta(hours=12)
time2_end = start_date + datetime.timedelta(hours=24) - datetime.timedelta(seconds=1)
# Initialize client (API key optional for public endpoints)
client = BinanceClient()
# Fetch both halves
data = []
data.extend(client.download_12h_candles(symbol, time1_start, time1_end))
data.extend(client.download_12h_candles(symbol, time2_start, time2_end))
# Sort by timestamp just in case
data.sort(key=lambda x: x[0])
# Generate filename
filename = f"{symbol}_{date_str}"
# Save as JSON
with open(f"{filename}.json", 'w') as f:
json.dump(data, f, indent=2)
# Save open prices as CSV
with open(f"{filename}.csv", 'w') as f:
for row in data:
f.write(f"{row[1]}\n") # Open price only
print(f"✅ Data saved: {filename}.json and {filename}.csv")How to Use It
download_historical_data('BTCUSDT', '2025-04-05')This will create two files:
BTCUSDT_2025-04-05.json– Full candlestick dataBTCUSDT_2025-04-05.csv– List of open prices (one per line)
FAQ: Frequently Asked Questions
Q: Do I need an API key to download historical data?
No. Public endpoints like /api/v1/klines do not require authentication. You can omit the API key unless you're accessing private account data.
Q: Can I download more than one day of data?
Yes. You can loop over multiple dates. Just ensure you respect rate limits (e.g., no more than 1,200 requests per minute).
Q: What does each value in the kline array represent?
The response structure is:
[
[
1499040000000, // Open time
"0.01634790", // Open price
"0.80000000", // High price
"0.01575800", // Low price
"0.01577100", // Close price
"148976.11427815", // Volume
1499644799999, // Close time
"2434.19055334", // Quote asset volume
308, // Number of trades
"1756.87402397", // Taker buy base asset volume
"28.46694368", // Taker buy quote asset volume
"17928899.62484339" // Ignore field
]
]Q: Why not use intervals larger than 1 minute?
Larger intervals (like 5m or 1h) return fewer data points per day, so you won’t hit the 1,000-record limit. This workaround is only needed for 1-minute granularity.
Q: Can I store this data in a database instead?
Absolutely. Instead of writing to files, you can insert rows into SQLite, PostgreSQL, or cloud databases like MongoDB or BigQuery for long-term analysis.
Core Keywords for SEO
To improve discoverability and align with search intent, here are the core keywords naturally integrated throughout this article:
- Binance API
- historical cryptocurrency data
- download candlestick data
- Python crypto trading
- Binance kline data
- fetch OHLC data
- crypto backtesting data
- minute-level market data
These terms reflect common queries from developers and quants looking to work with granular Binance datasets.
Enhance Your Crypto Data Workflow
Whether you're building a trading bot, conducting technical analysis, or training machine learning models, having reliable access to clean historical data is essential.
While Binance provides robust public APIs, managing pagination, time zones, error handling, and file output can become complex at scale.
👉 Explore advanced tools for fetching and processing crypto market data efficiently.
Final Thoughts
Downloading full-day 1-minute candlesticks from Binance requires splitting requests due to API limitations — but with a simple two-call strategy, it’s entirely feasible. The provided Python code is production-ready, modular, and easy to extend for batch processing or integration into larger systems.
By mastering these techniques, you gain direct control over your dataset without relying on third-party providers — saving costs and increasing transparency.
Remember to handle rate limits responsibly and consider caching responses locally to avoid redundant calls.
With accurate historical data in hand, your next breakthrough in algorithmic trading or market research is just a script away.
👉 Start analyzing real-time crypto trends today — no signup required.