Claude
Skills
Sign in
Back

browser-rendering

Included with Lifetime
$97 forever

Headless Chrome automation for web scraping, screenshots, PDFs, and testing at the edge. Load when capturing page screenshots, generating PDFs, scraping dynamic content, extracting structured data, or automating browser interactions. Supports REST API, Puppeteer, Playwright, and Stagehand.

Backend & APIs

What this skill does


# Browser Rendering

Cloudflare Browser Rendering provides headless Chrome instances on the global edge network for web scraping, screenshots, PDF generation, and automated testing. Choose from **REST API** for simple tasks or **Workers Bindings** (Puppeteer, Playwright, Stagehand) for advanced automation.

## Choosing an Integration Method

| Method | Use Case | Complexity |
|--------|----------|------------|
| **REST API** | Simple screenshots, PDFs, markdown extraction, structured data | Low - just HTTP requests |
| **Puppeteer** | Industry-standard Chrome automation, porting existing scripts | Medium - familiar API |
| **Playwright** | Modern cross-browser automation, AI agent integration (MCP) | Medium - developer-friendly |
| **Stagehand** | AI-native automation with natural language selectors | Low - resilient to site changes |

## REST API Method

For simple, stateless tasks like capturing screenshots or generating PDFs without writing complex scripts.

### Setup

Create an API token with `Browser Rendering - Edit` permission in the Cloudflare dashboard.

### Available Endpoints

| Endpoint | Purpose |
|----------|---------|
| `/content` | Fetch fully-rendered HTML |
| `/screenshot` | Capture page screenshot |
| `/pdf` | Generate PDF document |
| `/snapshot` | Take webpage snapshot |
| `/scrape` | Extract HTML elements |
| `/json` | Capture structured data using AI |
| `/links` | Retrieve all links from page |
| `/markdown` | Extract markdown content |

### REST API Example

```bash
curl -X POST \
  https://api.cloudflare.com/client/v4/accounts/{account_id}/browser-rendering/screenshot \
  -H "Authorization: Bearer {api_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "fullPage": true,
    "type": "png"
  }' \
  --output screenshot.png
```

**Monitoring usage:** Check the `X-Browser-Ms-Used` header in responses to see browser time consumed (in milliseconds).

## Workers Bindings Method

For complex workflows, persistent sessions, and custom automation. Requires deploying a Cloudflare Worker.

### Configuration

Add browser binding to `wrangler.jsonc`:

```jsonc
{
  "name": "browser-automation",
  "main": "src/index.ts",
  "compatibility_date": "2025-09-17",
  "compatibility_flags": ["nodejs_compat"],
  "browser": {
    "binding": "BROWSER"
  }
}
```

### Option A: Puppeteer

Industry-standard Chrome automation API, ideal for porting existing scripts.

**Installation:**
```bash
npm install @cloudflare/puppeteer --save-dev
```

**Basic example:**

```typescript
import puppeteer from "@cloudflare/puppeteer";

interface Env {
  BROWSER: Fetcher;
}

export default {
  async fetch(request, env): Promise<Response> {
    const { searchParams } = new URL(request.url);
    const url = searchParams.get("url");

    if (!url) {
      return new Response("Missing ?url parameter", { status: 400 });
    }

    const browser = await puppeteer.launch(env.BROWSER);
    try {
      const page = await browser.newPage();
      await page.goto(url);
      const text = await page.$eval("body", (el) => el.textContent);

      return Response.json({ bodyText: text });
    } finally {
      await browser.close();
    }
  },
} satisfies ExportedHandler<Env>;
```

### Option B: Playwright (Recommended for New Projects)

Modern browser automation with developer-friendly API and AI agent integration.

**Installation:**
```bash
npm install @cloudflare/playwright --save-dev
```

**Current version:** v1.57.0

**Basic example:**

