Getting Started with Browserless: Cloud Headless Browser Testing

Getting Started with Browserless: Cloud Headless Browser Testing

Browserless is a managed headless Chrome service. Instead of spinning up Chrome yourself — dealing with dependencies, sandbox flags, memory leaks, and process management — you point your code at a WebSocket endpoint or REST API and let Browserless handle the browser lifecycle.

The core value proposition: your test or scraping code stays the same. The browser runs somewhere else.

What Browserless Actually Is

Browserless runs Chromium (and Chrome) in a containerized environment and exposes two interfaces:

  1. WebSocket endpoint — drop-in replacement for puppeteer.launch() or playwright.chromium.launch(). Your existing automation code connects to a remote browser instead of a local one.
  2. REST API — fire-and-forget endpoints for common tasks: screenshots, PDFs, content extraction, scraping. No Puppeteer/Playwright required for simple cases.

The hosted version is at browserless.io. The self-hosted version is a Docker image you run anywhere.

Hosted vs Self-Hosted

Hosted (browserless.io)

You get an API key and a WebSocket URL like wss://chrome.browserless.io?token=YOUR_API_KEY. No infrastructure to manage. Pricing is based on concurrent sessions and monthly usage. Good for teams that don't want to maintain browser infrastructure.

Self-hosted

Pull the Docker image, configure environment variables, run it. You control concurrency limits, memory, timeouts, and data residency. No per-request cost beyond your hosting. The image is ghcr.io/browserless/chromium (v2+) or browserless/chrome (v1).

The self-hosted path makes sense if you have high volume, strict data requirements, or want to run inside a private network alongside your test infrastructure.

REST API Overview

The REST API handles one-off tasks without requiring a persistent WebSocket connection. All endpoints accept a JSON body describing what to do.

/screenshot

Capture a PNG or JPEG of any URL.

curl -X POST \
  "https://chrome.browserless.io/screenshot?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "options": {
      "fullPage": true,
      "type": "png"
    }
  }' \
  --output screenshot.png

/pdf

Generate a PDF from a URL or raw HTML.

curl -X POST \
  "https://chrome.browserless.io/pdf?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "options": {
      "printBackground": true,
      "format": "A4"
    }
  }' \
  --output page.pdf

/content

Fetch the fully-rendered HTML of a page after JavaScript execution. Useful when you need the DOM after React/Vue/Angular has run.

curl -X POST \
  "https://chrome.browserless.io/content?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com"}' 

/scrape

Extract structured data from a page without writing full Puppeteer code. You pass CSS selectors and get back the matching elements.

curl -X POST \
  "https://chrome.browserless.io/scrape?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://news.ycombinator.com",
    "elements": [
      { "selector": ".titleline a" }
    ]
  }'

Response:

{
  "data": [
    {
      "selector": ".titleline a",
      "results": [
        { "text": "Show HN: ...", "href": "https://..." },
        ...
      ]
    }
  ]
}

/function

Execute arbitrary JavaScript in a browser context. You send a function body as a string; Browserless runs it and returns the result.

curl -X POST \
  "https://chrome.browserless.io/function?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "module.exports = async ({ page }) => { await page.goto(\"https://example.com\"); return page.title(); }"
  }'

WebSocket Endpoint for Puppeteer/Playwright

The WebSocket interface is the primary way to run full automation scripts. The endpoint is:

wss://chrome.browserless.io?token=YOUR_TOKEN

For self-hosted: ws://localhost:3000

Both Puppeteer and Playwright support connecting to an existing browser over WebSocket instead of launching a new process. This is covered in detail in the Puppeteer and Playwright integration posts in this series, but the short version:

Puppeteer:

const browser = await puppeteer.connect({
  browserWSEndpoint: 'wss://chrome.browserless.io?token=YOUR_TOKEN'
});

Playwright:

const browser = await chromium.connectOverCDP(
  'wss://chrome.browserless.io?token=YOUR_TOKEN'
);

Once connected, the API is identical to a locally launched browser.

Your First REST API Request

Here's a complete Node.js example that takes a screenshot without Puppeteer or Playwright — just fetch:

const fs = require('fs');

const TOKEN = process.env.BROWSERLESS_TOKEN;

async function screenshot(url, outputPath) {
  const response = await fetch(
    `https://chrome.browserless.io/screenshot?token=${TOKEN}`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        url,
        options: {
          fullPage: true,
          type: 'png'
        }
      })
    }
  );

  if (!response.ok) {
    const text = await response.text();
    throw new Error(`Browserless error: ${response.status}${text}`);
  }

  const buffer = await response.arrayBuffer();
  fs.writeFileSync(outputPath, Buffer.from(buffer));
  console.log(`Screenshot saved to ${outputPath}`);
}

screenshot('https://example.com', 'output.png');

No Chrome install required. No apt-get install chromium-browser. No sandbox flags. It just works.

What to Check Before Committing

A few things to verify before building on Browserless:

  • Token security: Never hardcode your API token. Use environment variables.
  • Timeout defaults: The default request timeout is 30 seconds. Long-running scripts need ?timeout=60000 in the query string.
  • Concurrent session limits: The hosted free tier limits concurrent sessions. If you're running parallel tests, check your plan or self-host with your own limits.
  • Version pinning: Self-hosted, pin the Docker image tag. browserless/chrome:latest will drift and break your tests.

Next Steps

The REST API is the fastest way to get started, but the real power comes from running full Puppeteer and Playwright test suites against a remote browser. The rest of this series covers:

  • Setting up self-hosted Browserless with Docker
  • Connecting Playwright to Browserless
  • Connecting Puppeteer to Browserless
  • Scaling concurrent headless tests in CI

Read more

Start now free