Free Crypto Price Data APIs: A Trader's Complete Guide
A practical guide to free crypto price data APIs covering CoinGecko, Binance, and more, with working Python and JavaScript code examples for real-time and historical price data.
A practical guide to free crypto price data APIs covering CoinGecko, Binance, and more, with working Python and JavaScript code examples for real-time and historical price data.
Price data is the foundation of every trading decision — and the best sources are completely free. Whether you're building an algorithmic trading bot, a portfolio tracker, a custom alert system, or pulling historical prices into a backtest, you don't need to pay for a data subscription to get started. A handful of well-maintained free crypto price data APIs cover almost everything individual traders and small teams need, with rate limits generous enough for most non-institutional use cases. This guide covers the top options, compares their actual limitations, and gives you working code to connect in minutes.
The crypto data API landscape has matured considerably. Several providers now offer genuinely useful free tiers — not just trial access, but sustainable free plans that cover real use cases.
CoinGecko consistently tops the list for breadth and accessibility. Their free Demo tier covers over 14,000 coins, requires no API key for basic endpoints, and returns prices, market caps, volume, and 24-hour changes in a single call. Binance's public REST API is arguably the best option for real-time major-pair data: zero authentication required, reflecting one of the world's highest-volume exchanges, and extremely fast. CryptoCompare offers strong historical data going back several years with a free tier of 100,000 monthly calls. Kraken's public API is worth knowing for USD-denominated spot prices on a regulated platform. For derivatives data, OKX exposes full market data at /api/v5/market/tickers with no account needed, and Bybit's V5 API does the same at /v5/market/tickers — useful if you're already trading on either of those platforms.
| API | Free Rate Limit | Auth Required | Best For |
|---|---|---|---|
| CoinGecko | 30 req/min | No (key optional) | Broad market data, altcoins |
| Binance Public | 1200 req/min | No | Real-time major pairs |
| CryptoCompare | 100k calls/month | API key required | Historical OHLCV data |
| Kraken Public | ~1 req/sec | No | USD regulated spot prices |
| OKX Public | 20 req/2sec | No | Spot and derivatives |
| Coinbase Advanced | 10 req/sec | No (public) | US-regulated spot prices |
CoinGecko's /simple/price endpoint is the fastest path to current crypto prices. No API key is required at the free tier — though registering for a free Demo key and passing it in the x-cg-demo-api-key header unlocks higher rate limits. The endpoint accepts comma-separated coin IDs and returns prices in any fiat currency, plus optional fields like 24h price change, market cap, and fully diluted valuation. The coin ID format is lowercase with hyphens, so Bitcoin is 'bitcoin', Ethereum is 'ethereum', Solana is 'solana'. You can look up exact IDs on CoinGecko's /coins/list endpoint. Here is a production-ready implementation with full error handling:
import requests
import time
BASE_URL = "https://api.coingecko.com/api/v3"
API_KEY = None # Optional: register free at coingecko.com
def get_crypto_price(coin_ids, currency="usd"):
"""
Fetch current prices for one or more coins.
coin_ids: str or list of CoinGecko coin IDs
"""
if isinstance(coin_ids, list):
coin_ids = ",".join(coin_ids)
url = f"{BASE_URL}/simple/price"
params = {
"ids": coin_ids,
"vs_currencies": currency,
"include_24hr_change": "true",
"include_market_cap": "true"
}
headers = {}
if API_KEY:
headers["x-cg-demo-api-key"] = API_KEY
try:
response = requests.get(url, params=params, headers=headers, timeout=10)
if response.status_code == 429:
print("Rate limit hit — sleeping 60s")
time.sleep(60)
return get_crypto_price(coin_ids, currency) # retry once
response.raise_for_status()
return response.json()
except requests.exceptions.Timeout:
print("Request timed out")
return {}
except requests.exceptions.HTTPError as e:
print(f"HTTP error {e.response.status_code}")
return {}
except requests.exceptions.RequestException as e:
print(f"Network error: {e}")
return {}
# Fetch prices for multiple coins in one request
coins = ["bitcoin", "ethereum", "solana", "chainlink"]
prices = get_crypto_price(coins)
for coin, data in prices.items():
price = data["usd"]
change = data["usd_24h_change"]
sign = "+" if change > 0 else ""
print(f"{coin.upper()}: ${price:,.2f} ({sign}{change:.2f}%)")
The code handles the three most common failure modes: a 429 rate-limit response (retries after a 60-second pause), request timeouts (CoinGecko occasionally spikes in latency during high-traffic periods), and generic network errors. Passing multiple coin IDs in a single request is critical for staying under the rate limit — one call for 20 coins is far more efficient than 20 individual calls. For the crypto prices api free use case, this batching pattern is the single most important optimization you can make.
For backtesting strategies or analyzing historical trends, the free crypto price history api from CoinGecko is one of the most valuable features available without a paid subscription. The /coins/{id}/market_chart endpoint returns timestamps paired with price, volume, and market cap data. The resolution adjusts automatically based on the range requested: under 1 day gives hourly data, 1–90 days returns daily candles, and beyond 90 days gives weekly resolution on the free tier. Daily granularity covers most swing trading and trend-following backtests without touching a paid data provider. Here is a clean implementation that also pulls volume alongside price:
import requests
from datetime import datetime
import time
BASE_URL = "https://api.coingecko.com/api/v3"
def get_price_history(coin_id, days=30, currency="usd"):
"""
Fetch free crypto price history from CoinGecko.
days: 1, 7, 14, 30, 90, 180, 365, or 'max'
Returns list of {date, price, volume} dicts.
"""
url = f"{BASE_URL}/coins/{coin_id}/market_chart"
params = {
"vs_currency": currency,
"days": days,
}
try:
response = requests.get(url, params=params, timeout=15)
if response.status_code == 429:
print("Rate limit — waiting 60s before retry")
time.sleep(60)
return get_price_history(coin_id, days, currency)
response.raise_for_status()
data = response.json()
prices = data.get("prices", [])
volumes = data.get("total_volumes", [])
result = []
for i, (ts, price) in enumerate(prices):
entry = {
"date": datetime.fromtimestamp(ts / 1000).strftime("%Y-%m-%d"),
"price": round(price, 4),
"volume": round(volumes[i][1], 0) if i < len(volumes) else None
}
result.append(entry)
return result
except requests.exceptions.RequestException as e:
print(f"Error fetching history for {coin_id}: {e}")
return []
# Get Ethereum 30-day price history
history = get_price_history("ethereum", days=30)
print(f"Retrieved {len(history)} data points")
for entry in history[-5:]: # Show last 5 entries
print(f"{entry['date']}: ${entry['price']:,.2f} | Vol: ${entry['volume']:,.0f}")
Tip: Add time.sleep(2) between calls when looping through many assets. CoinGecko's free tier is lenient but will return 429 errors when you loop quickly through large coin lists. Batching coin IDs in the /simple/price endpoint is far more efficient than sequential single-coin calls.
For longer-term data going back years, the /coins/{id}/market_chart/range endpoint accepts Unix timestamps for a precise time window. Bitcoin and Ethereum history goes back to 2013 and 2016 respectively on the free tier. If you need tick-by-tick trade data rather than daily candles, you will need to move to either a paid CoinGecko tier or Binance's klines endpoint, which offers sub-minute granularity with no authentication required.
Binance's public market data API is one of the most underused tools in crypto development. No Binance account, no API key, and no OAuth flow required — just HTTP requests returning clean JSON. The rate limits are generous at 1200 requests per minute on the IP-level weight system, and the data reflects live prices on one of the world's largest exchanges by volume. This makes it particularly reliable as a reference price source or as a fallback when other APIs are rate-limited. The JavaScript example below demonstrates concurrent price fetching using Promise.allSettled, which gracefully handles individual failures without blocking the full batch:
// Binance public market data — no API key needed
const BINANCE_BASE = 'https://api.binance.com/api/v3';
async function getBinancePrice(symbol) {
try {
const res = await fetch(`${BINANCE_BASE}/ticker/price?symbol=${symbol}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
return { symbol: data.symbol, price: parseFloat(data.price) };
} catch (err) {
console.error(`[${symbol}] failed:`, err.message);
return { symbol, price: null, error: err.message };
}
}
async function get24hrStats(symbol) {
try {
const res = await fetch(`${BINANCE_BASE}/ticker/24hr?symbol=${symbol}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const d = await res.json();
return {
symbol: d.symbol,
price: parseFloat(d.lastPrice),
change24h: parseFloat(d.priceChangePercent),
high24h: parseFloat(d.highPrice),
low24h: parseFloat(d.lowPrice),
volume: parseFloat(d.volume)
};
} catch (err) {
return { symbol, error: err.message };
}
}
// Fetch spot prices for multiple pairs concurrently
const symbols = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'LINKUSDT'];
const results = await Promise.allSettled(symbols.map(getBinancePrice));
results.forEach(result => {
if (result.status === 'fulfilled' && result.value.price) {
const { symbol, price } = result.value;
console.log(`${symbol}: $${price.toLocaleString('en-US', { minimumFractionDigits: 2 })}`);
}
});
// Full 24hr stats for BTC
const btcStats = await get24hrStats('BTCUSDT');
console.log(`BTC 24h high: $${btcStats.high24h.toLocaleString()} | low: $${btcStats.low24h.toLocaleString()} | change: ${btcStats.change24h}%`);
Beyond spot prices, Binance's free public API exposes order book depth at /api/v3/depth (useful for estimating slippage before a large trade), historical klines at /api/v3/klines with configurable intervals from 1 minute to 1 month, and recent trades at /api/v3/trades. All unauthenticated. The klines endpoint is particularly powerful for free crypto price history api use cases — it returns up to 1,000 candles per request and goes back to each pair's listing date on Binance. On Binance you can also query aggregate trade data using /api/v3/aggTrades, which is useful for reconstructing volume profiles during specific time windows.
Real trading tools almost never rely on a single data source. Rate limits are the obvious reason — CoinGecko's 30 calls per minute evaporates quickly when you're monitoring 50 assets every few seconds. But there's a deeper reason too: API outages happen. When your primary data source goes down, your tool needs to keep working.
A practical approach is a simple fallback chain: CoinGecko for broad market data and altcoins, Binance for real-time prices on major pairs, and Coinbase's public API for USD spot reference prices on regulated assets. When your primary source returns a 429 or times out, immediately retry against the fallback. Cache the most recent successful response with a timestamp so you can serve slightly-stale data during gaps rather than failing entirely. For traders on Bybit and OKX specifically, using the native exchange API for your primary trading venue means your price reference matches your execution prices exactly — no slippage surprises from cross-exchange price divergence.
If you're building trading signals rather than raw data infrastructure, platforms like VoiceOfChain aggregate price data alongside on-chain flows and volume metrics into ready-to-use trade signals — removing the need to build and maintain the full pipeline yourself. But for custom analytics, backtests, or bots where you control the logic, the free APIs described here are the right foundation.
Free crypto price data APIs have gotten genuinely good. CoinGecko covers the broadest range of assets with minimal setup, Binance delivers the most reliable real-time data for major pairs, and together they handle the vast majority of use cases without spending anything. Start with CoinGecko's /simple/price endpoint for exploration and broad market coverage, add Binance as a real-time supplement for major pairs, and implement a basic fallback between them. The code in this guide is a production-ready starting point — add your caching layer, a retry strategy for rate limits, and local storage for historical data, and you'll have a solid data foundation for whatever you're building.