How to Scrape Crypto Prices: API First, Browser When You Have To

June 26, 2026 17 min read

Scraping crypto prices with Python: CoinGecko API, CCXT, and a cloud browser pulling Bitcoin prices into structured JSON

By the end of this guide, you'll have three working ways to get crypto prices into Python: a free API call, an exchange ticker via CCXT, and a cloud browser that pulls live prices off a public web page as structured JSON. You'll know exactly which one to reach for and when.

Here's the honest version most scraping tutorials skip: for crypto prices, you usually should not scrape at all. CoinGecko, CoinMarketCap, and every major exchange expose prices through APIs, often on a free tier. An API hands you clean JSON. Scraping hands you HTML, anti-bot challenges, and selectors that break next month.

So why write a scraping guide? Because the API path runs out. The data you want sits only on a dashboard. The API tier that includes it costs $100+/month for a one-off pull. Or you need to compare ten sources and three of them have no API. That's when a browser earns its place.

We tested every site in this guide live before publishing. Some scrape fine. Some are walled off behind Cloudflare even with residential proxies. We'll show you the results so you don't waste a weekend on a page that will never load.

What you'll build in this guide:

  • A free crypto price fetcher using the CoinGecko public API in Python
  • Exchange-specific prices (Coinbase, Kraken) with the CCXT library
  • A live price scraper that pulls structured JSON from a public crypto page with Browserbeam
  • A first-hand audit of which crypto sites are scrapeable in 2026 and which are not
  • A crypto price tracker that polls, stores history in SQLite, and runs on a schedule
  • A price alert that emails or pings Slack when a coin crosses your threshold

TL;DR: For most crypto price needs, call the CoinGecko API (free, keyless, JSON) or use CCXT for exchange prices. Scrape only when the data is HTML-only or locked behind an expensive tier. When you do scrape, JavaScript rendering and Cloudflare will block plain requests, so use a real browser. Browserbeam returns structured JSON from a single API call, and our live tests show it reads public crypto pages that simple scrapers cannot.


Should You Scrape Crypto Prices or Use an API?

Start with the API. For live prices, historical candles, and market caps, a crypto price API in Python just works. You get JSON, no HTML parsing, no broken CSS selectors, and no proxy bills. Reach for scraping only when the API cannot give you what you need.

The Decision Matrix

Use this to pick an approach before you write a line of code.

Your goal Best approach Why
Live spot price CoinGecko /simple/price API Free and keyless
Multiple coins at once CoinGecko API (comma-separated ids) One call, all coins
Exchange-specific price CCXT library Unified API across 100+ exchanges
Historical OHLC candles CoinGecko or exchange API Structured, paginated
Data only shown on a web page Scrape with a cloud browser No endpoint exists
Many sources, some without APIs API where possible, scrape the rest Don't scrape what you can fetch
A page behind Cloudflare or heavy JS Cloud browser with the right proxy Plain requests gets a 403

When an API Wins

If the number you want is a current or historical price for a known coin, an API is faster, cleaner, and more reliable than any scraper. APIs return typed JSON, version their fields, and rarely change overnight. A scraper depends on someone else's page layout, which can shift without warning.

The free tiers are generous. CoinGecko's keyless endpoint allows roughly 10 to 30 calls per minute per IP, which covers most hobby projects and many production ones.

When Scraping Wins

Scraping earns its keep in three cases: the data lives only in HTML, the API that includes it is too expensive for your use, or you're aggregating sources where not everyone offers an API. We cover each below. The key idea: scrape as a fallback, not a default.


Get Crypto Prices with the CoinGecko API in Python

Before we scrape anything, let's do it the easy way. This is the fast path you should try first.

The Keyless Public Endpoint

The CoinGecko /simple/price endpoint needs no API key. Install requests and make one call.

pip install requests
import requests

r = requests.get(
    "https://api.coingecko.com/api/v3/simple/price",
    params={"vs_currencies": "usd", "ids": "bitcoin"},
)
print(r.json())
# {'bitcoin': {'usd': 58995}}

That's the whole thing. No browser, no proxy, no parsing. We ran this and got {'bitcoin': {'usd': 58995}} back in under a second.

Add Market Cap and Multiple Coins

The same endpoint takes a comma-separated list of coin ids and optional flags for market cap and 24-hour change.

import requests

