Why Use a Cloud Headless Browser?
Self-hosting Playwright or Puppeteer works fine in development. In production, you hit the same walls repeatedly:
- Memory: Each Chromium instance uses 150–300MB. With concurrent requests, your 2GB server OOMs.
- Lambda/serverless: Chromium binary is 100–300MB. Lambda's layer limit is 250MB unzipped. You need workarounds.
- Docker image size: A Node.js app with Playwright is 800MB–1.2GB. That's slow to pull and expensive to store.
- Anti-bot: Your headless browser gets fingerprinted and blocked. Stealth patches get detected, then you patch again.
- Browser updates: A Chromium security patch breaks your site captures. You have to re-test and redeploy.
Cloud headless APIs manage all of this as infrastructure. You send a request, get back the result.
Two Categories of Headless APIs
Full browser-as-a-service (Browserless, Browserbase): expose a WebSocket endpoint so your Puppeteer/Playwright code connects to a remote browser instead of launching locally. You keep your existing code structure, just swap the browser launch for a remote connection.
Task-specific APIs (SnapAPI, ScrapingBee, Apify): expose dedicated endpoints for screenshots, scraping, PDF generation, etc. Simpler interface, less flexible, but zero browser code required on your side.
Browserless
The most popular browser-as-a-service. Runs Chromium in the cloud and exposes a CDP (Chrome DevTools Protocol) endpoint. Your existing Puppeteer/Playwright code connects via WebSocket.
// Connect Puppeteer to Browserless instead of local Chrome
const puppeteer = require('puppeteer-core');
const browser = await puppeteer.connect({
browserWSEndpoint: `wss://chrome.browserless.io?token=${process.env.BROWSERLESS_TOKEN}`
});
const page = await browser.newPage();
await page.goto('https://example.com');
const screenshot = await page.screenshot({ fullPage: true });
await browser.disconnect();
// No browser.close() — just disconnect from the remote session
// Playwright connecting to Browserless
const { chromium } = require('playwright-core');
const browser = await chromium.connectOverCDP(
`wss://chrome.browserless.io?token=${process.env.BROWSERLESS_TOKEN}`
);
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle' });
const screenshot = await page.screenshot({ fullPage: true });
await browser.close();
Browserless also exposes REST endpoints for common operations so you don't need browser code at all:
// Browserless REST screenshot endpoint
const response = await fetch('https://chrome.browserless.io/screenshot?token=YOUR_TOKEN', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
url: 'https://example.com',
options: { fullPage: true, type: 'png' }
})
});
const imageBuffer = await response.buffer();
| Browserless Plan | Concurrent sessions | Price |
|---|---|---|
| Hobby | 2 | $50/mo |
| Startup | 10 | $200/mo |
| Growth | 25 | $500/mo |
| Self-hosted | Unlimited | Free (OSS) |
SnapAPI
Task-specific REST API for screenshots, scraping, content extraction, PDF generation, video recording, and AI page analysis. No browser code required — send a URL, get back the result.
// Screenshot
const response = await fetch('https://api.snapapi.pics/v1/screenshot', {
method: 'POST',
headers: { 'X-Api-Key': process.env.SNAPAPI_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({
url: 'https://example.com',
full_page: true,
wait_for: 'networkidle',
stealth: true,
block_ads: true
})
});
const { screenshot } = await response.json();
// base64 PNG
// Scrape structured data
const scrapeRes = await fetch('https://api.snapapi.pics/v1/scrape', {
method: 'POST',
headers: { 'X-Api-Key': process.env.SNAPAPI_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ url: 'https://news.ycombinator.com', wait_for: 'networkidle' })
});
const { html, text, links } = await scrapeRes.json();
// Extract specific fields with AI
const extractRes = await fetch('https://api.snapapi.pics/v1/extract', {
method: 'POST',
headers: { 'X-Api-Key': process.env.SNAPAPI_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({
url: 'https://shop.example.com/product',
schema: { name: 'string', price: 'number', in_stock: 'boolean', images: 'string[]' }
})
});
const { data } = await extractRes.json();
// data.name, data.price, data.in_stock, data.images
| SnapAPI Plan | Calls/mo | Price | Per call |
|---|---|---|---|
| Free | 200 | Free | — |
| Starter | 5,000 | $19/mo | $0.0038 |
| Pro | 50,000 | $79/mo | $0.0016 |
| Business | 500,000 | $299/mo | $0.0006 |
Browserbase
Newer entrant, built specifically for AI agents and LLM-driven automation. Uses the same CDP/WebSocket protocol as Browserless but adds session recording, live debugging, and better stealth.
// Browserbase — Playwright connection
const { chromium } = require('playwright-core');
const browser = await chromium.connectOverCDP(
`wss://connect.browserbase.com?apiKey=${process.env.BROWSERBASE_KEY}`
);
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle' });
const screenshot = await page.screenshot({ fullPage: true });
await browser.close();
| Browserbase Plan | Minutes/mo | Price |
|---|---|---|
| Hobby | 1,000 | Free |
| Starter | 10,000 | $99/mo |
| Growth | 50,000 | $399/mo |
Apify
Full-stack web scraping platform. Provides hosted actors (containerized scrapers), a proxy network, and Crawlee — their open-source Node.js scraping framework.
// Crawlee + Apify — full scraping framework
import { PlaywrightCrawler } from 'crawlee';
const crawler = new PlaywrightCrawler({
maxRequestsPerCrawl: 100,
async requestHandler({ request, page, enqueueLinks, pushData }) {
const title = await page.title();
const data = await page.evaluate(() => ({
heading: document.querySelector('h1')?.textContent,
links: [...document.querySelectorAll('a')].map(a => a.href).slice(0, 10)
}));
await pushData({ url: request.url, title, ...data });
// Follow internal links automatically
await enqueueLinks({ strategy: 'same-domain' });
}
});
await crawler.run(['https://example.com']);
| Apify Plan | Compute units/mo | Price |
|---|---|---|
| Free | 5 CU | Free |
| Starter | 49 CU | $49/mo |
| Scale | 499 CU | $499/mo |
ScrapingBee
Managed headless Chrome with built-in proxy rotation. Simpler than Browserless — you send a URL, it returns the HTML of the rendered page.
// ScrapingBee — returns rendered HTML
const params = new URLSearchParams({
api_key: process.env.SCRAPINGBEE_KEY,
url: 'https://example.com',
render_js: 'true',
wait_for: '.content-loaded'
});
const response = await fetch(`https://app.scrapingbee.com/api/v1?${params}`);
const html = await response.text();
// Parse html with cheerio
Full Comparison
| Service | Type | Free tier | Best for | Pricing model |
|---|---|---|---|---|
| Browserless | Browser-as-a-service | ❌ | Complex automation, existing Puppeteer code | Concurrent sessions |
| Browserbase | Browser-as-a-service | 1K min/mo | AI agents, session recording | Minutes used |
| SnapAPI | Task API | 200/mo | Screenshots, scraping, PDF, video | Per call |
| Apify | Platform | 5 CU/mo | Large-scale crawling, actor marketplace | Compute units |
| ScrapingBee | Proxy + render | Limited | HTML rendering with proxy rotation | API credits |
When to Use Which
Use Browserless / Browserbase when:
- You have existing Puppeteer/Playwright code and just want to offload the browser
- You need full browser control: clicking, form filling, multi-step navigation
- You're building AI agents that need to browse the web
Use SnapAPI when:
- You need screenshots, PDFs, or structured data extraction
- You want the simplest possible interface (POST URL → get result)
- You're on Lambda/Vercel/Cloudflare Workers with no room for browser binaries
- Cost efficiency matters — SnapAPI's $0.0016/call beats browser-as-a-service pricing for task-specific work
Use Apify when:
- You need to crawl millions of pages
- You want managed actors with retry logic, queues, and storage built in
- You need proxy rotation across datacenter and residential IPs
Self-Hosted Option: Playwright with Browser Pool
If you're doing high volume (>100K calls/month) and can manage the infrastructure, self-hosted beats all of the above on cost. The key is a browser pool that reuses instances instead of launching a new browser per request.
// browser-pool.js — simple Playwright browser pool
const { chromium } = require('playwright');
const genericPool = require('generic-pool');
const pool = genericPool.createPool({
create: () => chromium.launch({ headless: true, args: ['--no-sandbox'] }),
destroy: browser => browser.close()
}, { min: 2, max: 10 });
async function withBrowser(fn) {
const browser = await pool.acquire();
try {
return await fn(browser);
} finally {
await pool.release(browser);
}
}
// Usage
const screenshot = await withBrowser(async (browser) => {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle' });
const buf = await page.screenshot({ fullPage: true });
await page.close();
return buf;
});
The break-even point between self-hosted and SnapAPI Pro ($79/50K) is roughly 150K+ calls/month at $0.05/server-hour, assuming your infra is well-tuned. Below that, the API wins on total cost of ownership once engineering time is factored in.