
Coingecko Api
- 201 installs
- 257 repo stars
- Updated June 24, 2026
- agiprolabs/claude-trading-skills
coingecko-api is a Claude Code skill that queries the CoinGecko API for crypto prices, historical charts, exchange volumes, trending tokens, and global market stats.
About
coingecko-api is a Claude Code skill that queries the CoinGecko REST API for crypto market data: current and historical prices, OHLC candles, exchange volumes, trending tokens, and global market stats. A developer uses it when building macro or long-term historical crypto analysis into an agent or script. It documents the free (no-key, 30 calls/min) and Pro tiers plus rate-limit backoff.
- Queries CoinGecko for prices, historical OHLCV, exchange volumes, and global market stats
- Free tier needs no API key (30 calls/min); Pro key raises the limit
- Covers 13,000+ tokens with backoff-on-429 rate-limit handling
Coingecko Api by the numbers
- 201 all-time installs (skills.sh)
- Ranked #466 of 1,106 Finance & Trading skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
coingecko-api capabilities & compatibility
Free on the no-key tier (30 calls/min); optional paid Pro key for higher limits.
- Capabilities
- crypto market data · historical price fetch · ohlc candles · global market stats · trending tokens
- Use cases
- data analysis · research · trading
- Runs
- Runs locally
- Pricing
- Freemium
What coingecko-api says it does
Broad crypto market data from CoinGecko covering 13,000+ tokens.
The free tier requires no API key and supports 30 calls/min.
npx skills add https://github.com/agiprolabs/claude-trading-skills --skill coingecko-apiAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 201 |
|---|---|
| repo stars | ★ 257 |
| Last updated | June 24, 2026 |
| Repository | agiprolabs/claude-trading-skills ↗ |
What it does
Fetch crypto prices, historical OHLCV, and global market stats from CoinGecko for macro and long-term analysis.
Who is it for?
Macro analysis, long-term historical OHLCV, and cross-exchange volume comparisons across 13,000+ tokens.
Skip if: Real-time Solana DEX data or sub-daily granularity on new token launches.
When should I use this skill?
You need CoinGecko market data (prices, history, global stats, trending) inside an agent or script.
What you get
Working CoinGecko queries for prices, history, and market stats with proper rate-limit handling.
By the numbers
- Covers 13,000+ tokens
- Free tier: 30 calls/min
- OHLC windows limited to 1/7/14/30/90/180/365 days
Files
CoinGecko API Skill
Query the CoinGecko API for comprehensive crypto market data — prices, historical charts, exchange volumes, trending tokens, global stats, and category breakdowns. The free tier requires no API key and supports 30 calls/min.
When to Use This Skill
- Macro analysis: Global market cap, BTC dominance, total volume trends
- Historical data: Daily/hourly OHLCV going back years (not minutes-level)
- Cross-exchange comparisons: Exchange volume rankings and trust scores
- Trending/discovery: What tokens are trending on CoinGecko in the last 24h
- Category analysis: Compare DeFi vs L1 vs meme coin market caps
- Token research: Full metadata including links, description, community stats
Use Birdeye or DexScreener instead for real-time Solana DEX data, new token launches, or sub-daily granularity on Solana tokens.
Quick Start
Get Current Prices
import httpx
# No API key needed for free tier
resp = httpx.get(
"https://api.coingecko.com/api/v3/simple/price",
params={"ids": "solana,bitcoin,ethereum", "vs_currencies": "usd",
"include_24hr_change": "true"},
)
data = resp.json()
for coin, info in data.items():
print(f"{coin}: ${info['usd']:.2f} ({info['usd_24h_change']:+.1f}%)")Get Top Coins by Market Cap
import httpx
resp = httpx.get(
"https://api.coingecko.com/api/v3/coins/markets",
params={"vs_currency": "usd", "order": "market_cap_desc",
"per_page": 10, "page": 1, "sparkline": "false"},
)
for coin in resp.json():
print(f"{coin['symbol'].upper():>6} ${coin['current_price']:>10,.2f} "
f"MCap: ${coin['market_cap']/1e9:.1f}B "
f"24h: {coin['price_change_percentage_24h']:+.1f}%")Get Historical Price Data
import httpx
import pandas as pd
resp = httpx.get(
"https://api.coingecko.com/api/v3/coins/solana/market_chart",
params={"vs_currency": "usd", "days": "90", "interval": "daily"},
)
data = resp.json()
df = pd.DataFrame(data["prices"], columns=["timestamp", "price"])
df["date"] = pd.to_datetime(df["timestamp"], unit="ms")
df = df.set_index("date").drop(columns=["timestamp"])
print(df.describe())Get OHLC Candle Data
import httpx
resp = httpx.get(
"https://api.coingecko.com/api/v3/coins/solana/ohlc",
params={"vs_currency": "usd", "days": "30"},
)
# Returns [[timestamp, open, high, low, close], ...]
candles = resp.json()
for c in candles[-5:]:
print(f" O={c[1]:.2f} H={c[2]:.2f} L={c[3]:.2f} C={c[4]:.2f}")Global Market Stats
import httpx
resp = httpx.get("https://api.coingecko.com/api/v3/global")
g = resp.json()["data"]
print(f"Total Market Cap: ${g['total_market_cap']['usd']/1e12:.2f}T")
print(f"24h Volume: ${g['total_volume']['usd']/1e9:.0f}B")
print(f"BTC Dominance: {g['market_cap_percentage']['btc']:.1f}%")
print(f"Active Coins: {g['active_cryptocurrencies']:,}")Trending Coins
import httpx
resp = httpx.get("https://api.coingecko.com/api/v3/search/trending")
for item in resp.json()["coins"]:
coin = item["item"]
print(f"#{coin['market_cap_rank'] or '?':>4} {coin['name']} ({coin['symbol']})")Authentication
The free tier requires no API key (30 calls/min). For higher limits, get a Pro key from https://www.coingecko.com/en/api/pricing and set:
export COINGECKO_API_KEY="CG-xxxxxxxxxxxxxxxxxxxx"Pro requests use a different base URL and header:
import os, httpx
API_KEY = os.getenv("COINGECKO_API_KEY", "")
if API_KEY:
BASE_URL = "https://pro-api.coingecko.com/api/v3"
HEADERS = {"x-cg-pro-api-key": API_KEY}
else:
BASE_URL = "https://api.coingecko.com/api/v3"
HEADERS = {}Rate Limiting
Free tier: 30 requests/min. Implement backoff on 429 responses:
import time, httpx
def cg_get(url: str, params: dict, max_retries: int = 3) -> dict:
"""GET with retry on rate limit."""
for attempt in range(max_retries):
resp = httpx.get(url, params=params, headers=HEADERS, timeout=15.0)
if resp.status_code == 429:
wait = 2 ** attempt * 10
print(f"Rate limited, waiting {wait}s...")
time.sleep(wait)
continue
resp.raise_for_status()
return resp.json()
raise RuntimeError("Max retries exceeded")Finding CoinGecko Token IDs
CoinGecko uses slug-style IDs (e.g., solana, bitcoin, usd-coin). To find an ID from a contract address or name, see references/id_mapping.md.
Quick lookup by contract address (useful for Solana tokens):
import httpx
# Look up by Solana contract address
contract = "So11111111111111111111111111111111111111112"
resp = httpx.get(
"https://api.coingecko.com/api/v3/coins/solana/contract/"
+ contract
)
coin = resp.json()
print(f"ID: {coin['id']}, Name: {coin['name']}")Key Limitations
- No real-time data: Prices update every 1-2 minutes on free tier
- Limited Solana coverage: Many newer Solana tokens are not listed
- OHLC granularity: Only 1/7/14/30/90/180/365 day windows, candle size
depends on the window (see references/endpoints.md)
- Historical gaps: Some tokens have missing data for early periods
- Free tier throttling: 30 req/min means batch operations need careful pacing
See references/data_quality.md for detailed notes on data gaps and tier differences.
Files
References
references/endpoints.md— Complete endpoint reference with parameters, response schemas, and rate limitsreferences/id_mapping.md— How to find CoinGecko IDs for tokens, contract address mapping, search tipsreferences/data_quality.md— Data quality notes, historical gaps, free vs pro tier differences
Scripts
scripts/fetch_market_data.py— Fetch top coins, trending tokens, and global stats (supports--demomode)scripts/historical_analysis.py— Fetch historical OHLCV data and compute returns, volatility, drawdown (supports--demomode)
CoinGecko API — Data Quality Notes
Free vs Pro Tier Differences
| Feature | Free | Pro (Analyst+) |
|---|---|---|
| Rate limit | 30 calls/min | 500 calls/min |
| Authentication | None required | x-cg-pro-api-key header |
| Base URL | api.coingecko.com | pro-api.coingecko.com |
| Historical granularity | Daily (90+ days) | 5-min, hourly available |
| OHLC intervals | 30m / 4h / 4d | More granular options |
| Data freshness | ~1-2 min delay | ~30 sec delay |
| Market chart range | max available | max available |
| Endpoints available | Most endpoints | All endpoints + extras |
Price Data Freshness
- Free tier prices update approximately every 60-120 seconds.
- CoinGecko aggregates prices across all exchanges where a token is listed.
- The reported price is a volume-weighted average, not a single exchange price.
- For Solana DEX tokens, CoinGecko may lag behind on-chain price by several minutes.
- Use Birdeye or DexScreener for real-time Solana DEX prices.
Historical Data Granularity
The /coins/{id}/market_chart endpoint auto-selects granularity based on the days parameter:
| Days Range | Auto Granularity | Approx Data Points |
|---|---|---|
| 1 | ~5 minutes | ~288 |
| 2-90 | Hourly | ~48-2160 |
| 91-365 | Daily | ~91-365 |
max | Daily | Varies (up to ~3650) |
Setting interval=daily forces daily granularity for any range. There is no way to force hourly or minute-level granularity on the free tier for ranges over 90 days.
OHLC Candle Granularity
The /coins/{id}/ohlc endpoint returns different candle sizes depending on the days parameter. You cannot choose the candle size independently.
| Days | Candle Size | Approx Candles |
|---|---|---|
| 1 | 30 minutes | 48 |
| 7 | 4 hours | 42 |
| 14 | 4 hours | 84 |
| 30 | 4 hours | 180 |
| 90 | 4 days | ~23 |
| 180 | 4 days | ~45 |
| 365 | 4 days | ~91 |
Known Data Gaps
Missing Early History
Some tokens have gaps in their early trading history. The /market_chart endpoint may return fewer data points than expected, or skip days entirely. Always check the actual timestamps returned rather than assuming regular intervals.
Volume Spikes
CoinGecko volume data can include wash trading volume from some exchanges. The "trust score" on exchanges helps filter this, but aggregated volume numbers should be treated as approximate.
Market Cap Accuracy
market_cap=current_price * circulating_supplycirculating_supplyis manually maintained by CoinGecko and may lag behind
on-chain reality, especially for tokens with complex unlock schedules.
fully_diluted_valuationusestotal_supplywhich may also be approximate.
ATH/ATL Data
All-time high and low values are based on CoinGecko's own price history. If a token was listed on CoinGecko after its actual ATH (common for tokens that launched on DEXes before getting listed), the recorded ATH may be lower than the true ATH.
Rate Limit Handling
CoinGecko returns HTTP 429 when rate limited. The response includes no Retry-After header, so implement exponential backoff:
import time
import httpx
def safe_get(url: str, params: dict, max_retries: int = 3) -> dict:
"""Fetch with exponential backoff on rate limits."""
for attempt in range(max_retries):
resp = httpx.get(url, params=params, timeout=15.0)
if resp.status_code == 429:
wait = 2 ** attempt * 10 # 10s, 20s, 40s
time.sleep(wait)
continue
resp.raise_for_status()
return resp.json()
raise RuntimeError(f"Rate limited after {max_retries} retries")Timestamp Format
- All Unix timestamps in responses are in milliseconds (not seconds).
- Convert with:
pd.to_datetime(ts, unit='ms')ordatetime.fromtimestamp(ts / 1000). - The
last_updated_atfield in/simple/priceis in seconds (inconsistency).
Currency Support
CoinGecko supports ~60 fiat currencies and several crypto currencies as the vs_currency parameter. Common values: usd, eur, gbp, jpy, btc, eth, sol.
To get the full list: GET /simple/supported_vs_currencies.
Data Not Available via Free Tier
The following require a paid plan:
- Historical data with sub-daily granularity beyond 90 days
- Exchange-specific OHLCV data
- On-chain DEX data (use Birdeye instead)
- NFT floor price history
- Global DeFi data endpoints (some)
Comparison with Other Data Sources
| Feature | CoinGecko | Birdeye | DexScreener |
|---|---|---|---|
| Solana DEX coverage | Limited | Comprehensive | Good |
| Historical depth | Years | Months | Days-weeks |
| Real-time latency | 1-2 min | Seconds | Seconds |
| Auth required | No (free) | Yes | No |
| Cross-chain | 100+ chains | Solana only | Multi-chain |
| Token count | 13,000+ | 100,000+ | Varies |
| Best for | Macro, history | Solana trading | Quick lookups |
CoinGecko API — Endpoints Reference
Base URL (free): https://api.coingecko.com/api/v3 Base URL (pro): https://pro-api.coingecko.com/api/v3
Free tier: 30 calls/min, no key required. Pro tier: add header x-cg-pro-api-key: YOUR_KEY.
---
Simple Price
- Endpoint:
GET /simple/price - Description: Current price for one or more coins in one or more currencies.
- Parameters:
| Param | Required | Description |
|---|---|---|
ids | Yes | Comma-separated CoinGecko IDs (e.g., bitcoin,solana) |
vs_currencies | Yes | Comma-separated fiat/crypto (e.g., usd,btc) |
include_market_cap | No | true to include market cap |
include_24hr_vol | No | true to include 24h volume |
include_24hr_change | No | true to include 24h % change |
include_last_updated_at | No | true to include Unix timestamp |
- Response:
{
"solana": {
"usd": 125.43,
"usd_market_cap": 58000000000,
"usd_24h_vol": 2100000000,
"usd_24h_change": 3.456,
"last_updated_at": 1709856000
}
}- Example:
curl "https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd&include_24hr_change=true"
---
Coins Markets
- Endpoint:
GET /coins/markets - Description: Top coins with market data (price, volume, market cap, changes).
- Parameters:
| Param | Required | Default | Description |
|---|---|---|---|
vs_currency | Yes | — | Target currency (e.g., usd) |
ids | No | — | Filter to specific coin IDs |
category | No | — | Filter by category slug |
order | No | market_cap_desc | Sort: market_cap_desc, volume_desc, id_asc |
per_page | No | 100 | Results per page (1-250) |
page | No | 1 | Page number |
sparkline | No | false | Include 7-day sparkline array |
price_change_percentage | No | — | Comma-separated: 1h,24h,7d,14d,30d,200d,1y |
- Response (array):
[{
"id": "solana",
"symbol": "sol",
"name": "Solana",
"current_price": 125.43,
"market_cap": 58000000000,
"market_cap_rank": 5,
"total_volume": 2100000000,
"high_24h": 128.50,
"low_24h": 120.10,
"price_change_percentage_24h": 3.456,
"circulating_supply": 440000000,
"total_supply": 580000000,
"ath": 260.06,
"ath_date": "2021-11-06T21:54:35.825Z",
"atl": 0.500801,
"atl_date": "2020-05-11T19:35:23.449Z"
}]- Example:
curl "https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&per_page=10&page=1"
---
Coin Detail
- Endpoint:
GET /coins/{id} - Description: Full coin data — description, links, community stats, market data.
- Parameters:
| Param | Required | Default | Description |
|---|---|---|---|
id | Yes (path) | — | CoinGecko coin ID |
localization | No | true | Include localized descriptions |
tickers | No | true | Include exchange tickers |
market_data | No | true | Include market data |
community_data | No | true | Include community stats |
developer_data | No | true | Include GitHub stats |
sparkline | No | false | Include 7-day sparkline |
- Notes: Large response (~50KB). Set unneeded sections to
falseto reduce size. - Example:
curl "https://api.coingecko.com/api/v3/coins/solana?tickers=false&community_data=false"
---
Market Chart (Historical)
- Endpoint:
GET /coins/{id}/market_chart - Description: Historical price, market cap, and volume over time.
- Parameters:
| Param | Required | Description |
|---|---|---|
id | Yes (path) | CoinGecko coin ID |
vs_currency | Yes | Target currency |
days | Yes | Data range: 1, 7, 14, 30, 90, 180, 365, max |
interval | No | daily for daily points (auto-selected otherwise) |
- Granularity rules (free tier):
- 1 day: ~5-minute intervals
- 2-90 days: hourly
- 90+ days: daily
- Setting
interval=dailyforces daily regardless - Response:
{
"prices": [[1709856000000, 125.43], [1709942400000, 128.10]],
"market_caps": [[1709856000000, 58000000000], ...],
"total_volumes": [[1709856000000, 2100000000], ...]
}- Example:
curl "https://api.coingecko.com/api/v3/coins/solana/market_chart?vs_currency=usd&days=90&interval=daily"
---
OHLC
- Endpoint:
GET /coins/{id}/ohlc - Description: OHLC candle data.
- Parameters:
| Param | Required | Description |
|---|---|---|
id | Yes (path) | CoinGecko coin ID |
vs_currency | Yes | Target currency |
days | Yes | 1, 7, 14, 30, 90, 180, 365 |
- Candle granularity:
- 1-2 days: 30-minute candles
- 3-30 days: 4-hour candles
- 31-365 days: 4-day candles
- Response:
[[timestamp, open, high, low, close], ...] - Example:
curl "https://api.coingecko.com/api/v3/coins/solana/ohlc?vs_currency=usd&days=30"
---
Trending
- Endpoint:
GET /search/trending - Description: Top-7 trending coins on CoinGecko in the last 24 hours.
- Parameters: None.
- Response:
{
"coins": [{
"item": {
"id": "pepe",
"coin_id": 31642,
"name": "Pepe",
"symbol": "PEPE",
"market_cap_rank": 30,
"thumb": "https://...",
"score": 0
}
}]
}- Example:
curl "https://api.coingecko.com/api/v3/search/trending"
---
Global
- Endpoint:
GET /global - Description: Global crypto market statistics.
- Response:
{
"data": {
"active_cryptocurrencies": 13000,
"markets": 900,
"total_market_cap": {"usd": 2500000000000},
"total_volume": {"usd": 85000000000},
"market_cap_percentage": {"btc": 52.1, "eth": 16.3},
"market_cap_change_percentage_24h_usd": 1.5,
"updated_at": 1709856000
}
}- Example:
curl "https://api.coingecko.com/api/v3/global"
---
Categories
- Endpoint:
GET /coins/categories - Description: Token categories with aggregate market data.
- Parameters:
order—market_cap_desc(default),market_cap_asc,name_asc, etc. - Response (array):
[{
"id": "decentralized-finance-defi",
"name": "Decentralized Finance (DeFi)",
"market_cap": 95000000000,
"market_cap_change_24h": 2.1,
"volume_24h": 5000000000,
"top_3_coins": ["https://...", "https://...", "https://..."],
"updated_at": "2025-03-10T00:00:00Z"
}]- Example:
curl "https://api.coingecko.com/api/v3/coins/categories"
---
Exchanges
- Endpoint:
GET /exchanges - Description: Exchange rankings by volume with trust scores.
- Parameters:
per_page(1-250, default 100),page. - Response (array):
[{
"id": "binance",
"name": "Binance",
"year_established": 2017,
"country": "Cayman Islands",
"trust_score": 10,
"trust_score_rank": 1,
"trade_volume_24h_btc": 450000
}]- Example:
curl "https://api.coingecko.com/api/v3/exchanges?per_page=10"
---
Search
- Endpoint:
GET /search - Description: Search for coins, exchanges, and categories by keyword.
- Parameters:
query(required) — search string. - Response:
{
"coins": [{"id": "solana", "name": "Solana", "symbol": "SOL", "market_cap_rank": 5}],
"exchanges": [...],
"categories": [...]
}- Example:
curl "https://api.coingecko.com/api/v3/search?query=solana"
---
Contract Lookup
- Endpoint:
GET /coins/{platform_id}/contract/{contract_address} - Description: Look up a coin by its contract address on a specific platform.
- Path params:
platform_id(e.g.,solana,ethereum),contract_address. - Notes: Returns the same structure as
/coins/{id}. Use this to map Solana mint addresses to CoinGecko IDs. - Example:
curl "https://api.coingecko.com/api/v3/coins/solana/contract/So11111111111111111111111111111111111111112"
CoinGecko API — ID Mapping Guide
CoinGecko uses slug-style string IDs (e.g., bitcoin, solana, usd-coin) rather than ticker symbols. Since many tokens share the same symbol, you must resolve the correct CoinGecko ID before making API calls.
Common Solana Token IDs
| Token | Symbol | CoinGecko ID | Solana Mint Address |
|---|---|---|---|
| Solana | SOL | solana | So11111111111111111111111111111111111111112 |
| Bonk | BONK | bonk | DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263 |
| Jupiter | JUP | jupiter-exchange-solana | JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN |
| Raydium | RAY | raydium | 4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R |
| Marinade SOL | mSOL | msol | mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So |
| Jito SOL | JitoSOL | jito-staked-sol | J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn |
| Pyth Network | PYTH | pyth-network | HZ1JovNiVvGrGNiiYvEozEVgZ58xaU3RKwX8eACQBCt3 |
| Render | RENDER | render-token | rndrizKT3MK1iimdxRdWabcF7Zg7AR5T4nud4EkHBof |
| Helium | HNT | helium | hntyVP6YFm1Hg25TN9WGLqM12b8TQmcknKrdu1oxWux |
| dogwifhat | WIF | dogwifcoin | EKpQGSJtjMFqKZ9KQanSqYXRcF8fBopzLHYxdM65zcjm |
Method 1: Search by Name or Symbol
Use the /search endpoint to find a coin's ID:
import httpx
def search_coin(query: str) -> list[dict]:
"""Search CoinGecko for coins matching a query string."""
resp = httpx.get(
"https://api.coingecko.com/api/v3/search",
params={"query": query},
timeout=15.0,
)
resp.raise_for_status()
return resp.json()["coins"]
results = search_coin("jupiter")
for coin in results[:5]:
print(f" {coin['id']:<35} {coin['symbol']:<8} rank={coin.get('market_cap_rank')}")Gotcha: Searching "SOL" returns dozens of results. Filter by market_cap_rank to find the real one — the token with the lowest rank number is almost always correct.
Method 2: Lookup by Contract Address
Map a Solana mint address directly to a CoinGecko coin:
import httpx
def lookup_by_contract(
contract: str, platform: str = "solana"
) -> dict | None:
"""Look up a CoinGecko coin by its contract/mint address."""
url = (
f"https://api.coingecko.com/api/v3/coins/{platform}"
f"/contract/{contract}"
)
resp = httpx.get(url, timeout=15.0)
if resp.status_code == 404:
return None # Token not listed on CoinGecko
resp.raise_for_status()
data = resp.json()
return {"id": data["id"], "name": data["name"], "symbol": data["symbol"]}
# Example: look up wrapped SOL
result = lookup_by_contract("So11111111111111111111111111111111111111112")
print(result) # {'id': 'solana', 'name': 'Solana', 'symbol': 'sol'}Gotcha: Many newer Solana tokens (especially meme coins launched in the last few weeks) are not yet listed on CoinGecko. A 404 means the token is unlisted.
Method 3: Coins List (Full Mapping)
Download the complete list of all CoinGecko IDs (~14,000 entries):
import httpx
def get_all_coins(include_platform: bool = True) -> list[dict]:
"""Fetch the full CoinGecko coins list with platform addresses."""
resp = httpx.get(
"https://api.coingecko.com/api/v3/coins/list",
params={"include_platform": str(include_platform).lower()},
timeout=30.0,
)
resp.raise_for_status()
return resp.json()
coins = get_all_coins()
# Build a lookup from Solana mint to CoinGecko ID
sol_map = {}
for c in coins:
addr = c.get("platforms", {}).get("solana")
if addr:
sol_map[addr] = c["id"]
# Now look up any Solana mint instantly
mint = "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263"
print(f"BONK CoinGecko ID: {sol_map.get(mint)}") # "bonk"Tip: Cache this list locally — it changes infrequently and costs 1 API call. Re-fetch at most once per day.
Common Gotchas
Symbol Collisions
Many tokens share the same ticker symbol. For example, "SOL" matches both Solana and several other tokens. Always verify by checking market_cap_rank or the contract address.
Wrapped vs Native
CoinGecko often maps wrapped tokens to the same ID as the native token. For example, wrapped SOL (So11111111111111111111111111111111111111112) maps to the solana ID.
ID Format
IDs are always lowercase, hyphen-separated slugs. They do not change once assigned. Examples: bitcoin, usd-coin, jupiter-exchange-solana.
Unlisted Tokens
CoinGecko requires a listing application and review process. Tokens launched in the last few days or with very low liquidity may not be listed. For unlisted Solana tokens, use the Birdeye or DexScreener APIs instead.
Platform IDs
When using contract lookups, the platform ID for Solana is solana, for Ethereum it is ethereum, for BSC it is binance-smart-chain. The full list is available at GET /asset_platforms.
#!/usr/bin/env python3
"""Fetch current crypto market data from the CoinGecko API.
Demonstrates fetching top coins by market cap, trending tokens, and global
market statistics. Supports --demo mode that uses embedded sample data so
no API key or network access is required.
Usage:
python scripts/fetch_market_data.py # Live API calls
python scripts/fetch_market_data.py --demo # Use sample data
Dependencies:
uv pip install httpx
Environment Variables:
COINGECKO_API_KEY: (Optional) CoinGecko Pro API key for higher rate limits.
Free tier (30 calls/min) is used when not set.
"""
import argparse
import json
import os
import sys
import time
from typing import Any, Optional
try:
import httpx
except ImportError:
print("httpx is required. Install with: uv pip install httpx")
sys.exit(1)
# ── Configuration ───────────────────────────────────────────────────
API_KEY = os.getenv("COINGECKO_API_KEY", "")
if API_KEY:
BASE_URL = "https://pro-api.coingecko.com/api/v3"
HEADERS: dict[str, str] = {"x-cg-pro-api-key": API_KEY}
else:
BASE_URL = "https://api.coingecko.com/api/v3"
HEADERS = {}
TOP_N = 15 # Number of top coins to fetch
# ── Demo Data ───────────────────────────────────────────────────────
DEMO_MARKETS = [
{"id": "bitcoin", "symbol": "btc", "name": "Bitcoin",
"current_price": 97250.00, "market_cap": 1920000000000,
"market_cap_rank": 1, "total_volume": 38000000000,
"price_change_percentage_24h": 1.23, "circulating_supply": 19800000,
"ath": 108268.00, "ath_date": "2024-12-17T15:02:41.429Z"},
{"id": "ethereum", "symbol": "eth", "name": "Ethereum",
"current_price": 3450.00, "market_cap": 415000000000,
"market_cap_rank": 2, "total_volume": 18000000000,
"price_change_percentage_24h": -0.45, "circulating_supply": 120000000,
"ath": 4878.26, "ath_date": "2021-11-10T14:24:19.604Z"},
{"id": "solana", "symbol": "sol", "name": "Solana",
"current_price": 185.50, "market_cap": 89000000000,
"market_cap_rank": 5, "total_volume": 4200000000,
"price_change_percentage_24h": 4.78, "circulating_supply": 480000000,
"ath": 293.31, "ath_date": "2025-01-19T11:15:00.000Z"},
{"id": "ripple", "symbol": "xrp", "name": "XRP",
"current_price": 2.35, "market_cap": 135000000000,
"market_cap_rank": 3, "total_volume": 8500000000,
"price_change_percentage_24h": 0.67, "circulating_supply": 57000000000,
"ath": 3.40, "ath_date": "2018-01-07T00:00:00.000Z"},
{"id": "binancecoin", "symbol": "bnb", "name": "BNB",
"current_price": 680.00, "market_cap": 98000000000,
"market_cap_rank": 4, "total_volume": 1800000000,
"price_change_percentage_24h": -0.12, "circulating_supply": 144000000,
"ath": 793.35, "ath_date": "2024-12-04T10:35:00.000Z"},
]
DEMO_TRENDING = {
"coins": [
{"item": {"id": "pepe", "name": "Pepe", "symbol": "PEPE",
"market_cap_rank": 25, "score": 0}},
{"item": {"id": "bonk", "name": "Bonk", "symbol": "BONK",
"market_cap_rank": 65, "score": 1}},
{"item": {"id": "dogwifcoin", "name": "dogwifhat", "symbol": "WIF",
"market_cap_rank": 80, "score": 2}},
{"item": {"id": "render-token", "name": "Render", "symbol": "RENDER",
"market_cap_rank": 35, "score": 3}},
{"item": {"id": "sui", "name": "Sui", "symbol": "SUI",
"market_cap_rank": 15, "score": 4}},
]
}
DEMO_GLOBAL = {
"data": {
"active_cryptocurrencies": 15320,
"markets": 1120,
"total_market_cap": {"usd": 3250000000000},
"total_volume": {"usd": 125000000000},
"market_cap_percentage": {"btc": 56.2, "eth": 12.8, "usdt": 4.1},
"market_cap_change_percentage_24h_usd": 1.85,
"updated_at": 1741564800,
}
}
# ── API Functions ───────────────────────────────────────────────────
def cg_get(endpoint: str, params: Optional[dict[str, Any]] = None,
max_retries: int = 3) -> Any:
"""Make a GET request to the CoinGecko API with retry on rate limit.
Args:
endpoint: API path (e.g., "/coins/markets").
params: Query parameters.
max_retries: Number of retries on 429 responses.
Returns:
Parsed JSON response.
Raises:
httpx.HTTPStatusError: On non-2xx, non-429 response.
RuntimeError: If max retries exceeded.
"""
url = f"{BASE_URL}{endpoint}"
for attempt in range(max_retries):
resp = httpx.get(url, params=params or {}, headers=HEADERS, timeout=15.0)
if resp.status_code == 429:
wait = 2 ** attempt * 10
print(f" Rate limited, waiting {wait}s (attempt {attempt + 1})...")
time.sleep(wait)
continue
resp.raise_for_status()
return resp.json()
raise RuntimeError(f"Rate limited after {max_retries} retries on {endpoint}")
def fetch_top_coins(n: int = TOP_N) -> list[dict[str, Any]]:
"""Fetch top N coins by market cap."""
return cg_get("/coins/markets", params={
"vs_currency": "usd",
"order": "market_cap_desc",
"per_page": str(n),
"page": "1",
"sparkline": "false",
"price_change_percentage": "24h,7d",
})
def fetch_trending() -> dict[str, Any]:
"""Fetch trending coins from the last 24 hours."""
return cg_get("/search/trending")
def fetch_global() -> dict[str, Any]:
"""Fetch global crypto market statistics."""
return cg_get("/global")
# ── Display Functions ───────────────────────────────────────────────
def display_top_coins(coins: list[dict[str, Any]]) -> None:
"""Print a formatted table of top coins."""
print("\n" + "=" * 75)
print("TOP COINS BY MARKET CAP")
print("=" * 75)
print(f"{'#':>3} {'Symbol':<7} {'Price':>12} {'Market Cap':>14} "
f"{'24h Vol':>12} {'24h %':>7}")
print("-" * 75)
for coin in coins:
rank = coin.get("market_cap_rank", "?")
symbol = coin["symbol"].upper()
price = coin["current_price"]
mcap = coin["market_cap"]
vol = coin["total_volume"]
chg = coin.get("price_change_percentage_24h", 0) or 0
# Format market cap and volume
mcap_str = f"${mcap / 1e9:.1f}B" if mcap >= 1e9 else f"${mcap / 1e6:.0f}M"
vol_str = f"${vol / 1e9:.1f}B" if vol >= 1e9 else f"${vol / 1e6:.0f}M"
if price >= 1000:
price_str = f"${price:,.0f}"
elif price >= 1:
price_str = f"${price:,.2f}"
else:
price_str = f"${price:.6f}"
print(f"{rank:>3} {symbol:<7} {price_str:>12} {mcap_str:>14} "
f"{vol_str:>12} {chg:>+6.1f}%")
def display_trending(data: dict[str, Any]) -> None:
"""Print trending coins."""
print("\n" + "=" * 50)
print("TRENDING COINS (Last 24h)")
print("=" * 50)
for item in data.get("coins", []):
coin = item["item"]
rank = coin.get("market_cap_rank") or "?"
print(f" #{str(rank):>5} {coin['name']} ({coin['symbol']})")
def display_global(data: dict[str, Any]) -> None:
"""Print global market statistics."""
g = data["data"]
total_mcap = g["total_market_cap"]["usd"]
total_vol = g["total_volume"]["usd"]
btc_dom = g["market_cap_percentage"].get("btc", 0)
eth_dom = g["market_cap_percentage"].get("eth", 0)
chg_24h = g.get("market_cap_change_percentage_24h_usd", 0)
print("\n" + "=" * 50)
print("GLOBAL CRYPTO MARKET")
print("=" * 50)
print(f" Total Market Cap: ${total_mcap / 1e12:.2f}T ({chg_24h:+.1f}% 24h)")
print(f" Total 24h Volume: ${total_vol / 1e9:.0f}B")
print(f" BTC Dominance: {btc_dom:.1f}%")
print(f" ETH Dominance: {eth_dom:.1f}%")
print(f" Active Coins: {g.get('active_cryptocurrencies', 0):,}")
print(f" Active Exchanges: {g.get('markets', 0):,}")
# ── Main ────────────────────────────────────────────────────────────
def main() -> None:
"""Run the market data fetch and display pipeline."""
parser = argparse.ArgumentParser(description="Fetch CoinGecko market data")
parser.add_argument("--demo", action="store_true",
help="Use embedded sample data instead of live API")
args = parser.parse_args()
if args.demo:
print("[DEMO MODE] Using embedded sample data\n")
markets = DEMO_MARKETS
trending = DEMO_TRENDING
global_data = DEMO_GLOBAL
else:
print("Fetching data from CoinGecko API...\n")
try:
markets = fetch_top_coins()
time.sleep(2) # Respect rate limit
trending = fetch_trending()
time.sleep(2)
global_data = fetch_global()
except httpx.HTTPStatusError as e:
print(f"API error: {e.response.status_code} — {e.response.text[:200]}")
sys.exit(1)
except httpx.ConnectError:
print("Connection error. Check your internet connection.")
sys.exit(1)
display_global(global_data)
display_top_coins(markets)
display_trending(trending)
print()
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""Fetch historical price data from CoinGecko and compute basic analytics.
Retrieves OHLC or market chart data for a given coin and computes returns,
rolling volatility, maximum drawdown, and summary statistics. Supports
--demo mode with embedded sample data so no API key or network is required.
Usage:
python scripts/historical_analysis.py # Live: SOL, 90 days
python scripts/historical_analysis.py --coin bitcoin --days 365
python scripts/historical_analysis.py --demo # Sample data
Dependencies:
uv pip install httpx pandas numpy
Environment Variables:
COINGECKO_API_KEY: (Optional) CoinGecko Pro API key for higher rate limits.
Free tier (30 calls/min) is used when not set.
"""
import argparse
import math
import os
import sys
import time
from typing import Any, Optional
try:
import httpx
except ImportError:
print("httpx is required. Install with: uv pip install httpx")
sys.exit(1)
try:
import numpy as np
import pandas as pd
except ImportError:
print("pandas and numpy are required. Install with: uv pip install pandas numpy")
sys.exit(1)
# ── Configuration ───────────────────────────────────────────────────
API_KEY = os.getenv("COINGECKO_API_KEY", "")
if API_KEY:
BASE_URL = "https://pro-api.coingecko.com/api/v3"
HEADERS: dict[str, str] = {"x-cg-pro-api-key": API_KEY}
else:
BASE_URL = "https://api.coingecko.com/api/v3"
HEADERS = {}
DEFAULT_COIN = "solana"
DEFAULT_DAYS = 90
# ── Demo Data ───────────────────────────────────────────────────────
# 30 days of synthetic SOL daily prices for demo mode
_DEMO_BASE_TS = 1738368000000 # 2025-02-01 in ms
_DEMO_PRICES = [
195.0, 198.5, 192.3, 189.7, 194.2, 200.1, 205.8, 203.4, 199.6, 196.1,
201.5, 208.3, 212.7, 210.1, 206.4, 198.9, 193.5, 188.2, 191.7, 196.3,
202.8, 209.5, 215.1, 218.4, 213.6, 208.9, 204.2, 210.7, 216.3, 220.8,
]
DEMO_MARKET_CHART = {
"prices": [
[_DEMO_BASE_TS + i * 86400000, p] for i, p in enumerate(_DEMO_PRICES)
],
"market_caps": [
[_DEMO_BASE_TS + i * 86400000, p * 480_000_000]
for i, p in enumerate(_DEMO_PRICES)
],
"total_volumes": [
[_DEMO_BASE_TS + i * 86400000, 2_000_000_000 + (i % 5) * 500_000_000]
for i, p in enumerate(_DEMO_PRICES)
],
}
# Synthetic OHLC candles (30 candles, 4h each for demo)
DEMO_OHLC = [
[_DEMO_BASE_TS + i * 14400000,
p, p + abs(p * 0.02), p - abs(p * 0.015), p + ((-1) ** i) * p * 0.005]
for i, p in enumerate(_DEMO_PRICES)
]
# ── API Functions ───────────────────────────────────────────────────
def cg_get(endpoint: str, params: Optional[dict[str, Any]] = None,
max_retries: int = 3) -> Any:
"""Make a GET request to CoinGecko with retry on rate limit.
Args:
endpoint: API path.
params: Query parameters.
max_retries: Number of retries on 429.
Returns:
Parsed JSON response.
"""
url = f"{BASE_URL}{endpoint}"
for attempt in range(max_retries):
resp = httpx.get(url, params=params or {}, headers=HEADERS, timeout=15.0)
if resp.status_code == 429:
wait = 2 ** attempt * 10
print(f" Rate limited, waiting {wait}s...")
time.sleep(wait)
continue
resp.raise_for_status()
return resp.json()
raise RuntimeError(f"Rate limited after {max_retries} retries")
def fetch_market_chart(coin_id: str, days: int) -> dict[str, Any]:
"""Fetch historical market chart data (price, mcap, volume)."""
return cg_get(f"/coins/{coin_id}/market_chart", params={
"vs_currency": "usd",
"days": str(days),
"interval": "daily",
})
def fetch_ohlc(coin_id: str, days: int) -> list[list[float]]:
"""Fetch OHLC candle data. Days must be 1/7/14/30/90/180/365."""
valid_days = [1, 7, 14, 30, 90, 180, 365]
ohlc_days = min(valid_days, key=lambda d: abs(d - days))
return cg_get(f"/coins/{coin_id}/ohlc", params={
"vs_currency": "usd",
"days": str(ohlc_days),
})
# ── Analysis Functions ──────────────────────────────────────────────
def build_price_dataframe(chart_data: dict[str, Any]) -> pd.DataFrame:
"""Convert market chart response to a DataFrame with daily returns.
Args:
chart_data: Response from /coins/{id}/market_chart.
Returns:
DataFrame with columns: price, market_cap, volume, daily_return.
"""
df_price = pd.DataFrame(chart_data["prices"], columns=["timestamp", "price"])
df_mcap = pd.DataFrame(chart_data["market_caps"], columns=["timestamp", "market_cap"])
df_vol = pd.DataFrame(chart_data["total_volumes"], columns=["timestamp", "volume"])
df = df_price.copy()
df["market_cap"] = df_mcap["market_cap"]
df["volume"] = df_vol["volume"]
df["date"] = pd.to_datetime(df["timestamp"], unit="ms")
df = df.set_index("date").drop(columns=["timestamp"])
df["daily_return"] = df["price"].pct_change()
return df
def build_ohlc_dataframe(ohlc_data: list[list[float]]) -> pd.DataFrame:
"""Convert OHLC response to a DataFrame.
Args:
ohlc_data: List of [timestamp, open, high, low, close].
Returns:
DataFrame with columns: open, high, low, close.
"""
df = pd.DataFrame(ohlc_data, columns=["timestamp", "open", "high", "low", "close"])
df["date"] = pd.to_datetime(df["timestamp"], unit="ms")
df = df.set_index("date").drop(columns=["timestamp"])
return df
def compute_max_drawdown(prices: pd.Series) -> tuple[float, Any, Any]:
"""Compute maximum drawdown from a price series.
Args:
prices: Series of prices.
Returns:
Tuple of (max_drawdown_pct, peak_date, trough_date).
"""
cummax = prices.cummax()
drawdown = (prices - cummax) / cummax
trough_idx = drawdown.idxmin()
peak_idx = prices.loc[:trough_idx].idxmax()
return float(drawdown.min()) * 100, peak_idx, trough_idx
def compute_stats(df: pd.DataFrame) -> dict[str, float]:
"""Compute summary statistics from a price DataFrame.
Args:
df: DataFrame with 'price' and 'daily_return' columns.
Returns:
Dictionary of computed metrics.
"""
returns = df["daily_return"].dropna()
prices = df["price"]
n_days = len(returns)
total_return = (prices.iloc[-1] / prices.iloc[0] - 1) * 100
ann_return = ((1 + total_return / 100) ** (365 / max(n_days, 1)) - 1) * 100
ann_vol = float(returns.std() * math.sqrt(365)) * 100
sharpe = ann_return / ann_vol if ann_vol > 0 else 0.0
max_dd, dd_peak, dd_trough = compute_max_drawdown(prices)
return {
"start_price": float(prices.iloc[0]),
"end_price": float(prices.iloc[-1]),
"high": float(prices.max()),
"low": float(prices.min()),
"total_return_pct": total_return,
"annualized_return_pct": ann_return,
"annualized_volatility_pct": ann_vol,
"sharpe_ratio": sharpe,
"max_drawdown_pct": max_dd,
"max_dd_peak": str(dd_peak.date()) if hasattr(dd_peak, "date") else str(dd_peak),
"max_dd_trough": str(dd_trough.date()) if hasattr(dd_trough, "date") else str(dd_trough),
"avg_daily_volume": float(df["volume"].mean()) if "volume" in df.columns else 0,
"days": n_days,
}
# ── Display Functions ───────────────────────────────────────────────
def display_stats(stats: dict[str, float], coin_id: str) -> None:
"""Print formatted statistics."""
print(f"\n{'=' * 55}")
print(f" HISTORICAL ANALYSIS: {coin_id.upper()}")
print(f" Period: {stats['days']} days")
print(f"{'=' * 55}")
print(f" Start Price: ${stats['start_price']:>12,.2f}")
print(f" End Price: ${stats['end_price']:>12,.2f}")
print(f" Period High: ${stats['high']:>12,.2f}")
print(f" Period Low: ${stats['low']:>12,.2f}")
print(f" {'─' * 45}")
print(f" Total Return: {stats['total_return_pct']:>+11.2f}%")
print(f" Annualized Return: {stats['annualized_return_pct']:>+11.2f}%")
print(f" Annualized Volatility: {stats['annualized_volatility_pct']:>11.2f}%")
print(f" Sharpe Ratio (0% rf): {stats['sharpe_ratio']:>11.2f}")
print(f" Max Drawdown: {stats['max_drawdown_pct']:>11.2f}%")
print(f" Peak: {stats['max_dd_peak']}")
print(f" Trough: {stats['max_dd_trough']}")
if stats["avg_daily_volume"] > 0:
avg_vol = stats["avg_daily_volume"]
vol_str = f"${avg_vol / 1e9:.1f}B" if avg_vol >= 1e9 else f"${avg_vol / 1e6:.0f}M"
print(f" Avg Daily Volume: {vol_str:>12}")
print()
def display_ohlc_summary(df: pd.DataFrame) -> None:
"""Print a summary of OHLC data."""
print(f"\n{'=' * 55}")
print(f" OHLC CANDLE SUMMARY ({len(df)} candles)")
print(f"{'=' * 55}")
print(f" First candle: {df.index[0]}")
print(f" Last candle: {df.index[-1]}")
print(f" Overall range: ${df['low'].min():.2f} — ${df['high'].max():.2f}")
# Show last 5 candles
print(f"\n Last 5 candles:")
print(f" {'Date':<22} {'Open':>10} {'High':>10} {'Low':>10} {'Close':>10}")
print(f" {'─' * 62}")
for idx, row in df.tail(5).iterrows():
date_str = str(idx)[:19]
print(f" {date_str:<22} ${row['open']:>9.2f} ${row['high']:>9.2f} "
f"${row['low']:>9.2f} ${row['close']:>9.2f}")
print()
# ── Main ────────────────────────────────────────────────────────────
def main() -> None:
"""Run the historical analysis pipeline."""
parser = argparse.ArgumentParser(
description="Fetch CoinGecko historical data and compute analytics"
)
parser.add_argument("--coin", default=DEFAULT_COIN,
help=f"CoinGecko coin ID (default: {DEFAULT_COIN})")
parser.add_argument("--days", type=int, default=DEFAULT_DAYS,
help=f"Number of days of history (default: {DEFAULT_DAYS})")
parser.add_argument("--demo", action="store_true",
help="Use embedded sample data instead of live API")
args = parser.parse_args()
coin_id = args.coin
days = args.days
if args.demo:
print("[DEMO MODE] Using embedded sample data (SOL, 30 days)\n")
chart_data = DEMO_MARKET_CHART
ohlc_data = DEMO_OHLC
coin_id = "solana (demo)"
else:
print(f"Fetching {days}-day history for '{coin_id}' from CoinGecko...\n")
try:
chart_data = fetch_market_chart(coin_id, days)
time.sleep(2) # Respect rate limit
ohlc_data = fetch_ohlc(coin_id, days)
except httpx.HTTPStatusError as e:
if e.response.status_code == 404:
print(f"Coin '{coin_id}' not found. Use /search to find the correct ID.")
print("See references/id_mapping.md for help.")
else:
print(f"API error: {e.response.status_code} — {e.response.text[:200]}")
sys.exit(1)
except httpx.ConnectError:
print("Connection error. Check your internet or try --demo mode.")
sys.exit(1)
# Build DataFrames
df = build_price_dataframe(chart_data)
df_ohlc = build_ohlc_dataframe(ohlc_data)
# Compute and display stats
stats = compute_stats(df)
display_stats(stats, coin_id)
display_ohlc_summary(df_ohlc)
# Rolling volatility (7-day window)
if len(df) >= 7:
df["rolling_vol_7d"] = df["daily_return"].rolling(7).std() * math.sqrt(365) * 100
recent_vol = df["rolling_vol_7d"].dropna()
if len(recent_vol) > 0:
print(f" 7-Day Rolling Volatility (annualized):")
print(f" Current: {recent_vol.iloc[-1]:.1f}%")
print(f" Average: {recent_vol.mean():.1f}%")
print(f" Min: {recent_vol.min():.1f}%")
print(f" Max: {recent_vol.max():.1f}%")
print()
if __name__ == "__main__":
main()
Related skills
FAQ
Does the CoinGecko API need an API key?
No. The free tier requires no key and supports 30 calls per minute; a Pro key raises the limit and uses a different base URL and header.
How far back does historical data go?
Daily and hourly OHLCV going back years, but not minute-level; OHLC windows are limited to 1/7/14/30/90/180/365 days.