r = requests.get(
    "https://api.coingecko.com/api/v3/simple/price",
    params={
        "vs_currencies": "usd",
        "ids": "bitcoin,ethereum",
        "include_market_cap": "true",
    },
)
print(r.json())
# {'bitcoin': {'usd': 58995, 'usd_market_cap': 1183018954037.78},
#  'ethereum': {'usd': 1528.65, 'usd_market_cap': 184560851908.55}}

For higher rate limits and historical data, sign up for a CoinGecko API key and read the official CoinGecko API docs. CoinMarketCap offers a similar API if you prefer their data.

Exchange-Specific Prices with CCXT

Sometimes you want the price on a specific exchange, not an aggregate. The CCXT library gives you one consistent interface across more than 100 exchanges.

pip install ccxt
import ccxt

kraken = ccxt.kraken()
coinbase = ccxt.coinbase()

print("Kraken BTC/USD:", kraken.fetch_ticker("BTC/USD")["last"])
print("Coinbase BTC/USD:", coinbase.fetch_ticker("BTC/USD")["last"])
# Kraken BTC/USD: 59090.3
# Coinbase BTC/USD: 59067.18

We ran this against live exchanges and got real ticker data back. CCXT handles the per-exchange differences, so your code stays the same whether you query Kraken, Coinbase, or Binance. If an API covers your need, stop here. The rest of this guide is for when it doesn't.


When Scraping Crypto Prices Still Makes Sense

Scraping is the fallback, but it's a real one. Here are the cases where reaching for a browser beats fighting an API.

Data That Lives Only on a Web Page

Some sites publish numbers they don't expose through any API. BitInfoCharts, for example, shows per-exchange Bitcoin prices, transaction stats, and wealth distribution on one dense page, with no public API. If you want that data, the page is the only source.

Comparing Many Sources at Once

When you aggregate prices across exchanges, dashboards, and aggregators, some will have APIs and some won't. Rather than maintain two completely separate code paths, a browser-based scraper can read any public page with the same extraction pattern, filling the gaps where APIs don't exist.

Avoiding Expensive API Tiers

Say you want Bitcoin's daily close for a two-week window in 2020 for a one-off backtest. That historical range may sit behind a $100+/month analyst plan. If the same data renders on a public chart page, scraping it once is cheaper than a month's subscription you'll cancel immediately. Just respect the site's terms and rate limits, which we cover below.


Scrape Live Crypto Prices with Browserbeam

When you do need to scrape, the hard parts are JavaScript rendering and anti-bot defenses. A cloud browser API handles both. You send a URL and an extraction schema. You get structured JSON back. No browser binary, no proxy rotation script, no CAPTCHA solver to babysit.

We'll scrape live Bitcoin prices from BitInfoCharts, the no-API site from the last section.

One Request, Structured JSON

Here's a complete crypto price scraper in four languages. Replace YOUR_API_KEY and run it. Don't have a key yet? Create a free Browserbeam account, you get 5,000 credits and no credit card is required.

curl -s -X POST https://api.browserbeam.com/v1/sessions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://bitinfocharts.com/",
    "proxy": { "kind": "datacenter" },
    "steps": [
      {
        "extract": {
          "btc_usd": "ai >> the main Bitcoin price in USD",
          "coinbase": "ai >> the Coinbase Bitcoin price in USD",
          "kraken": "ai >> the Kraken Bitcoin price in USD",
          "market_cap": "ai >> the Bitcoin market capitalization"
        }
      },
      { "close": {} }
    ]
  }' | jq '.extraction'

Running the Python version returned real data:

{
  "btc_usd": "59,449.98",
  "coinbase": "59,596.32",
  "kraken": "59,635.9",
  "market_cap": "$1,191,577,939,248"
}

One call, structured JSON, no BeautifulSoup. Compare that to a typical requests + BeautifulSoup scraper that splits text on = and : to pull the same numbers out of a giant table cell.

How the Extraction Schema Works

The extract step takes a schema. Each key is a field you want, and each value is a selector. Browserbeam runs the page in a real browser, applies your selectors, and returns the result on session.extraction. For lists, you wrap the fields in an array with a _parent selector for the repeating container, exactly like the structured web scraping guide shows.

AI Selectors vs CSS Selectors

We used ai >> the main Bitcoin price in USD instead of a CSS selector. Here's why. On BitInfoCharts, the main price is a bare text node inside a table cell, with per-exchange prices in nested span tags. A precise CSS selector is fiddly and brittle. An AI selector describes the value in plain English, and Browserbeam resolves it to a cached CSS selector automatically, so repeat runs stay fast.

