This site is not affiliated with or endorsed by Cloudflare, Inc. It showcases deployable experiments across most Cloudflare products.
Browser Rendering

Rendered Text

Extract JavaScript-rendered visible text with Cloudflare Browser Rendering - SPA text scrape for search and AI ingestion

Load any URL in a headless browser and extract the visible page text after JavaScript has executed.

API Reference

GET /text

Returns the document title and normalized visible text from document.body.innerText.

Prop

Type

Example Request

curl "https://your-worker.workers.dev/text?url=https://www.cloudflare.com"

Success Response

url string

The normalized URL that was loaded

title string

The page <title> after rendering

text string

Visible text with whitespace collapsed

textLength number

Length of the returned text in characters

truncated boolean

Whether the text was truncated at 50,000 characters

{
  "url": "https://www.cloudflare.com/",
  "title": "Connect, protect, and build everywhere",
  "text": "Connect, protect, and build everywhere ...",
  "textLength": 1234,
  "truncated": false
}

Error Response

{
  "error": "Missing or invalid query parameter: url",
  "code": "INVALID_URL"
}

Error Codes

  • 400 - Missing or invalid url parameter
  • 502 - Navigation or extraction failed (RENDER_ERROR)

Use Cases

  • Scrape single-page apps and client-rendered content
  • Feed rendered page text to LLM pipelines
  • Compare static fetch vs browser-rendered output
  • Build search indexes for JavaScript-heavy sites

Limitations

  • Requires Browser Rendering enabled on your Cloudflare account
  • Local development needs npx wrangler dev --remote
  • Extracted text is truncated at a maximum length
  • Navigation timeout on slow or script-heavy pages

Use in your project

Copy these files into an existing Worker. Prefer Deployment to try the full experiment first. Source: apps/experiments/rendered-text.

DependenciesNoneBindingsBrowser RenderingPlatformWorkers runtime
import puppeteer from "@cloudflare/puppeteer";import type { Page } from "@cloudflare/puppeteer";import { DEFAULT_VIEWPORT, NAVIGATION_TIMEOUT_MS } from "../constants/defaults";import { normalizeTitle, truncateText } from "./text";import type { RenderedTextResponse } from "../types/text";export async function withBrowserPage<T>(  browserBinding: Fetcher,  url: string,  fn: (page: Page) => Promise<T>): Promise<T> {  let browser: Awaited<ReturnType<typeof puppeteer.launch>> | null = null;  try {    browser = await puppeteer.launch(browserBinding);    const page = await browser.newPage();    await page.setViewport(DEFAULT_VIEWPORT);    await page.goto(url, {      waitUntil: "networkidle0",      timeout: NAVIGATION_TIMEOUT_MS,    });    return await fn(page);  } finally {    if (browser) {      try {        await browser.close();      } catch {        /* ignore close errors */      }    }  }}export async function extractRenderedText(  browserBinding: Fetcher,  url: string): Promise<RenderedTextResponse> {  const raw = await withBrowserPage(browserBinding, url, async (page) => {    const title = await page.title();    const text = await page.$eval("body", (body) => body.innerText ?? "");    return { title, text };  });  const title = normalizeTitle(raw.title);  const { text, truncated } = truncateText(raw.text);  return {    url,    title,    text,    textLength: text.length,    truncated,  };}

Deployment

Click the deploy button

Deploy to Cloudflare Workers

Enable Browser Rendering

Browser Rendering must be enabled on your account.

Test your deployment

curl "https://your-worker.workers.dev/text?url=https://www.cloudflare.com"

Local Development

cd apps/experiments/rendered-text
npm install
npm run dev

Test locally:

curl "http://localhost:8787/text?url=https://www.cloudflare.com"

Cloudflare Features Used

On this page