Browser

Scrapfly Crawler

Try it

Crawl entire websites using the Scrapfly Crawler API with the Python SDK

What it does

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.

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

ParameterTypeDefaultDescription
urlstrrequiredStarting URL for the crawl
page_limitintNoneMaximum number of pages to crawl
max_depthintNoneMaximum link traversal depth from start URL
max_durationintNoneMaximum crawl duration in seconds
exclude_pathslist[str]NoneURL path patterns to skip (supports *, max 100). Mutually exclusive with include_only_paths
include_only_pathslist[str]NoneOnly crawl URLs matching these path patterns (supports *, max 100). Mutually exclusive with exclude_paths
ignore_base_path_restrictionboolFalseAllow crawling any path on the same domain, not just the seed URL's base path
follow_external_linksboolFalseFollow links to external domains
allowed_external_domainslist[str]NoneWhitelist of external domains when follow_external_links=True (supports *, max 250)
headersdictNoneCustom HTTP headers as JSON object
delayintNoneDelay between requests in milliseconds (0-15000)
user_agentstrNoneCustom User-Agent string (ignored when asp=True)
max_concurrencyintNoneMaximum concurrent crawl requests
rendering_delayintNoneWait time in ms after page load before extraction (0-25000). 0 disables browser rendering
use_sitemapsboolFalseDiscover URLs from sitemap.xml when available
respect_robots_txtboolFalseRespect robots.txt rules and Disallow directives
ignore_no_followboolFalseIgnore rel="nofollow" attributes on links
cacheboolFalseEnable cache layer for crawled pages
cache_ttlintNoneCache time-to-live in seconds (0-604800)
cache_clearboolFalseForce refresh of cached pages
content_formatslist[str]NoneContent formats to extract: html, clean_html, markdown, text
extraction_rulesdictNoneCustom extraction rules for structured data
aspboolFalseEnable Anti Scraping Protection with browser rendering
proxy_poolstrNoneProxy pool to use (e.g., public_residential_pool)
countrystrNoneProxy country selection (ISO code)
webhook_namestrNoneName of webhook configured in dashboard
webhook_eventslist[str]NoneEvents to subscribe to (e.g., crawler_started, crawler_url_visited, crawler_finished)
max_api_creditintNoneMaximum 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

ClassDescription
ScrapflyClientMain client - handles auth and API calls
CrawlerConfigCrawl job configuration
CrawlHigh-level crawl lifecycle manager (start, wait, read)
CrawlerStartResponseResponse from client.start_crawl()
CrawlerStatusResponseResponse from client.get_crawl_status() - has is_complete, is_failed, progress_pct
CrawlerArtifactResponseResponse from client.get_crawl_artifact() - has get_pages(), iter_responses(), save()
CrawlContentContent for a single URL - has url, content, status_code, duration

Important Notes

  • Use page_limit to control credit spend on large sites
  • Use include_only_paths and exclude_paths to focus crawls on relevant sections
  • exclude_paths and include_only_paths are mutually exclusive
  • Crawl jobs are asynchronous - use Crawl.crawl().wait() for simple workflows or poll with client.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_links should be used carefully to avoid crawling the entire web

Related skills

Fetch raw ad creative, app, ranking, and revenue data from AdMapix as structured JSON.

by fly0pants4.3k installs296 stars

Stores durable facts in a categorized, plain-markdown vault on disk, alongside your agent's built-in memory.

by Iván559 installs18 stars

Generate and edit Draw.io, Mermaid, and Excalidraw diagrams from natural language using a structured JSON spec.

by nssa.io1.0k installs47 stars

Join a video meeting as an AI bot with voice, avatar, and screenshare across four operating modes.

by johnpatternai22 installs8 stars

Find why your productivity system keeps failing, then apply the smallest fix — capacity math, bottleneck routing, durable local notes.

by Iván855 installs69 stars

More from scrapfly

Browse all skills

Web scraping using the Scrapfly Scraper API with the Python SDK

by scrapfly1 installs

Cross-tool golden rules for an autonomous Scrapfly web agent connected to the Scrapfly MCP server (web_scrape, screenshot, extract, classify_block, the Cloud...

by scrapfly

Threshold-based alerting on Scrapfly account metrics. Wire up rules like "alert me when scrape success rate drops below 95% for 10 minutes" and receive notif...

by scrapfly

Automate cloud browsers using the Scrapfly Cloud Browser API with Python Playwright

by scrapfly

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...

by scrapfly

Extract structured data from web content using the Scrapfly Extraction API with the Python SDK

by scrapfly