CSS selectors are still the right call when the structure is clean. On a page with stable classes, .price >> text is direct and predictable. Use CSS when the markup cooperates and AI selectors when it fights you. You can mix both in the same schema.


Which Crypto Sites Are Actually Scrapeable in 2026

Most tutorials show you code that works on their hand-picked example and stay quiet about the sites that block you. We ran live tests so you know what to expect. Here's the original data.

Our Test Setup

On June 26, 2026, we pointed Browserbeam at the crypto pages people most often try to scrape. We tested with a datacenter proxy first (cheaper) and escalated to residential (pricier, harder to block) when datacenter failed. Each test either returned the price data or hit a wall.

Results

Target Proxy Result What happened
bitinfocharts.com (live prices) Datacenter Works Full price table loaded; AI extract returned BTC, Coinbase, Kraken, Bitfinex prices
bitinfocharts.com (retry) Datacenter Flaky Some datacenter IPs hit a Cloudflare "Just a moment" challenge; a retry cleared it
coingecko.com historical data page Datacenter Blocked HTTP 403, Cloudflare challenge
coingecko.com historical data page Residential Blocked Still HTTP 403, even on a residential IP
coinmarketcap.com historical data page Residential Blocked Page shell loaded, prices never hydrated, only a spinner
CoinGecko /simple/price API None Works JSON in under a second

What the Results Mean

Three takeaways from the data:

  1. BitInfoCharts is a genuinely scrapeable source. A datacenter proxy is enough most of the time, so you don't need to burn pricier residential bandwidth. When a single IP trips Cloudflare, a retry on a fresh IP usually clears it.
  2. CoinGecko and CoinMarketCap historical pages are bad scrape targets. Even a residential proxy got a 403 from CoinGecko, and CoinMarketCap's table never rendered. These sites want you on their API, and they enforce it. Fighting them is wasted effort when the API exists.
  3. The API is the fastest path by far. The CoinGecko API answered in under a second with zero anti-bot friction. If your data is in the API, that's where you belong.

The lesson: pick scrape targets that don't actively block you, and use the API for the ones that do. Honesty about this saves you days.


Why Your Crypto Scraper Gets Blocked

When a crypto scraper fails, it's almost always one of two reasons. Knowing which one tells you how to fix it.

Cloudflare and Anti-Bot Defenses

If you get a 403 or a "Just a moment" page, the site is using anti-bot protection like Cloudflare. It detected that your request didn't come from a real browser. Swapping your User-Agent string rarely helps, because these systems check TLS fingerprints, JavaScript execution, and behavioral signals, not just headers. You need a real browser, and sometimes a residential IP. As our tests showed, even that isn't always enough.

JavaScript-Rendered Prices

If you get a 200 response but the price is missing from the HTML, the data is rendered by JavaScript after the page loads. Plain requests only sees the initial HTML before any script runs, so the number simply isn't there yet. This is the CoinMarketCap case: the shell loads, then a script fetches and injects prices. You need a browser that executes JavaScript and waits for the content. See our deep dive on scraping JavaScript-heavy sites for the full pattern.

Datacenter vs Residential IPs

Datacenter IP ranges are well known and easy to flag. Residential IPs look like ordinary home connections and pass more checks. The tradeoff is cost: residential bandwidth is roughly ten times pricier. Start with datacenter, escalate to residential only when you get blocked. Our residential vs datacenter proxies guide breaks down when each is worth it.


Build a Crypto Price Tracker in Python

Now let's turn a one-off fetch into a tracker that records prices over time. We'll use the CoinGecko API for the data because it's free and reliable, then store history and schedule it. The same structure works if you swap the API call for a Browserbeam scrape.

Step 1: Fetch and Store Prices

A single function fetches the current price and returns it with a timestamp.

import requests
from datetime import datetime, timezone

def fetch_price(coin_id="bitcoin", vs="usd"):
    r = requests.get(
        "https://api.coingecko.com/api/v3/simple/price",
        params={"ids": coin_id, "vs_currencies": vs},
        timeout=10,
    )
    r.raise_for_status()
    price = r.json()[coin_id][vs]
    return {
        "coin": coin_id,
        "price": float(price),
        "fetched_at": datetime.now(timezone.utc).isoformat(),
    }

Step 2: Store History in SQLite

