Skip to content

Changelog

New updates and improvements at Cloudflare.

Playwright MCP server is now compatible with Browser Rendering

We're excited to share that you can now use the Playwright MCP ↗︎ server with Browser Rendering.

Once you deploy the server, you can use any MCP client with it to interact with Browser Rendering. This allows you to run AI models that can automate browser tasks, such as taking screenshots, filling out forms, or scraping data.

Access Analytics

Playwright MCP is available as an npm package at @cloudflare/playwright-mcp ↗︎. To install it, type:

npm i -D @cloudflare/playwright-mcp

Deploying the server is then as easy as:

import { env } from "cloudflare:workers";
import { createMcpAgent } from "@cloudflare/playwright-mcp";

export const PlaywrightMCP = createMcpAgent(env.BROWSER);
export default PlaywrightMCP.mount("/sse");

Check out the full code at GitHub ↗︎.

Learn more about Playwright MCP in our documentation.

Browser Rendering REST API is Generally Available, with new endpoints and a free tier

We’re excited to announce Browser Rendering is now available on the Workers Free plan ↗︎, making it even easier to prototype and experiment with web search and headless browser use-cases when building applications on Workers.

The Browser Rendering REST API is now Generally Available, allowing you to control browser instances from outside of Workers applications. We've added three new endpoints to help automate more browser tasks:

  • Extract structured data – Use /json to retrieve structured data from a webpage.
  • Retrieve links – Use /links to pull all links from a webpage.
  • Convert to Markdown – Use /markdown to convert webpage content into Markdown format.

For example, to fetch the Markdown representation of a webpage:

Markdown examplebash
curl -X 'POST' 'https://api.cloudflare.com/client/v4/accounts/<accountId>/browser-rendering/markdown' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <apiToken>' \
  -d '{
    "url": "https://example.com"
  }'

For the full list of endpoints, check out our REST API documentation. You can also interact with Browser Rendering via the Cloudflare TypeScript SDK ↗︎.

We also recently landed support for Playwright in Browser Rendering for browser automation from Cloudflare Workers, in addition to Puppeteer, giving you more flexibility to test across different browser environments.

Visit the Browser Rendering docs to learn more about how to use headless browsers in your applications.

Playwright for Browser Rendering now available

We're excited to share that you can now use Playwright's browser automation capabilities ↗︎ from Cloudflare Workers.

Playwright ↗︎ is an open-source package developed by Microsoft that can do browser automation tasks; it's commonly used to write software tests, debug applications, create screenshots, and crawl pages. Like Puppeteer, we forked ↗︎ Playwright and modified it to be compatible with Cloudflare Workers and Browser Rendering.

Below is an example of how to use Playwright with Browser Rendering to test a TODO application using assertions:

Assertion examplets
import { launch, type BrowserWorker } from "@cloudflare/playwright";
import { expect } from "@cloudflare/playwright/test";

interface Env {
	MYBROWSER: BrowserWorker;
}

export default {
	async fetch(request: Request, env: Env) {
		const browser = await launch(env.MYBROWSER);
		const page = await browser.newPage();

		await page.goto("https://demo.playwright.dev/todomvc");

		const TODO_ITEMS = [
			"buy some cheese",
			"feed the cat",
			"book a doctors appointment",
		];

		const newTodo = page.getByPlaceholder("What needs to be done?");
		for (const item of TODO_ITEMS) {
			await newTodo.fill(item);
			await newTodo.press("Enter");
		}

		await expect(page.getByTestId("todo-title")).toHaveCount(TODO_ITEMS.length);

		await Promise.all(
			TODO_ITEMS.map((value, index) =>
				expect(page.getByTestId("todo-title").nth(index)).toHaveText(value),
			),
		);
	},
};

Playwright is available as an npm package at @cloudflare/playwright ↗︎ and the code is at GitHub ↗︎.

Learn more in our documentation.

New REST API is in open beta!

We've released a new REST API for Browser Rendering in open beta, making interacting with browsers easier than ever. This new API provides endpoints for common browser actions, with more to be added in the future.

With the REST API you can:

  • Capture screenshots – Use /screenshot to take a screenshot of a webpage from provided URL or HTML.
  • Generate PDFs – Use /pdf to convert web pages into PDFs.
  • Extract HTML content – Use /content to retrieve the full HTML from a page. Snapshot (HTML + Screenshot) – Use /snapshot to capture both the page's HTML and a screenshot in one request
  • Scrape Web Elements – Use /scrape to extract specific elements from a page.

For example, to capture a screenshot:

Screenshot examplebash
curl -X POST 'https://api.cloudflare.com/client/v4/accounts/<accountId>/browser-rendering/screenshot' \
  -H 'Authorization: Bearer <apiToken>' \
  -H 'Content-Type: application/json' \
  -d '{
    "html": "Hello World!",
    "screenshotOptions": {
      "type": "webp",
      "omitBackground": true
    }
  }' \
  --output "screenshot.webp"

Learn more in our documentation.

Increased Browser Rendering limits!

Browser Rendering now supports 10 concurrent browser instances per account and 10 new instances per minute, up from the previous limits of 2.

This allows you to launch more browser tasks from Cloudflare Workers.

To manage concurrent browser sessions, you can use Queues or Workflows:

index.jsjs
export default {
	async queue(batch, env) {
		for (const message of batch.messages) {
			const browser = await puppeteer.launch(env.BROWSER);
			const page = await browser.newPage();

			try {
				await page.goto(message.url, {
					waitUntil: message.waitUntil,
				});
				// Process page...
			} finally {
				await browser.close();
			}
		}
	},
};
index.tsts
interface QueueMessage {
	url: string;
	waitUntil: number;
}

export interface Env {
	BROWSER_QUEUE: Queue<QueueMessage>;
	BROWSER: Fetcher;
}

export default {
	async queue(batch: MessageBatch<QueueMessage>, env: Env): Promise<void> {
		for (const message of batch.messages) {
			const browser = await puppeteer.launch(env.BROWSER);
			const page = await browser.newPage();

			try {
				await page.goto(message.url, {
					waitUntil: message.waitUntil,
				});
				// Process page...
			} finally {
				await browser.close();
			}
		}
	},
};