CoinGecko Setup
The CoinGecko free API requires no API key for basic endpoints. You only need Python's built-in requests library to fetch live cryptocurrency data. Unlike other APIs, there's zero registration overhead — just start making calls.
Installation & First Call
Most Python installations include requests by default. If not, install it with pip install requests. Here's a simple function to fetch data from CoinGecko:
# No pip install needed — just requests (standard)
import requests
import json
from datetime import datetime
BASE = "https://api.coingecko.com/api/v3"
def cg_get(endpoint, params=None):
"""Rate-limited CoinGecko fetch."""
import time
resp = requests.get(f"{BASE}{endpoint}", params=params, timeout=10)
resp.raise_for_status()
return resp.json()
# Quick test — get BTC price
data = cg_get("/simple/price", {"ids": "bitcoin", "vs_currencies": "usd"})
print(f"BTC: ${data['bitcoin']['usd']:,.2f}")
This simple wrapper handles error checking and will be the foundation for all our examples. The timeout=10 parameter prevents hanging on slow network connections.
Top Coins by Market Cap
The /coins/markets endpoint returns detailed market data for the top cryptocurrencies by market capitalization. This is perfect for building a watchlist or dashboard of the largest crypto assets.
Fetching Top 20 Coins
Request the top 20 coins with 24h and 7d price changes in a single call:
def get_top_coins(limit=20):
"""Fetch top N coins by market cap."""
coins = cg_get("/coins/markets", {
"vs_currency": "usd",
"order": "market_cap_desc",
"per_page": limit,
"page": 1,
"sparkline": False,
"price_change_percentage": "24h,7d"
})
return coins
coins = get_top_coins(20)
print(f"{'Rank':<5} {'Coin':<15} {'Price':>12} {'24h%':>8} {'7d%':>8} {'Mkt Cap':>15}")
print("-" * 65)
for c in coins:
rank = c['market_cap_rank']
name = c['symbol'].upper()
price = c['current_price']
d24 = c.get('price_change_percentage_24h') or 0
d7 = c.get('price_change_percentage_7d_in_currency') or 0
mcap = c['market_cap']
arrow24 = "▲" if d24 > 0 else "▼"
print(f"{rank:<5} {name:<15} ${price:>11,.4f} {arrow24}{abs(d24):>6.2f}% {'':>8} ${mcap:>13,.0f}")
Key parameters:
vs_currency: The fiat currency for prices (usd, eur, gbp, etc.)order: Sort bymarket_cap_desc,gecko_desc, orvolume_descper_page: Number of results (max 250 per page)page: Pagination (1-based)price_change_percentage: Comma-separated list of time windows (1h,24h,7d,30d)
OHLCV Historical Data
OHLCV stands for Open, High, Low, Close, Volume. CoinGecko provides daily candle data via the /coins/{id}/ohlc endpoint, perfect for technical analysis and volatility studies.
Fetching Daily Candles
Retrieve 30 days of OHLCV data and calculate volatility metrics:
def get_ohlcv(coin_id: str, days: int = 30):
"""
Fetch OHLCV data via CoinGecko market_chart.
Returns DataFrame with [date, open, high, low, close].
"""
data = cg_get(f"/coins/{coin_id}/ohlc", {
"vs_currency": "usd",
"days": days
})
# Each row: [timestamp_ms, open, high, low, close]
import pandas as pd
df = pd.DataFrame(data, columns=["timestamp_ms","open","high","low","close"])
df["date"] = pd.to_datetime(df["timestamp_ms"], unit="ms")
df = df[["date","open","high","low","close"]]
return df
df = get_ohlcv("bitcoin", 30)
print(df.tail(5).to_string(index=False))
# Simple volatility check
df["daily_range_pct"] = (df["high"] - df["low"]) / df["open"] * 100
print(f"\nAvg daily range (30d): {df['daily_range_pct'].mean():.2f}%")
print(f"Max daily range (30d): {df['daily_range_pct'].max():.2f}%")
Valid day parameters: 1, 7, 14, 30, 90, 180, 365, max (all-time)
The daily range percentage helps identify volatile periods — useful for setting stop-losses or alert thresholds.
Price Alert System
Build a simple polling-based price alert system that respects CoinGecko's rate limits (30 req/min on free tier). We use a cooldown mechanism to prevent alert spam:
import time
import smtplib
from email.mime.text import MIMEText
WATCHLIST = {
"bitcoin": {"above": 75000, "below": 55000},
"ethereum": {"above": 4500, "below": 2800},
"solana": {"above": 280, "below": 120},
}
COOLDOWN_SECONDS = 3600 # 1 hour between same alert
last_alert = {}
def check_prices():
ids = ",".join(WATCHLIST.keys())
prices = cg_get("/simple/price", {
"ids": ids,
"vs_currencies": "usd",
"include_24hr_change": True
})
for coin, levels in WATCHLIST.items():
price = prices[coin]["usd"]
change = prices[coin].get("usd_24h_change", 0)
key_above = f"{coin}_above"
key_below = f"{coin}_below"
now = time.time()
if price > levels["above"]:
if now - last_alert.get(key_above, 0) > COOLDOWN_SECONDS:
send_alert(coin, price, change, "ABOVE", levels["above"])
last_alert[key_above] = now
if price < levels["below"]:
if now - last_alert.get(key_below, 0) > COOLDOWN_SECONDS:
send_alert(coin, price, change, "BELOW", levels["below"])
last_alert[key_below] = now
def send_alert(coin, price, change, direction, level):
emoji = "🚀" if direction == "ABOVE" else "📉"
msg = MIMEText(
f"{emoji} {coin.upper()} is {direction} ${level:,.0f}\n"
f"Current price: ${price:,.2f}\n"
f"24h change: {change:+.2f}%\n"
f"Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
)
msg["Subject"] = f"[OpenClaw] {coin.upper()} Alert: {direction} ${level:,.0f}"
msg["From"] = "alerts@yourdomain.com"
msg["To"] = "you@example.com"
with smtplib.SMTP("localhost") as s:
s.send_message(msg)
print(f"Alert sent: {coin} {direction} ${level:,.0f}")
# Run every 2 minutes (well within 30 req/min limit)
while True:
check_prices()
time.sleep(120)
Key design patterns:
- Watchlist dict: Easy to add/remove coins and set thresholds
- Cooldown mechanism: Prevents alert fatigue by enforcing a 1-hour minimum between identical alerts
- Batch calls: Fetches all coin prices in one API call via comma-separated IDs
- 2-minute polling: Respects the 30 req/min limit with margin for other calls
The example uses smtplib.SMTP("localhost") which assumes a local mail server. For Gmail or other providers, use the provider's SMTP host and credentials. For production, consider using a service like SendGrid or Twilio.
Multi-Coin Dashboard
Build a live updating terminal dashboard to monitor multiple coins simultaneously. This example uses the tabulate library for pretty-printed tables:
from tabulate import tabulate
import os
def clear():
os.system("cls" if os.name == "nt" else "clear")
PORTFOLIO_COINS = ["bitcoin","ethereum","solana","cardano","polkadot","chainlink","avalanche-2"]
def dashboard():
data = cg_get("/coins/markets", {
"vs_currency": "usd",
"ids": ",".join(PORTFOLIO_COINS),
"order": "market_cap_desc",
"per_page": 50,
"page": 1,
"sparkline": False,
"price_change_percentage": "1h,24h,7d"
})
rows = []
for c in data:
d1h = c.get("price_change_percentage_1h_in_currency") or 0
d24 = c.get("price_change_percentage_24h") or 0
d7d = c.get("price_change_percentage_7d_in_currency") or 0
rows.append([
c["symbol"].upper(),
f"${c['current_price']:,.4f}",
f"{'+' if d1h>0 else ''}{d1h:.2f}%",
f"{'+' if d24>0 else ''}{d24:.2f}%",
f"{'+' if d7d>0 else ''}{d7d:.2f}%",
f"${c['market_cap']/1e9:.2f}B",
])
clear()
print(f"=== OpenClaw Crypto Dashboard | {datetime.now().strftime('%H:%M:%S')} ===\n")
print(tabulate(rows, headers=["Coin","Price","1h","24h","7d","Mkt Cap"], tablefmt="rounded_outline"))
print("\nRefreshing in 2 min... (Ctrl+C to exit)")
while True:
try:
dashboard()
time.sleep(120)
except KeyboardInterrupt:
print("\nDashboard stopped.")
break
Install tabulate with pip install tabulate. This dashboard refreshes every 2 minutes, showing 1-hour, 24-hour, and 7-day price changes alongside current market cap. Perfect for monitoring a crypto portfolio.
Rate Limiting & Best Practices
CoinGecko's free API is rate-limited but generous for most use cases. Understanding these limits prevents your automation from being blocked:
Free tier: 30 requests/minute. Pro Demo key: 500/minute (still free for testing). If you exceed limits, you'll get HTTP 429 (Too Many Requests) responses.
Strategies to Stay Within Limits
- Batch requests: Fetch multiple coins in one call using comma-separated IDs:
ids=bitcoin,ethereum,solana - Increase polling interval: Use
time.sleep(2)or more between sequential calls in loops - Cache responses: Store data locally and only refresh when necessary (e.g., every 2–5 minutes)
- Use websockets: For real-time data, switch to CoinCap's WebSocket API (unlimited free tier) instead of polling
- Avoid per-coin loops: Instead of
for coin in coins: cg_get(...), batch them:cg_get(..., ids=",".join(coins))
Handling Rate Limit Errors
Add retry logic with exponential backoff:
import time
def cg_get_with_retry(endpoint, params=None, max_retries=3):
"""Fetch with exponential backoff on rate limit."""
for attempt in range(max_retries):
try:
resp = requests.get(f"{BASE}{endpoint}", params=params, timeout=10)
if resp.status_code == 429:
wait = 2 ** attempt # 1, 2, 4 seconds
print(f"Rate limited. Waiting {wait}s...")
time.sleep(wait)
continue
resp.raise_for_status()
return resp.json()
except requests.RequestException as e:
print(f"Error: {e}")
if attempt < max_retries - 1:
time.sleep(2 ** attempt)
return None
CoinGecko Endpoints Reference
A quick reference table of the most useful free API endpoints:
| Endpoint | Description | Key Parameters |
|---|---|---|
/simple/price |
Live prices for 1+ coins (fastest) | ids, vs_currencies, include_24hr_change |
/coins/markets |
Full market data + % changes (paginated) | vs_currency, order, per_page, price_change_percentage |
/coins/{id}/ohlc |
OHLCV candles (daily or 4h), 1–90 days | vs_currency, days |
/coins/{id}/market_chart |
Price + volume + mcap history | vs_currency, days, interval |
/search/trending |
Top 7 trending coins right now | none (no params required) |
/global |
Total crypto market cap + dominance % | none (no params required) |
Coin IDs: Use lowercase with hyphens (e.g., bitcoin, ethereum, chainlink). Get a full list at /coins/list.
Common Use Cases
Get trending coins in real time
trending = cg_get("/search/trending")
for coin in trending["coins"][:7]:
print(f"{coin['item']['name']} (#{coin['item']['market_cap_rank']})")
Get total market cap + Bitcoin dominance
global_data = cg_get("/global")
mcap = global_data["data"]["total_market_cap"]["usd"]
btc_dom = global_data["data"]["btc_dominance"]
print(f"Total Market Cap: ${mcap/1e12:.2f}T")
print(f"BTC Dominance: {btc_dom:.2f}%")
Get price data in multiple currencies
prices = cg_get("/simple/price", {
"ids": "bitcoin,ethereum",
"vs_currencies": "usd,eur,gbp,jpy"
})
for coin, rates in prices.items():
print(f"{coin}: {rates}")
Next Steps
You now have the foundation for fetching live prices, historical OHLCV data, and building automated price alerts. In the next part of the series, we'll explore On-Chain Analytics — tracking wallet movements, transaction flows, and on-chain signals using APIs like Glassnode and The Graph.
Key skills to practice:
- Build a price alert system for your favorite coins
- Create a persistent dashboard that logs prices to a CSV file
- Experiment with different polling intervals and batch sizes
- Integrate alerts into a Slack or Discord webhook