Append every reading to a SQLite table so you keep a full history, not just the latest value.

import sqlite3

DB_PATH = "crypto_prices.db"

def init_db(db_path=DB_PATH):
    conn = sqlite3.connect(db_path)
    conn.execute(
        """CREATE TABLE IF NOT EXISTS price_history (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            coin TEXT NOT NULL,
            price REAL NOT NULL,
            fetched_at TEXT NOT NULL
        )"""
    )
    conn.commit()
    conn.close()

def save_price(reading, db_path=DB_PATH):
    conn = sqlite3.connect(db_path)
    conn.execute(
        "INSERT INTO price_history (coin, price, fetched_at) VALUES (?, ?, ?)",
        (reading["coin"], reading["price"], reading["fetched_at"]),
    )
    conn.commit()
    conn.close()

Storing the price as a REAL, not a string, matters. You cannot compare "59,449.98" to a threshold, but you can compare 59449.98. We cover this exact pitfall in the price monitoring bot guide.

Step 3: Schedule It

Run one fetch-and-store cycle, then let the operating system schedule repeats. A cron job is more reliable than a Python while True loop, which dies if the process crashes.

def run_once(coin_id="bitcoin"):
    init_db()
    reading = fetch_price(coin_id)
    save_price(reading)
    print(f"{reading['fetched_at']}: {coin_id} = {reading['price']}")

if __name__ == "__main__":
    run_once()

Add a crontab entry to run it every minute:

* * * * * /usr/bin/python3 /path/to/tracker.py >> /var/log/crypto.log 2>&1

Respect the API rate limits. CoinGecko's keyless tier allows roughly 10 to 30 calls per minute, so once a minute is comfortable.


Add a Price Alert

A tracker is more useful when it tells you something happened. Let's add an alert that fires when the price crosses a threshold you set.

Threshold Detection

Compare the new price against your target and decide whether to notify.

def crossed_below(price, threshold):
    return price < threshold

def crossed_above(price, threshold):
    return price > threshold

Keep the logic simple and explicit. A buy alert fires when the price drops below your target. A sell alert fires when it climbs above.

Slack and Email Alerts

Slack is the fastest channel for a developer alert. Create an incoming webhook and post to it.

import requests

SLACK_WEBHOOK_URL = "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"

def alert_slack(coin, price, threshold):
    text = f":rotating_light: {coin} is {price}, below your {threshold} target."
    resp = requests.post(SLACK_WEBHOOK_URL, json={"text": text}, timeout=10)
    resp.raise_for_status()

To avoid a flood of alerts when the price hovers near your threshold, send one notification and then pause checks for an hour before alerting again. The same de-duplication pattern works with email via SMTP as a fallback channel when Slack is down.


Common Mistakes When Scraping Crypto Prices

Five mistakes account for most failed crypto scrapers. Here's how to avoid them.

1. Scraping When a Free API Exists

The most common mistake is reaching for BeautifulSoup before checking for an API. CoinGecko, CoinMarketCap, and every major exchange expose prices through APIs, often free. Scraping a site that offers an API adds fragility for no benefit. Check for an API first, every time.

2. Ignoring robots.txt and Terms of Service

Scraping public data is generally fine, but each site sets its own rules. Read the target's robots.txt and terms before you scrape, and never scrape content behind a login. This is both an ethical and a practical concern, since aggressive scraping gets your IP banned.

3. Polling Too Aggressively

Hitting an API or page every few seconds gets you rate-limited or blocked, and crypto prices rarely move enough to justify it. Once a minute is plenty for most trackers. If you need second-level freshness for trading, use a websocket feed from an exchange, not a scraper.

4. Storing Prices as Strings

Saving "59,449.98 USD" instead of 59449.98 makes every comparison impossible. Strip the currency symbol and commas, convert to a float, and store the numeric value with its currency in a separate column. A regex is more reliable than chained .replace() calls.

5. Not Closing Browser Sessions

When you scrape with a cloud browser, every open session consumes resources and keeps the billing clock running. Always close the session when you're done. In the Browserbeam SDK, a { "close": {} } step or session.close() releases it immediately.


Cost Comparison: API vs Scraping vs Scraping APIs

Cost should drive your choice as much as capability. Here's how the approaches stack up for crypto prices.

