Headless Browser vs HTTP Requests: When to Use What

Published July 24, 2026 · 5 min read

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.

The Tradeoff at a Glance

HTTP RequestsHeadless Browser
Speed50-500ms per page2-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 complexityLow (1 dependency)High (browser binary, driver, pool mgmt)

When HTTP Requests Win

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

When You Need a Headless Browser

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 Hybrid Approach

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;
}
The heuristic rule: If less than 10% of the HTML is actual text content (after stripping tags), it's probably an SPA. The payload is mostly JavaScript bundles and empty container divs. Skip straight to browser rendering.

Real Numbers: Cost Comparison

Approach10K pages/month100K pages/month1M pages/month
DIY HTTP (1 VPS + Cheerio)~$5~$10 (2 servers)~$50 (auto-scale)
DIY Headless (browser pool)~$80~$400Not feasible on VPS
ExtractAPI (browser API)$9$99Enterprise (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.

The Bottom Line

Key insight: Most developers over-invest in "proper" browser automation when 80% of their target pages would work fine with HTTP requests. Profile your targets before choosing infrastructure. ExtractAPI's javascript parameter lets you toggle rendering per-request — so you only pay the browser cost when you actually need it.
Try ExtractAPI — Browser Rendering from $9/month