Web Scraping Services Comparison: Cloudflare Browser Rendering vs Firecrawl vs Scrapling
Date: 2026-03-12 | Author: @senrecep
Cloudflare Browser Rendering
Firecrawl
Scrapling
Type
Infrastructure service (Workers ecosystem)
Managed scraping SaaS
Open-source Python framework
Approach
Low-level, flexible
High-level, easy to use
Full control, self-hosted
Pricing model
Browser seconds (time-based)
Credit-based (per page)
Free (infrastructure cost only)
Self-host
No
Yes (open source)
Yes (Python/Docker)
SDK
REST API only
Python, Node.js, Go, Rust
Python only
Cloudflare bypass
No
No
Yes (built-in)
License
Commercial
Open source + SaaS
Open source
GitHub
-
github.com/mendableai/firecrawl
github.com/D4Vinci/Scrapling
2. Cloudflare Browser Rendering - /crawl Endpoint
Endpoint: https://api.cloudflare.com/client/v4/accounts/<account_id>/browser-rendering/crawl
Methods: POST (initiate), GET (retrieve results), DELETE (cancel)
Auth: Authorization: Bearer <apiToken> - requires Browser Rendering - Edit permission
1. POST /crawl --> Get job ID (async)
2. GET /crawl/{id} --> Poll for results (status: running -> completed)
3. DELETE /crawl/{id} --> (Optional) Cancel the job
Parameter
Default
Description
url
(required)
Starting URL
limit
10
Maximum pages to crawl (max: 100,000)
depth
100,000
Link follow depth
formats
["html"]
html, markdown, json
render
true
Execute JS. false = HTML-only fetch (fast + cheap)
source
all
URL discovery source: all, sitemaps, links
maxAge
86,400 (1 day)
Cache freshness in seconds (max: 604,800)
modifiedSince
-
Unix timestamp - only crawl pages modified after this time
options.includeExternalLinks
false
Follow external domain links
options.includeSubdomains
false
Follow subdomain links
options.includePatterns
-
Wildcard patterns (*, **) to include
options.excludePatterns
-
Wildcard patterns to exclude
Advanced parameters: authenticate, cookies, gotoOptions, setExtraHTTPHeaders, waitForSelector, rejectResourceTypes, userAgent
Status
Meaning
running
Crawl in progress
completed
Successfully finished
cancelled_due_to_timeout
Exceeded 7-day max runtime
cancelled_due_to_limits
Hit account limits
cancelled_by_user
Manually cancelled
errored
Encountered an error
Max runtime: 7 days
Result retention: 14 days
Max pages/job: 100,000
Respects robots.txt (mandatory)
CAPTCHA/Turnstile bypass: None
Default User-Agent: CloudflareBrowserRenderingCrawler/1.0
Native integration with Workers, R2, KV
render: false for very cheap static HTML fetch
14-day result retention (longest)
100K pages/job limit (highest)
Browser seconds pricing - cheap for simple pages
Workers Paid Plan required ($5/mo minimum)
No CAPTCHA/Cloudflare bypass
No Webhook/WebSocket (polling required)
REST API only (no SDK)
Significant limitations on Free plan
Endpoint: POST /v2/crawl
Auth: Authorization: Bearer fc-YOUR-API-KEY
SDKs: Python, Node.js, Go, Rust
# Blocking (automatic polling + pagination)
result = firecrawl .crawl (url , limit = 50 )
# Non-blocking
job = firecrawl .start_crawl (url , limit = 50 )
status = firecrawl .get_crawl_status (job ['id' ])
Parameter
Default
Description
url
(required)
Starting URL
limit
10,000
Maximum pages to crawl
maxDiscoveryDepth
-
Link follow depth
includePaths
-
Regex patterns (include)
excludePaths
-
Regex patterns (exclude)
crawlEntireDomain
false
Include sibling/parent URLs
allowSubdomains
false
Follow subdomain links
allowExternalLinks
false
Follow external links
sitemap
"include"
"include", "skip", "only"
ignoreQueryParameters
false
Avoid re-scraping same path with different query params
delay
-
Delay between requests (seconds)
maxConcurrency
-
Concurrent request limit
Plan
Price/mo
Credits
Concurrent
Extra Credits
Free
$0
500 (one-time)
2
-
Hobby
$16
3,000
5
$9/1K
Standard
$83
100,000
50
$47/35K
Growth
$333
500,000
100
$177/175K
Scale
$599
1,000,000
150
-
Enterprise
Custom
Custom
Custom
-
Credit usage:
Feature
Cost
Scrape/Crawl
1 credit/page
JSON mode (AI)
+4 credits/page
Enhanced proxy
+4 credits/page
PDF parsing
1 credit/PDF page
Search
2 credits/10 results
Browser
2 credits/minute
Rich SDK support (Python, Node, Go, Rust)
Webhook (signed) + WebSocket (real-time) support
Self-host option (open source)
Regex-based URL filtering
Blocking SDK method (automatic polling)
Geo-location targeting
Error tracking endpoint (/crawl/{id}/errors)
Credit-based - expensive at high volume
JSON/AI extraction costs +4 credits (5x cost)
Result retention only 24 hours (shortest)
No CAPTCHA/Cloudflare bypass
Credits reset monthly (no rollover)
pip install scrapling
scrapling install # install browsers
# or Docker
docker pull d4vinci/scrapling
Fetcher
Use Case
CF Bypass
Speed
Resource Usage
Fetcher
Normal sites, APIs
No
Fastest
Low
StealthyFetcher
Cloudflare-protected sites
Yes (Turnstile + Interstitial)
Medium
Medium
DynamicFetcher
JS-heavy SPAs
Via Playwright
Slowest
High
Anti-Bot Bypass Capabilities
Protection
Support
Cloudflare Turnstile
Built-in
Cloudflare Interstitial
Built-in
TLS Fingerprint
Chrome, Firefox, Safari, Edge impersonation
Akamai, DataDome, Kasada
Via Hyper Solutions integration (external service)
Adaptive Parsing: Automatically relocates elements when page structure changes
Spider Framework: Scrapy-like API with concurrent crawling
Pause/Resume: Ctrl+C to pause, restart to continue
Multi-session: Use different fetchers within the same spider
MCP Server: AI-assisted scraping with Claude/Cursor
CLI: Scrape from terminal without writing code
Async/await support
Proxy rotation
Free and open source
Cloudflare Turnstile/Interstitial bypass (biggest differentiator)
Adaptive parsing (works even when site changes)
Full spider framework (Scrapy-like)
TLS fingerprint impersonation
Zero dependency on managed services
Python only
Self-hosting required (infrastructure cost)
Server maintenance, browser updates are your responsibility
No Webhook/WebSocket
No managed monitoring
IP ban risk (may need proxies)
Scaling is your responsibility
Feature
CF Browser Rendering
Firecrawl
Scrapling
JS rendering
Yes (render: true)
Yes (default)
Yes (Playwright)
Output formats
HTML, Markdown, JSON
HTML, Markdown, JSON
HTML (with parsing)
AI extraction
Workers AI
Built-in LLM
MCP server
Sitemap support
Yes
Yes
Via spider
URL filtering
Wildcard (*, **)
Regex patterns
Python code
Subdomain following
Yes
Yes
Yes
External link following
Yes
Yes
Yes
Auth support
Basic auth, cookies, headers
Basic auth, cookies, headers
Full control
Proxy
Cloudflare network (built-in)
Yes (extra credits)
Self-configured
Geo-location
No
Yes
Possible via proxy
Webhook
No
Yes (signed)
No
WebSocket (real-time)
No
Yes
No
Rate limit control
Automatic
delay + maxConcurrency
Full control
Result retention
14 days
24 hours
Unlimited (your disk)
Max job runtime
7 days
Not specified
Unlimited
Max pages/job
100,000
10,000 (default)
Unlimited
robots.txt compliance
Mandatory
Optional
Optional
CAPTCHA bypass
No
No
Yes (CF Turnstile)
Error tracking
No
Yes
Via logging
Adaptive parsing
No
No
Yes
Spider framework
No
No
Yes
Pause/Resume
No
No
Yes
Cloudflare Browser Rendering
Prerequisite: Workers Paid Plan ($5/mo)
Pricing: Browser seconds based
render: false for HTML-only fetch - very cheap
render: true for JS rendering - cost varies by site complexity
Plan
Price/mo
Pages/mo
Cost per Page
Free
$0
500 (one-time)
$0
Hobby
$16
3,000
$0.0053
Standard
$83
100,000
$0.00083
Growth
$333
500,000
$0.00067
Scale
$599
1,000,000
$0.00060
Software: $0 (open source)
Infrastructure: Google Cloud, AWS, or your own server
See next section for details
7. Infrastructure Cost (Scrapling Self-Host)
Why Infrastructure Matters
Scrapling runs headless Chromium. Each browser instance uses ~300-500MB RAM.
With concurrent scraping, this multiplies. Memory is the critical resource.
Google Cloud Compute Engine
VM Type
vCPU
RAM
Monthly Price
Concurrent Scrapes
e2-small
0.5-2
2 GB
~$13/mo
2-3 pages
e2-medium
1-2
4 GB
~$27/mo
5-8 pages
e2-standard-2
2
8 GB
~$49/mo
10-15 pages
e2-standard-4
4
16 GB
~$97/mo
25-30 pages
Additional costs:
Disk (30GB SSD): ~$5/mo
Egress: first 1GB free, then ~$0.12/GB
Static IP (optional): ~$3/mo
Pricing:
vCPU: $0.00002400/vCPU-second
Memory: $0.00000250/GiB-second
Requests: $0.40/million
Free tier: 2M requests, 360K vCPU-sec, 180K GiB-sec/mo
Per scrape request (CF bypass ~10 sec, 2 vCPU, 4GB RAM):
CPU: 10s x 2vCPU x $0.000024 = $0.00048
RAM: 10s x 4GiB x $0.0000025 = $0.0001
Cost per page: ~$0.00058
Cloud Run caveats:
Cold start adds 5-10s for browser launch
Solved with min instances (but adds cost)
Max timeout 60min - long crawl jobs need to be split
8. Total Cost of Ownership (TCO)
Low Volume: 3,000 pages/month
Service
Monthly Cost
Notes
Scrapling + Cloud Run
~$2
Benefits from free tier
CF Browser Rendering
~$5-7
Workers paid plan required
Firecrawl Hobby
$16
3K credits included
Scrapling + Compute Engine
~$19
24/7 VM (overkill)
Winner: Scrapling + Cloud Run ($2)
If CF bypass needed: Scrapling + Cloud Run ($2) - only real option anyway
Medium Volume: 50,000 pages/month
Service
Monthly Cost
Notes
CF Browser Rendering
~$20-40
Browser seconds based
Scrapling + Cloud Run
~$44
Min instance required
Scrapling + Compute Engine
~$59
e2-standard-2, stable
Firecrawl Standard
$83
100K credits, easy to use
Winner: CF Browser Rendering ($20-40)
If CF bypass needed: Scrapling + Cloud Run ($44)
High Volume: 500,000 pages/month
Service
Monthly Cost
Notes
CF Browser Rendering
~$100-200
Estimated
Scrapling + Compute Engine
~$234
2x e2-standard-4
Firecrawl Growth
$333
500K credits
Scrapling + Cloud Run
~$350
Gets expensive at this scale
Firecrawl Scale
$599
1M credits
Winner: CF Browser Rendering ($100-200)
If CF bypass needed: Scrapling + Compute Engine ($234)
Cost Item
Scrapling (self-host)
Managed Services
Server maintenance
You manage
None
Browser updates
You update
Automatic
Proxy (if IP banned)
$20-100+/mo
Built-in/extra fee
Monitoring/logging
$5-20/mo (Cloud Monitoring)
Built-in
Developer time
High (debug, maintain)
Low
Downtime risk
Present
Low
Cloudflare Bypass Required
Volume
Recommendation
Cost
Low (3K)
Scrapling + Cloud Run
~$2/mo
Medium (50K)
Scrapling + Cloud Run
~$44/mo
High (500K)
Scrapling + Compute Engine
~$234/mo
Cloudflare Bypass Not Required
Volume
Recommendation
Cost
Low (3K)
CF Browser Rendering
~$5-7/mo
Medium (50K)
CF Browser Rendering
~$20-40/mo
High (500K)
CF Browser Rendering
~$100-200/mo
Scenario
Recommendation
Reason
Quick prototype
Firecrawl Free/Hobby
Working in 5 minutes with SDK
Zero-cost trial
Scrapling + local machine
Run on your own computer
Minimum management
Firecrawl
SDK + webhook + zero ops
Using Workers ecosystem
CF Browser Rendering
Native integration
Site structure changes often
Scrapling
Adaptive parsing
Real-time result streaming
Firecrawl
Webhook + WebSocket
Data control (compliance)
Scrapling self-host
Data never leaves your infra
Crawl from different countries
Firecrawl
Geo-location targeting
Cloudflare Browser Rendering
# 1. Start crawl
curl -X POST \
' https://api.cloudflare.com/client/v4/accounts/{account_id}/browser-rendering/crawl' \
-H ' Authorization: Bearer <API_TOKEN>' \
-H ' Content-Type: application/json' \
-d ' {
"url": "https://example.com",
"limit": 50,
"depth": 3,
"formats": ["markdown"],
"options": {
"includePatterns": ["**/blog/**"],
"excludePatterns": ["**/admin/**"]
}
}'
# 2. Retrieve results
curl -X GET \
' https://api.cloudflare.com/client/v4/accounts/{account_id}/browser-rendering/crawl/{job_id}?status=completed' \
-H ' Authorization: Bearer <API_TOKEN>'
# 3. Cancel
curl -X DELETE \
' https://api.cloudflare.com/client/v4/accounts/{account_id}/browser-rendering/crawl/{job_id}' \
-H ' Authorization: Bearer <API_TOKEN>'
// JavaScript polling example
async function crawlAndWait ( accountId , apiToken , targetUrl ) {
const startRes = await fetch (
`https://api.cloudflare.com/client/v4/accounts/${ accountId } /browser-rendering/crawl` ,
{
method : 'POST' ,
headers : {
'Authorization' : `Bearer ${ apiToken } ` ,
'Content-Type' : 'application/json'
} ,
body : JSON . stringify ( { url : targetUrl , limit : 50 , formats : [ 'markdown' ] } )
}
) ;
const { result : jobId } = await startRes . json ( ) ;
for ( let i = 0 ; i < 60 ; i ++ ) {
const res = await fetch (
`https://api.cloudflare.com/client/v4/accounts/${ accountId } /browser-rendering/crawl/${ jobId } ` ,
{ headers : { Authorization : `Bearer ${ apiToken } ` } }
) ;
const data = await res . json ( ) ;
if ( data . result . status !== 'running' ) return data . result ;
await new Promise ( r => setTimeout ( r , 5000 ) ) ;
}
}
from firecrawl import FirecrawlApp
app = FirecrawlApp (api_key = "fc-YOUR-API-KEY" )
# Blocking (automatic polling)
result = app .crawl (
url = "https://example.com" ,
limit = 50 ,
scrape_options = {
"formats" : ["markdown" ],
"includePaths" : ["/blog/*" ],
"excludePaths" : ["/admin/*" ]
}
)
for page in result ['data' ]:
print (page ['metadata' ]['title' ])
print (page ['markdown' ][:200 ])
import FirecrawlApp from '@mendable/firecrawl-js' ;
const app = new FirecrawlApp ( { apiKey : "fc-YOUR-API-KEY" } ) ;
const result = await app . crawlUrl ( "https://example.com" , {
limit : 50 ,
scrapeOptions : { formats : [ "markdown" ] }
} ) ;
result . data . forEach ( page => {
console . log ( page . metadata . title ) ;
console . log ( page . markdown . substring ( 0 , 200 ) ) ;
} ) ;
from scrapling .fetchers import Fetcher , StealthyFetcher , DynamicFetcher
# --- Simple HTTP request ---
page = Fetcher .get ('https://quotes.toscrape.com/' )
quotes = page .css ('.quote .text::text' ).getall ()
# --- With Cloudflare bypass ---
with StealthyFetcher .create_session (headless = True , solve_cloudflare = True ) as session :
page = session .fetch ('https://cloudflare-protected-site.com' )
title = page .css ('h1::text' ).get ()
links = page .css ('a::attr(href)' ).getall ()
# --- For JS-heavy SPAs ---
with DynamicFetcher .create_session (headless = True , network_idle = True ) as session :
page = session .fetch ('https://react-spa-site.com' )
data = page .css ('.product-card' )
# --- Full Spider ---
from scrapling import Spider , Response , Request
class MyScraper (Spider ):
name = "my_scraper"
start_urls = ["https://example.com" ]
concurrent_requests = 10
async def parse (self , response : Response ):
for item in response .css ('.item' ):
yield {
"title" : item .css ('.title::text' ).get (),
"url" : item .css ('a::attr(href)' ).get (),
}
# Follow pagination
next_page = response .css ('.next a::attr(href)' ).get ()
if next_page :
yield Request (next_page , callback = self .parse )
# Run (Ctrl+C to pause, restart to resume)
MyScraper (crawldir = "./crawl_data" ).start ()