KhueApps
Home/Python/Free Crypto Daily Price and Volume APIs in Python

Free Crypto Daily Price and Volume APIs in Python

Last updated: October 03, 2025

Overview

This guide shows practical, free ways to fetch daily crypto price and volume data in Python for Algorithmic Trading with Python. You’ll get minimal code, trade-offs, rate-limit notes, and pitfalls to avoid.

Quickstart

  1. Choose a source
  • CoinGecko: simple, no key, daily prices and total volume (not strict exchange OHLCV volume).
  • Binance: exchange-grade OHLCV for pairs like BTCUSDT, no key, strict rate weights.
  • CryptoCompare: unified OHLCV across many pairs, free key required.
  • Alpha Vantage: free key, slower limits, daily OHLCV in USD.
  • yfinance: convenient wrapper for daily bars like BTC-USD; unofficial.
  1. Install packages
pip install requests pandas yfinance
  1. Run the minimal working example (CoinGecko)
  • Great for quick daily close + total volume in fiat.

Minimal working example: CoinGecko daily close + volume

import requests
import pandas as pd

def coingecko_daily(coin_id="bitcoin", vs="usd", days=180):
    url = f"https://api.coingecko.com/api/v3/coins/{coin_id}/market_chart"
    params = {"vs_currency": vs, "days": days, "interval": "daily"}
    r = requests.get(url, params=params, timeout=30)
    r.raise_for_status()
    data = r.json()

    prices = pd.DataFrame(data["prices"], columns=["ts", "close"])  # close-like spot
    volumes = pd.DataFrame(data["total_volumes"], columns=["ts", "volume"])

    df = prices.merge(volumes, on="ts")
    df["date"] = pd.to_datetime(df["ts"], unit="ms", utc=True).dt.tz_localize(None).dt.date
    df = df[["date", "close", "volume"]].sort_values("date").reset_index(drop=True)
    return df

if __name__ == "__main__":
    df = coingecko_daily("bitcoin", "usd", days=365)
    print(df.tail())
    # Example: simple daily returns
    df["ret"] = df["close"].pct_change()
    print(df[["date", "close", "volume", "ret"]].tail())

Notes

  • volume is total traded volume in the quote currency (here USD-equivalent), aggregated across many venues.
  • For strict OHLCV per exchange/pair, use Binance (below).

More free sources (with short Python snippets)

1) Binance (exchange OHLCV for pairs; no key)

import requests, pandas as pd

def binance_klines(symbol="BTCUSDT", limit=1000):
    url = "https://api.binance.com/api/v3/klines"
    params = {"symbol": symbol, "interval": "1d", "limit": limit}
    r = requests.get(url, params=params, timeout=30)
    r.raise_for_status()
    raw = r.json()
    cols = [
        "open_time","open","high","low","close","volume",
        "close_time","qav","trades","tbbav","tbqav","ignore"
    ]
    df = pd.DataFrame(raw, columns=cols)
    for c in ["open","high","low","close","volume"]:
        df[c] = df[c].astype(float)
    df["date"] = pd.to_datetime(df["open_time"], unit="ms").dt.date
    return df[["date","open","high","low","close","volume"]]

# Example
# df = binance_klines("ETHUSDT", limit=1000)

Notes

  • volume is base-asset volume (e.g., BTC for BTCUSDT).
  • Symbol format is BASEQUOTE (e.g., BTCUSDT, ETHUSDT).

2) CryptoCompare (free key; unified OHLCV)

import os, requests, pandas as pd

def cryptocompare_histoday(fsym="BTC", tsym="USD", limit=2000, api_key=None):
    url = "https://min-api.cryptocompare.com/data/v2/histoday"
    params = {"fsym": fsym, "tsym": tsym, "limit": limit}
    headers = {"authorization": f"Apikey {api_key}"} if api_key else {}
    r = requests.get(url, params=params, headers=headers, timeout=30)
    r.raise_for_status()
    j = r.json()
    if j.get("Response") != "Success":
        raise RuntimeError(j.get("Message", "API error"))
    d = j["Data"]["Data"]
    df = pd.DataFrame(d)
    df["date"] = pd.to_datetime(df["time"], unit="s").dt.date
    df = df.rename(columns={"volumefrom": "volume_base", "volumeto": "volume_quote"})
    return df[["date","open","high","low","close","volume_base","volume_quote"]]

# Example
# api_key = os.getenv("CRYPTOCOMPARE_KEY")
# df = cryptocompare_histoday("BTC", "USD", 2000, api_key)

Notes

  • volume_base is in fsym (BTC); volume_quote is in tsym (USD).
  • Requires free API key via header authorization.

3) Alpha Vantage (free key; daily OHLCV in USD)

