The wrong scraping approach costs you either speed or data. Use HTTP requests on a JavaScript-rendered site and you get empty divs. Use a headless browser on a server-rendered blog and you waste 10x the resources. Here's how to pick the right tool for each job.
| HTTP Requests | Headless Browser | |
|---|---|---|
| Speed | 50-500ms per page | 2-15 seconds per page |
| Resource usage | ~10MB RAM | ~200-500MB RAM per instance |
| JavaScript rendering | ❌ None | ✅ Full browser engine |
| SPA support | ❌ Empty shell | ✅ Renders React/Vue/Angular |
| Anti-bot evasion | ⚠️ Easy to detect | ✅ Passes basic JS checks |
| Cost (self-hosted) | ~$5/month/server | ~$40/month/server |
| Setup complexity | Low (1 dependency) | High (browser binary, driver, pool mgmt) |
Use plain HTTP requests when:
// Fast: 50ms, 10MB RAM — perfect for server-rendered content
const res = await fetch('https://example.com/blog/post');
const html = await res.text();
const title = html.match(/<h1>(.+?)<\/h1>/)[1];
console.log(title); // Got it, no browser needed
Switch to a headless browser when:
// Slow but necessary: 3-8 seconds, 300MB RAM — but actually gets the data
const res = await fetch('https://extractapi.app/v1/extract', {
method: 'POST',
headers: { 'Authorization': 'Bearer ***', 'Content-Type': 'application/json' },
body: JSON.stringify({
url: 'https://react-ecommerce.com/products/widget',
javascript: true,
waitFor: '.product-details-loaded'
})
});
const data = await res.json();
console.log(data.data.title); // Got it — would be empty with HTTP-only
The smart strategy for any scraping project: try HTTP first, fall back to browser rendering only when necessary.
async function smartExtract(url) {
// Try 1: Check if data is in the static HTML
const httpRes = await fetch(url);
const html = await httpRes.text();
// Heuristic: if the body is mostly an empty div, it's an SPA
const textRatio = html.replace(/<[^>]+>/g, '').trim().length / html.length;
if (textRatio > 0.1) {
// Likely server-rendered — parse and return
return parseFromHtml(html);
}
// Try 2: SPA detected — use browser rendering
const apiRes = await fetch('https://extractapi.app/v1/extract', {
method: 'POST',
headers: { 'Authorization': 'Bearer ***', 'Content-Type': 'application/json' },
body: JSON.stringify({ url, javascript: true })
});
const data = await apiRes.json();
return data.data;
}
| Approach | 10K pages/month | 100K pages/month | 1M pages/month |
|---|---|---|---|
| DIY HTTP (1 VPS + Cheerio) | ~$5 | ~$10 (2 servers) | ~$50 (auto-scale) |
| DIY Headless (browser pool) | ~$80 | ~$400 | Not feasible on VPS |
| ExtractAPI (browser API) | $9 | $99 | Enterprise (contact) |
The gap is massive: running your own headless browser pool costs 8-40x more than an API that handles it for you — and that's before accounting for maintenance time.
javascript parameter lets you toggle rendering per-request — so you only pay the browser cost when you actually need it.