Quick answer: A CoinGecko API scraper feeds a list of coin IDs, rotates TLS fingerprints and proxy sessions to stay within CoinGecko's free-tier rate limits (~10 requests per minute), and returns one clean, typed JSON row per coin with 26 fields β price, market cap, ATH/ATL, supply, and sentiment. The Apify Actor below costs $0.002 per coin (~$2.00 per 1,000), handles the rate-limiting for you, and lands the data wherever your stack lives.
If you've built anything crypto-adjacent β a portfolio dashboard, an alert bot, a strategy backtest β you've run into CoinGecko's free tier. The data is excellent: prices, market caps, circulating vs total supply, all-time highs and lows, community sentiment, even GitHub activity. The problem isn't the data; it's getting it at scale without triggering a rate-limit cascade.
A for loop over your coin list, one requests.get per coin, works fine for five coins and falls apart at fifty. By the time you've added backoff, fingerprint rotation, proxy management, and typed output, you've spent half a day on plumbing you'd rather not maintain. Here's how I wrapped all of that into a single API call.
What is CoinGecko? π
CoinGecko is an independent cryptocurrency data aggregator, founded in 2014, that tracks over 14,000 digital assets across 900+ exchanges. It publishes market data β prices, volumes, supply figures, ATH/ATL records, community sentiment β through a public REST API (v3) with a free "Demo" tier and paid Pro plans. Unlike exchange APIs, CoinGecko aggregates across venues, so you get one normalized view regardless of where a coin trades.
For portfolio tracking, research, alerting, or tax reporting, CoinGecko is the reference source: public dataset, deep coverage, documented API. The catch is throughput β the free Demo tier enforces a hard cap that makes bulk pulls slow and fragile without deliberate rate-limit management.
Does CoinGecko have an API? π
Yes β CoinGecko publishes an official v3 REST API. This Actor uses the /coins/{id} endpoint to fetch the per-coin detail payload. The free Demo tier is rate-limited to roughly 10 requests per minute from a single IP; the Pro tier lifts that significantly. The API is documented and stable, but managing those rate limits across concurrent workers β when you're pulling hundreds of coins and can't afford to serialize them β is where most scripts break down.
This Actor adds the plumbing the raw API doesn't: fingerprint rotation, proxy sessions, backoff on 429s, and clean validated output. You bring the coin list; we bring the resilience.
What the data looks like π€
Each coin comes back as one flat, typed row. Every field below is from models.py:
{
"coin_id": "bitcoin",
"symbol": "btc",
"name": "Bitcoin",
"image_url": "https://coin-images.coingecko.com/coins/images/1/large/bitcoin.png",
"current_price_usd": 71250.32,
"current_price_vs": 65120.10,
"vs_currency": "eur",
"price_change_24h_pct": 2.41,
"price_change_7d_pct": -1.18,
"price_change_30d_pct": 14.73,
"market_cap_usd": 1408000000000,
"market_cap_rank": 1,
"total_volume_usd": 28540000000,
"circulating_supply": 19700000,
"total_supply": 21000000,
"max_supply": 21000000,
"ath_usd": 73738.00,
"ath_date": "2024-03-14T07:10:36.635Z",
"atl_usd": 67.81,
"atl_date": "2013-07-06T00:00:00.000Z",
"sentiment_up_pct": 74.29,
"exchanges_count": 58,
"github_stars": null,
"github_forks": null,
"coingecko_url": "https://www.coingecko.com/en/coins/bitcoin",
"scraped_at": "2026-05-31T09:00:00+00:00"
}
Twenty-six fields per coin, Pydantic-validated before they reach your dataset. github_stars and github_forks populate when you enable includeDeveloperData. Everything else β prices, supply, ATH/ATL, sentiment, exchange count β comes through on the default configuration.
The naive approach (and why it falls apart) β οΈ
The immediate instinct for anyone who's glanced at the CoinGecko API docs:
import httpx, asyncio
async def fetch_coins(coin_ids):
async with httpx.AsyncClient() as c:
for coin_id in coin_ids:
r = await c.get(f"https://api.coingecko.com/api/v3/coins/{coin_id}")
yield r.json()
Works for three coins on your laptop. Here's where it breaks:
1. Rate limits shut you down without ceremony. The Demo tier enforces roughly 10 requests per minute. A list of 100 coins serialized naively takes 10 minutes minimum and still 429s if your timing slips. We sleep 30 seconds on a 429, retry once, and warn loudly if it persists, so partial success surfaces rather than silently producing an empty dataset.
2. TLS fingerprinting tightens under load. Hit the endpoint at volume from a datacenter IP with Python's default TLS stack and the response profile changes. We rotate through Chrome 131, Chrome 124, Firefox 147, and Safari 180 TLS fingerprints via curl-cffi, picked randomly per session, so the handshake looks like a real browser even under concurrent load.
3. Concurrency without a semaphore blows the limit. Even asyncio.gather() with ten concurrent tasks exceeds the free-tier quota instantly. We thread a Semaphore around the fetch so parallel lookups stay within the configured ceiling β default 2, configurable up to 32 for Pro-key holders.
4. Null fields for low-coverage coins. Smaller coins return incomplete payloads with missing keys or null market-data fields. A naΓ―ve data["market_data"]["current_price"]["usd"] raises KeyError or TypeError. Our _build_row guards every field with .get() chains and Pydantic absorbs the rest β nulls land as typed None values rather than crashing the run.
We rotate residential proxies through Apify Proxy on a block, retry with exponential backoff on 408/429/5xx, and hand back Pydantic-validated rows. No data, no charge β only the small actor-start warm-up fee fires if the run produces zero items.
The Actor βοΈ
Store listing: apify.com/DevilScrapes/coingecko-crypto-scraper
Paste a list of coin IDs in the Apify Console and click Start, or drive it from Python:
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("DevilScrapes/coingecko-crypto-scraper").call(
run_input={
"coinIds": ["bitcoin", "ethereum", "solana", "cardano", "chainlink"],
"vsCurrency": "eur",
"includeDeveloperData": False,
"concurrency": 2,
}
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item["name"], item["current_price_usd"], item["price_change_24h_pct"])
Key input fields:
| Field | Default | Notes |
|---|---|---|
coinIds |
["bitcoin", "ethereum", "solana"] |
CoinGecko slug from the URL β e.g. uniswap, the-graph
|
vsCurrency |
"eur" |
Secondary quote currency alongside USD |
includeDeveloperData |
false |
Adds GitHub stars / forks for coins with linked repos |
apiKey |
β | CoinGecko Pro key β lifts the 10 req/min Demo ceiling |
concurrency |
2 |
Keep at 2 on Demo; raise to 8+ with a Pro key |
Coin IDs are the lowercase slugs in the URL β bitcoin is /en/coins/bitcoin, the-graph is /en/coins/the-graph. The CoinGecko coin list endpoint returns every valid ID if you need to enumerate them first.
Use cases π‘
Portfolio tracker. Schedule a daily run with your watch list β 20 coins, vsCurrency: "eur", concurrency: 2. Each run produces one row per coin with price in USD and EUR, 24h/7d/30d change, and market cap. Pipe the dataset to a Google Sheet via the Apify API or a Make webhook. Cost: 20 coins Γ $0.002 = $0.04 per day.
Alert bot. Run every hour, diff price_change_24h_pct against the previous run, and fire a Slack notification when any coin moves more than 10% in either direction. The scraped_at ISO timestamp on each row makes the diff deterministic.
Trading-strategy backtest data. Pull 50 coins by market cap rank, store daily snapshots in a named Apify dataset, then read back the stored runs for strategy simulation. market_cap_rank, total_volume_usd, and the price-change fields give you the features most trend-following strategies need.
Tax reporting. Capital-gains math needs a daily closing price for every asset held. One run per day per asset, stored with scraped_at, gives you an auditable price history without a dedicated pricing-feed subscription. At $0.002 per coin, 10 coins Γ 365 days = $7.30/year.
Developer-activity screening. Enable includeDeveloperData, then filter coins where github_stars > 500 and price_change_30d_pct > 0 β a rough proxy for active development plus market momentum. CoinGecko sources the GitHub stats from each project's linked repository.
Pricing β exact numbers π°
Pay-per-event. You pay for coins you receive, nothing for coins that error out.
| Event | Price |
|---|---|
actor-start (once per run) |
$0.005 |
result (per coin in dataset) |
$0.002 |
| Pull | Cost |
|---|---|
| 10 coins | $0.025 |
| 100 coins | $0.205 |
| 1,000 coins | $2.005 |
| 5,000 coins | $10.005 |
Apify's $5 free trial credit β no credit card β covers your first 2,497 coins. This Actor uses the free Demo tier by default, so you get well-structured, well-managed access to the same data without a subscription.
The technically interesting bit π§
CoinGecko's /coins/{id} endpoint packs market data into nested dicts with currency-keyed sub-objects β market_data.current_price.usd, market_data.ath.usd, and so on. Low-coverage coins return partial payloads: some fields have the outer key but null, some skip the key entirely, some return an empty dict {} for the inner object. A straightforward data["market_data"]["current_price"]["usd"] fails unpredictably across that variance.
The _build_row function in scraper.py guards every multi-level access with .get() chains β (market.get("ath") or {}).get("usd") β which safely returns None for any missing level without raising. Pydantic then validates the final ResultRow, coercing None to JSON null on model_dump(mode="json"). Your rows stay consistently typed regardless of which level of the CoinGecko response is missing, and the run never crashes on a partially-covered altcoin.
Limitations π§
-
Demo tier is ~10 req/min. Large coin lists (500+) are slow without a CoinGecko Pro key. Attach one via
apiKeyto lift the ceiling. -
Some fields are null for low-coverage coins. Smaller, less-traded coins often have null
market_cap_usd,total_volume_usd, orexchanges_count. The fields are present in the schema β just typed asnumber | null. - No historical OHLC. This Actor fetches current market state; historical price series use a different CoinGecko endpoint and are out of scope here.
- CoinGecko updates every ~60 seconds. Not a real-time tick feed. For sub-second data, use an exchange WebSocket.
-
exchanges_countreflects CoinGecko's ticker window. It's the number of distinct exchanges in the tickers CoinGecko returns for the coin, which may not be every venue listing it globally. Treat it as a coverage signal, not an exhaustive count.
FAQ β
Is scraping CoinGecko legal?
This Actor calls CoinGecko's own documented public API β the same endpoint CoinGecko publishes in its docs. It doesn't bypass authentication, doesn't scrape rendered HTML, and respects rate limits by backing off on 429s. CoinGecko's Terms of Service permit API access for non-commercial and commercial use subject to rate limits. Check your own use case against their terms.
Can I export to Google Sheets or a data warehouse?
Yes. Export CSV/Excel/JSON directly from the Apify Console dataset view. Use a webhook on ACTOR.RUN.SUCCEEDED to push each run's results into Make, Zapier, or n8n. Pull the Apify dataset API directly from any pipeline that can make an HTTP call.
Is there a CoinGecko API?
Yes β this Actor wraps CoinGecko's v3 REST API. If you prefer to call it directly, the documentation is public. The Actor adds rate-limit management, fingerprint rotation, proxy routing, and Pydantic-typed output on top of the raw API surface.
Why does my run return fewer coins than I requested?
A coin ID that doesn't exist returns a 404, which the scraper logs and skips. The usual cause is a typo in the slug β the-graph not thegraph, uniswap not uni. Check the exact slug in the CoinGecko URL.
Try it
apify.com/DevilScrapes/coingecko-crypto-scraper
Free $5 trial credit, no credit card required. Drop in ["bitcoin", "ethereum", "solana"], click Start, and you'll have your first three rows in seconds. Need a larger coin universe, or a field that isn't here yet? Drop a comment β the roadmap is driven by what people build with this.
Built by Devil Scrapes β Apify Actors for builders who'd rather not debug rate-limit waterfalls at 2 a.m. Pay-per-event, transparent pricing, no junk fields. π
Top comments (0)