Firecrawl
Web scraping API for LLMs — converts any website to clean markdown. Current version is 4.21.0 (Mar 2026). Two separate PyPI packages: firecrawl-py (official SDK) and firecrawl (different package). Class renamed from FirecrawlApp to Firecrawl in v2. Method names changed: scrape_url() → scrape(), crawl_url() → crawl(). Extreme API churn — 0.x to 4.x in one year.
Warnings
- breaking FirecrawlApp class renamed to Firecrawl in v2.0. from firecrawl import FirecrawlApp raises ImportError. All v1 tutorial code is broken.
- breaking Method names changed in v2: scrape_url() → scrape(), crawl_url() → crawl(), check_crawl_status() → get_crawl_status(). All old method calls raise AttributeError.
- breaking Extreme version churn: 0.x → 1.x → 2.x → 3.x → 4.x all within ~1 year. Each major version has breaking API changes. Code from tutorials more than a few months old is likely broken.
- gotcha pip install firecrawl installs a DIFFERENT package — not the official Firecrawl SDK. The correct install is pip install firecrawl-py.
- gotcha API response fields use camelCase in the Firecrawl REST API (sourceURL, ogTitle) but the Python SDK auto-converts to snake_case (source_url, og_title). Raw API response and SDK response field names differ.
Install
-
pip install firecrawl-py -
pip install firecrawl
Imports
- Firecrawl
from firecrawl import Firecrawl from firecrawl.types import ScrapeOptions firecrawl = Firecrawl(api_key='fc-YOUR_API_KEY') # Scrape (v2+ method name) result = firecrawl.scrape( 'https://example.com', formats=['markdown', 'html'] ) print(result.markdown) # Crawl (v2+ method name) crawl = firecrawl.crawl( 'https://example.com', limit=100, scrape_options=ScrapeOptions(formats=['markdown']) )
Quickstart
from firecrawl import Firecrawl
from firecrawl.types import ScrapeOptions
import os
# API key from env or direct
firecrawl = Firecrawl(api_key=os.environ.get('FIRECRAWL_API_KEY'))
# Scrape single URL → markdown
result = firecrawl.scrape(
'https://docs.firecrawl.dev',
formats=['markdown', 'html']
)
print(result.markdown[:500])
print(result.metadata.title)
# Crawl entire site (blocking, auto-polls)
crawl = firecrawl.crawl(
'https://docs.firecrawl.dev',
limit=50,
scrape_options=ScrapeOptions(formats=['markdown']),
poll_interval=5
)
for doc in crawl.data:
print(doc.metadata.source_url, len(doc.markdown or ''))
# Async crawl (non-blocking)
job = firecrawl.start_crawl(
'https://docs.firecrawl.dev',
limit=50
)
status = firecrawl.get_crawl_status(job.id)