Approach Cost Handles JS / anti-bot Best for
CoinGecko / exchange API Free tier, then paid Not needed Prices, market caps, history
Plain requests + BeautifulSoup Free No Static, unprotected pages only
Browserbeam (datacenter) Low per session Yes (JS) Public pages like BitInfoCharts
Browserbeam (residential) Higher per session Yes (JS + tougher anti-bot) Pages that block datacenter IPs
Credit-based scraping APIs 1 to 75 credits per call Yes Teams who prefer prepaid credits

The pattern that wins on cost: use the free API for anything it covers, scrape public pages with a datacenter proxy, and only pay for residential when a site actively blocks you. Don't pay residential rates for a page a datacenter IP can read.


This is not legal advice, and you should consult a lawyer for your specific case. As a general guide, scraping publicly available data sits in a legal gray area that most courts have treated leniently, while scraping anything behind a login or paywall is far riskier and can breach a site's terms directly.

For crypto prices specifically, you have a cleaner option most of the time: use an API. Because CoinGecko, CoinMarketCap, and the exchanges all publish prices through documented APIs, you can often avoid the gray area entirely. When you do scrape, scrape only public pages, read the robots.txt, follow rate limits, and don't hammer the site. Being a polite scraper keeps you out of trouble and keeps the page available for everyone.


Frequently Asked Questions

How do I scrape crypto prices in Python?

First check for an API. The CoinGecko /simple/price endpoint returns prices as JSON with no key required. If the data is HTML-only or the page blocks plain requests, use a cloud browser like Browserbeam that renders JavaScript and returns structured data from one API call.

Is the CoinGecko API still free?

Yes. As of 2026, CoinGecko offers a keyless public API for basic price lookups, rate-limited by IP to roughly 10 to 30 calls per minute. A free demo plan adds more endpoints with attribution required, and paid plans add historical data and higher limits.

Can I scrape CoinMarketCap with Python?

You can scrape public CoinMarketCap pages, but expect resistance. In our tests the historical data page rendered prices with JavaScript and never populated for a simple scraper. You'll need a real browser, and even then the cleaner path is the CoinMarketCap API.

Why does my crypto scraper return empty data?

Two common causes. Either you were blocked (a 403 or challenge page) for looking like a bot, or the prices are rendered by JavaScript and your HTTP client only sees the empty shell. The fix for both is a real browser that executes JavaScript, plus a better proxy if you're being blocked.

Do I need a proxy to scrape crypto prices?

It depends on the site. Some public crypto pages serve any IP fine, so no proxy is needed. Others flag datacenter IPs and require a residential proxy. Start with a datacenter proxy and switch to residential only if you get blocked.

How do I get real-time crypto prices in Python?

For near real-time data, poll a price API once a minute, which is what most trackers need. For true low-latency data used in trading, connect to an exchange websocket feed rather than scraping or polling. CCXT supports websocket streams for many exchanges.

Should I use an API or scrape for crypto prices?

Use an API whenever it covers your data. It's faster, cleaner, and more stable than scraping. Scrape only when the data lives only on a web page, the API tier that includes it is too expensive, or you're aggregating sources where some lack an API.

What is the best way to track Bitcoin price over time?

Poll a free price API on a schedule and append each reading to a database like SQLite with a timestamp. Store the price as a number, not a string, so you can query history and detect changes. Add a threshold alert to get notified when the price crosses a level you care about.


Start Tracking Crypto Prices

You now have the full toolkit: a free CoinGecko API call for live prices, CCXT for exchange-specific data, a Browserbeam scraper for public pages with no API, and a tracker with alerts to tie it together. More importantly, you know which tool to reach for, backed by live tests instead of guesses.

The honest summary: for crypto prices, try the API first, scrape public pages when the API falls short, and skip the sites that fight back when their API has what you need. That decision, made early, saves the most time.

Start with the CoinGecko snippet to confirm your setup, then point the Browserbeam scraper at a public page you care about. For a deeper build, extend the tracker into a full price monitoring bot, or read the Python web scraping guide for the broader toolchain. What coin will you track first?

You might also like:

How to Build a Price Monitoring Bot in Python

Build a Python price monitoring bot step by step. Scrape prices, store history in SQLite, detect changes, and send Slack alerts. Working code included.

18 min read May 04, 2026

Give your AI agent a faster, leaner browser

Structured page data instead of raw HTML. Your agent processes less, decides faster, and costs less to run.

Stability detection built in
Fraction of the payload size
Diffs after every action
No credit card required. 5,000 free credits included.