Install
openclaw skills install @scrapfly/scrapfly-crawlerCrawl entire websites using the Scrapfly Crawler API with the Python SDK
openclaw skills install @scrapfly/scrapfly-crawlerUse 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.
pip install scrapfly-sdk
The API key must be provided via environment variable SCRAPFLY_API_KEY or passed directly to the client.
from scrapfly import ScrapflyClient, CrawlerConfig, Crawl
Additional imports for webhooks:
from scrapfly import (
webhook_from_payload,
CrawlStartedWebhook,
CrawlUrlDiscoveredWebhook,
CrawlUrlFailedWebhook,
CrawlCompletedWebhook,
)
| 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 |
"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 successfullyimport 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")
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')
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()
config = CrawlerConfig(
url='https://web-scraping.dev',
page_limit=50,
asp=True,
content_formats=['html'],
)
crawl = Crawl(client, config).crawl().wait()
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 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)
for content in crawl.read_iter(pattern="*/products?page=*", format="markdown"):
print(f"{content.url}: {len(content.content)} chars")
# 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")
# 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}")
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))
stats = crawl.stats()
print(f"URLs discovered: {stats['urls_discovered']}")
print(f"URLs crawled: {stats['urls_crawled']}")
print(f"Progress: {stats['progress_pct']:.1f}%")
| 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 |
page_limit to control credit spend on large sitesinclude_only_paths and exclude_paths to focus crawls on relevant sectionsexclude_paths and include_only_paths are mutually exclusiveCrawl.crawl().wait() for simple workflows or poll with client.get_crawl_status() for more controlcrawl.warc() / crawl.har()read_batch() supports up to 100 URLs per request for efficient bulk content retrievalfollow_external_links should be used carefully to avoid crawling the entire web