import requests, pandas as pd

def alphavantage_daily(symbol="BTC", market="USD", api_key="demo"):
    url = "https://www.alphavantage.co/query"
    params = {
        "function": "DIGITAL_CURRENCY_DAILY",
        "symbol": symbol,
        "market": market,
        "apikey": api_key,
    }
    r = requests.get(url, params=params, timeout=30)
    r.raise_for_status()
    j = r.json()
    ts = j.get("Time Series (Digital Currency Daily)")
    if not ts:
        raise RuntimeError(j.get("Note") or j.get("Error Message") or "No data")
    df = pd.DataFrame.from_dict(ts, orient="index")
    df.index = pd.to_datetime(df.index)
    cols = {
        "1a. open (USD)": "open",
        "2a. high (USD)": "high",
        "3a. low (USD)": "low",
        "4a. close (USD)": "close",
        "5. volume": "volume",
    }
    out = df[list(cols.keys())].rename(columns=cols).astype(float)
    out["date"] = out.index.date
    return out.reset_index(drop=True)[["date","open","high","low","close","volume"]]

# Example
# df = alphavantage_daily("ETH", "USD", api_key="YOUR_KEY")

Notes

  • Free tier is rate-limited; batch smartly.

4) yfinance (convenient daily bars; unofficial)

import pandas as pd
import yfinance as yf

def yf_daily(ticker="BTC-USD", period="max"):
    df = yf.download(ticker, interval="1d", period=period, auto_adjust=False)
    df = df.rename(columns=str.lower).reset_index()
    df["date"] = pd.to_datetime(df["date"]).dt.date
    return df[["date","open","high","low","close","volume"]]

# Example
# df = yf_daily("BTC-USD", period="5y")

Notes

  • Subject to upstream changes; validate against a reference.

Quick comparison

SourceKey?Daily OHLCDaily VolumeHistorical depthNotes
CoinGeckoNoClose onlyTotal volume (quote)Up to maxSimple, aggregated across venues
BinanceNoYesBase-asset volumeDeep per pairExchange-specific pairs
CryptoCompareYesYesBase + quoteLong (limit=2000)Unified across many markets
Alpha VantageYesYesBase volumeLongTight rate limits
yfinanceNoYesQuote volumeLongUnofficial, may change

Common pitfalls

  • Symbol mismatches
    • BTCUSDT (Binance) vs BTC-USD (yfinance) vs BTC/USD (generic). Map explicitly per source.
  • Volume definitions vary
    • Exchange OHLCV often uses base-asset volume; aggregate feeds may present quote volume. Do not mix without conversion.
  • Timezones and daily cutoffs
    • Daily bars are typically UTC-based. Align to UTC when merging across sources.
  • Missing/partial last bar
    • The current day’s bar may be incomplete; filter out today if you need closed bars only.
  • Rate limits
    • Expect HTTP 429. Add retries with exponential backoff and caching.

Performance notes

  • Batch and cache
    • Cache responses on disk (e.g., parquet) keyed by source/symbol/date to avoid re-downloading.
  • Use vectorized pandas ops
    • Convert types once and keep DataFrames tidy to avoid repeated parsing.
  • Retry with backoff
import time, requests

def get_with_backoff(url, params=None, headers=None, attempts=5):
    delay = 1.0
    for i in range(attempts):
        try:
            r = requests.get(url, params=params, headers=headers, timeout=30)
            if r.status_code == 429 and i < attempts - 1:
                time.sleep(delay); delay *= 2; continue
            r.raise_for_status()
            return r
        except requests.RequestException:
            if i == attempts - 1:
                raise
            time.sleep(delay); delay *= 2
  • Minimize JSON-to-DataFrame overhead
    • Select and cast columns once; avoid holding unused fields.

Example workflow (end-to-end)

  1. Pull OHLCV from Binance for backtesting a USDT pair.
  2. Pull CoinGecko daily close for broad market sanity checks.
  3. Align on date, inner-join by date, drop today’s partial bar.
  4. Compute features (returns, rolling vol) and export to parquet.

Tiny FAQ

  • Q: I need OHLCV for BTCUSD without an account. What should I use?
    • A: Binance BTCUSDT daily klines are free and require no key.
  • Q: Why don’t CoinGecko OHLC match exchange candles?
    • A: CoinGecko aggregates across venues; definitions and timing differ.
  • Q: How can I get more than 1000 days from Binance?
    • A: Page backward using the startTime/endTime parameters or store incrementally.
  • Q: My last bar is zero volume; is that a bug?
    • A: Often it’s an incomplete current-day bar. Filter to completed days for signals.

Next Article: Generating Real-Time Trading Signals with yfinance and Python

Series: Algorithmic Trading with Python

Python