```typescript
import { launch } from "@cloudflare/playwright";

interface Env {
  BROWSER: Fetcher;
}

export default {
  async fetch(request, env): Promise<Response> {
    const browser = await launch(env.BROWSER);
    try {
      const page = await browser.newPage();
      await page.goto("https://example.com");
      
      const title = await page.title();
      const screenshot = await page.screenshot({ type: "png" });

      return new Response(screenshot, {
        headers: { "Content-Type": "image/png" },
      });
    } finally {
      await browser.close();
    }
  },
} satisfies ExportedHandler<Env>;
```

**Playwright advantages:**
- Built-in test assertions with `expect()` from `@cloudflare/playwright/test`
- Trace files for debugging (downloadable `trace.zip`)
- Storage state for persisting cookies/localStorage
- MCP integration for AI agents

### Option C: Stagehand (AI-Native)

Uses natural language selectors instead of brittle CSS selectors, making automation resilient to website changes.

**Example:**
```typescript
// Instead of: await page.click("#submit-button")
// Use natural language: await page.act("click the submit button")
```

Stagehand is ideal for AI agents that need to autonomously navigate websites without breaking when the DOM structure changes.

## FIRST: Installation (Workers Bindings)

Choose your library and install:

```bash
# Puppeteer (industry standard)
npm install @cloudflare/puppeteer --save-dev

# OR Playwright (modern, recommended)
npm install @cloudflare/playwright --save-dev

# OR Stagehand (AI-native)
npm install @cloudflare/stagehand --save-dev
```

## When to Use Browser Rendering

| Use Case | Recommended Method | Why |
|----------|-------------------|-----|
| Simple screenshots | REST API | No code required, just HTTP request |
| PDF generation | REST API or Puppeteer | REST for simple, Puppeteer for custom headers/auth |
| Web scraping | Puppeteer or Playwright | Full DOM access, complex navigation |
| Automated testing | Playwright | Built-in assertions, trace files |
| SEO analysis | REST API `/markdown` or `/json` | AI-powered structured extraction |
| AI agents | Playwright (MCP) or Stagehand | Natural language automation, resilient selectors |
| Page monitoring | REST API or Puppeteer | REST for periodic checks, Puppeteer for complex flows |

## Quick Reference

| Operation | API |
|-----------|-----|
| Launch browser | `await puppeteer.launch(env.BROWSER_RENDERING)` |
| Create new page | `await browser.newPage()` |
| Navigate to URL | `await page.goto(url)` |
| Get HTML content | `await page.content()` |
| Extract text | `await page.$eval("selector", el => el.textContent)` |
| Take screenshot | `await page.screenshot({ type: "png" })` |
| Generate PDF | `await page.pdf({ format: "A4" })` |
| Close browser | `await browser.close()` |

## Basic Page Scraping

```typescript
import puppeteer from "@cloudflare/puppeteer";

interface Env {
  BROWSER_RENDERING: Fetcher;
}

export default {
  async fetch(request, env): Promise<Response> {
    const { searchParams } = new URL(request.url);
    let url = searchParams.get("url");

    if (url) {
      url = new URL(url).toString(); // normalize
      const browser = await puppeteer.launch(env.BROWSER_RENDERING);
      const page = await browser.newPage();
      await page.goto(url);
      
      // Parse the page content
      const content = await page.content();
      
      // Find text within the page content
      const text = await page.$eval("body", (el) => el.textContent);
      
      // Do something with the text
      // e.g. log it to the console, write it to KV, or store it in a database.
      console.log(text);

      // Ensure we close the browser session
      await browser.close();

      return Response.json({
        bodyText: text,
      });
    } else {
      return Response.json({
        error: "Please add an ?url=https://example.com/ parameter"
      }, { status: 400 });
    }
  },
} satisfies ExportedHandler<Env>;
```

## Screenshot Capture

Capture screenshots of web pages as PNG or JPEG:

```typescript
import puppeteer from "@cloudflare/puppeteer";

interface Env {
  BROWSER_RENDERING: Fetcher;
}

export default {
  async fetch(request, env): Promise<Response> {
    const { searchParams } = new URL(request.url);
    const url = searchParams.get("url");

    if (!url) {
      return new Response("Missing ?url parameter", { status: 400 });
    }

    const browser = await puppeteer

Related in Backend & APIs