Web scraping using the Scrapfly Scraper API with the Python SDK
Browser
Scrapfly Crawler
Try itCrawl entire websites using the Scrapfly Crawler API with the Python SDK
What it does
Crawl entire websites using the Scrapfly Crawler API with the Python SDK
The skill document
Scrapfly Crawler
Use the Scrapfly Crawler API via the Python SDK to schedule and manage site-wide crawl jobs with automatic link discovery, depth control, and structured data extraction.
When to use
- Crawling entire websites or specific sections
- Discovering all pages/URLs on a site
- Collecting content from multiple pages in bulk
- Extracting structured data at scale across a site
- Downloading site archives (WARC/HAR format)
- Building LLM.txt files from crawled documentation
Setup
pip install scrapfly-sdk
The API key must be provided via environment variable SCRAPFLY_API_KEY or passed directly to the client.
SDK Imports
from scrapfly import ScrapflyClient, CrawlerConfig, Crawl
Additional imports for webhooks:
from scrapfly import (
webhook_from_payload,
CrawlStartedWebhook,
CrawlUrlDiscoveredWebhook,
CrawlUrlFailedWebhook,
CrawlCompletedWebhook,
)
CrawlerConfig Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
url | str | required | Starting URL for the crawl |
page_limit | int | None | Maximum number of pages to crawl |
max_depth | int | None | Maximum link traversal depth from start URL |
max_duration | int | None | Maximum crawl duration in seconds |
exclude_paths | list[str] | None | URL path patterns to skip (supports *, max 100). Mutually exclusive with include_only_paths |
include_only_paths | list[str] | None | Only crawl URLs matching these path patterns (supports *, max 100). Mutually exclusive with exclude_paths |
ignore_base_path_restriction | bool | False | Allow crawling any path on the same domain, not just the seed URL's base path |
follow_external_links | bool | False | Follow links to external domains |
allowed_external_domains | list[str] | None | Whitelist of external domains when follow_external_links=True (supports *, max 250) |
headers | dict | None | Custom HTTP headers as JSON object |
delay | int | None | Delay between requests in milliseconds (0-15000) |
user_agent | str | None | Custom User-Agent string (ignored when asp=True) |
max_concurrency | int | None | Maximum concurrent crawl requests |
rendering_delay | int | None | Wait time in ms after page load before extraction (0-25000). 0 disables browser rendering |
use_sitemaps | bool | False | Discover URLs from sitemap.xml when available |
respect_robots_txt | bool | False | Respect robots.txt rules and Disallow directives |
ignore_no_follow | bool | False | Ignore rel="nofollow" attributes on links |
cache | bool | False | Enable cache layer for crawled pages |
cache_ttl | int | None | Cache time-to-live in seconds (0-604800) |
cache_clear | bool | False | Force refresh of cached pages |
content_formats | list[str] | None | Content formats to extract: html, clean_html, markdown, text |
extraction_rules | dict | None | Custom extraction rules for structured data |
asp | bool | False | Enable Anti Scraping Protection with browser rendering |
proxy_pool | str | None | Proxy pool to use (e.g., public_residential_pool) |
country | str | None | Proxy country selection (ISO code) |
webhook_name | str | None | Name of webhook configured in dashboard |
webhook_events | list[str] | None | Events to subscribe to (e.g., crawler_started, crawler_url_visited, crawler_finished) |
max_api_credit | int | None | Maximum API credits to spend on this crawl |
Webhook Events
"crawler_started"- Crawler execution began"crawler_url_visited"- A URL was successfully crawled"crawler_url_skipped"- URLs were skipped"crawler_url_discovered"- New URLs were discovered"crawler_url_failed"- A URL failed to crawl"crawler_stopped"- Crawler stopped due to failure or limits"crawler_cancelled"- Crawl was manually cancelled"crawler_finished"- Crawler completed successfully
Examples
Quick start: crawl and get results
import os
from scrapfly import ScrapflyClient, CrawlerConfig, Crawl
client = ScrapflyClient(key=os.environ["SCRAPFLY_API_KEY"])
# Create, start, and wait for crawl in one chain
# 2. Create and run crawler
crawl = Crawl(
client,
CrawlerConfig(url='https://web-scraping.dev/products', page_limit=5)
).crawl().wait()
# 3. Get results
pages = crawl.warc().get_pages()
# 4. Process results
print(f"Crawled {len(pages)} pages:")
for page in pages:
print(f" • {page['url']} ({page['status_code']})")
# 5. Access specific URLs
html = crawl.read('https://web-scraping.dev/products')
if html:
print(f"\nMain page is {len(html):,} bytes")
Low-level workflow: start, poll, download
import os
import time
from scrapfly import ScrapflyClient, CrawlerConfig
client = ScrapflyClient(key=os.environ["SCRAPFLY_API_KEY"])
config = CrawlerConfig(
url='https://web-scraping.dev',
page_limit=10,
max_depth=2,
content_formats=['html', 'markdown']
)
# 1. Start crawler
start_response = client.start_crawl(config)
print(f"Crawler started: {start_response.uuid}")
# 2. Poll for status
while True:
status = client.get_crawl_status(start_response.uuid)
print(f"Status: {status.status} - {status.progress_pct:.1f}%")
print(f"Crawled: {status.urls_crawled}/{status.urls_discovered} pages")
if status.is_complete:
break
elif status.is_failed:
print("Crawl failed!")
break
time.sleep(5)
# 3. Download and process results
if status.is_complete:
artifact = client.get_crawl_artifact(start_response.uuid)
pages = artifact.get_pages()
print(f"Downloaded {len(pages)} pages")
for page in pages:
print(f" {page['url']}: {page['status_code']} ({len(page['content'])} bytes)")
# Save to file
artifact.save('crawl_results.warc.gz')
Crawl with path filtering
config = CrawlerConfig(
url='https://web-scraping.dev/',
page_limit=50,
max_depth=5,
include_only_paths=['*/product/*'],
content_formats=['markdown', 'text'],
)
crawl = Crawl(client, config).crawl().wait()
Crawl with anti-bot bypass
config = CrawlerConfig(
url='https://web-scraping.dev',
page_limit=50,
asp=True,
content_formats=['html'],
)
crawl = Crawl(client, config).crawl().wait()
Crawl with extraction rules
config = CrawlerConfig(
url='https://web-scraping.dev/products',
page_limit=100,
max_depth=3,
include_only_paths=['/products/*'],
extraction_rules={
"/products/*": {
"type": "model",
"value": "product",
},
},
)
crawl = Crawl(client, config).crawl().wait()
Read content for specific URLs
# Read HTML (from WARC - fast)
content = crawl.read('https://web-scraping.dev/product/1')
if content:
print(f"URL: {content.url}")
print(f"Status: {content.status_code}")
print(f"Duration: {content.duration}s")
print(content.content)
# Read markdown (from contents API)
content = crawl.read('https://web-scraping.dev/product/1', format='markdown')
if content:
print(content.content)
Iterate URLs matching a pattern
for content in crawl.read_iter(pattern="*/products?page=*", format="markdown"):
print(f"{content.url}: {len(content.content)} chars")
Batch content retrieval
# Get markdown for multiple URLs in a single API call (max 100 per request)
urls = ['https://web-scraping.dev/product/1', 'https://web-scraping.dev/product/2']
contents = crawl.read_batch(urls, formats=['markdown'])
for url, formats_dict in contents.items():
markdown = formats_dict.get('markdown', '')
print(f"{url}: {len(markdown)} chars")
Download WARC/HAR archive
# WARC format (default)
artifact = crawl.warc()
artifact.save('crawl_results.warc.gz')
# HAR format
artifact = crawl.har()
artifact.save('crawl_results.har')
# Iterate through records
for record in artifact.iter_responses():
print(f"{record.url}: {record.status_code}")
Build LLM.txt from crawled documentation
import os
from scrapfly import ScrapflyClient, CrawlerConfig, Crawl
client = ScrapflyClient(key=os.environ["SCRAPFLY_API_KEY"])
config = CrawlerConfig(
url='https://web-scraping.dev/',
include_only_paths=['/docs', '/docs/*'], # only allow doc resources
page_limit=50,
max_depth=5,
follow_external_links=False,
content_formats=['markdown'],
)
crawl = Crawl(client, config).crawl().wait(poll_interval=5, verbose=True)
# Get URLs from WARC
warc_artifact = crawl.warc()
all_urls = [
record.url for record in warc_artifact.iter_responses()
if record.status_code == 200
]
# Retrieve markdown content in batches
all_contents = {}
for i in range(0, len(all_urls), 100):
batch_urls = all_urls[i:i + 100]
batch_contents = crawl.read_batch(batch_urls, formats=['markdown'])
all_contents.update(batch_contents)
# Build llms-full.txt
lines = ["# Documentation\n"]
for url, formats_dict in all_contents.items():
markdown = formats_dict.get('markdown', '').strip()
if markdown:
lines.append(f"---\n\n### {url}\n\n{markdown}\n")
with open("llms-full.txt", "w", encoding="utf-8") as f:
f.write("\n".join(lines))
Crawl statistics
stats = crawl.stats()
print(f"URLs discovered: {stats['urls_discovered']}")
print(f"URLs crawled: {stats['urls_crawled']}")
print(f"Progress: {stats['progress_pct']:.1f}%")
Key SDK Classes
| Class | Description |
|---|---|
ScrapflyClient | Main client - handles auth and API calls |
CrawlerConfig | Crawl job configuration |
Crawl | High-level crawl lifecycle manager (start, wait, read) |
CrawlerStartResponse | Response from client.start_crawl() |
CrawlerStatusResponse | Response from client.get_crawl_status() - has is_complete, is_failed, progress_pct |
CrawlerArtifactResponse | Response from client.get_crawl_artifact() - has get_pages(), iter_responses(), save() |
CrawlContent | Content for a single URL - has url, content, status_code, duration |
Important Notes
- Use
page_limitto control credit spend on large sites - Use
include_only_pathsandexclude_pathsto focus crawls on relevant sections exclude_pathsandinclude_only_pathsare mutually exclusive- Crawl jobs are asynchronous - use
Crawl.crawl().wait()for simple workflows or poll withclient.get_crawl_status()for more control - WARC/HAR artifacts are available for offline processing via
crawl.warc()/crawl.har() read_batch()supports up to 100 URLs per request for efficient bulk content retrieval- Webhook signatures should be verified for security in production
follow_external_linksshould be used carefully to avoid crawling the entire web
Related skills
Capture web page screenshots using the Scrapfly Screenshot API with the Python SDK
Extract structured data from web content using the Scrapfly Extraction API with the Python SDK
Web scraping from Go with the Scrapfly SDK (`github.com/scrapfly/go-scrapfly`). Use when the user asks to scrape a URL, bypass anti-bot protection, render Ja...
Automate cloud browsers using the Scrapfly Cloud Browser API with Python Playwright
Use the Scrapfly CLI (`scrapfly`) to scrape web pages, capture screenshots, extract structured data with AI, crawl entire sites, and drive a cloud browser ov...