Browser Rendering API killed my Firecrawl bill

Firecrawl charges $83/month for 100K pages. Cloudflare’s /markdown endpoint costs me $0 at my volume. I switched.

I was building a content pipeline that scrapes documentation, research papers, and blogs then feeds them into a RAG system. Firecrawl worked, but I was paying for features I didn’t need. Proxy rotation, anti-detection, managed infrastructure. My sources are public technical content. They don’t block scrapers.

Then I found Cloudflare’s /markdown endpoint. One API call. URL in, Markdown out. No Puppeteer. No HTML parsing. No external service.

The /markdown endpoint

Most people don’t know this exists. It’s buried in the docs, added in July 2025 when Browser Rendering went GA.

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",
    "gotoOptions": { "waitUntil": "networkidle0" }
  }'

That’s it. Cloudflare spins up a headless browser, renders the page, waits for JavaScript, converts to Markdown, returns it. One call.

The waitUntil: "networkidle0" parameter matters. Without it, you get half-rendered React apps. With it, you wait for all network activity to stop.

In code:

export async function getMarkdown(
  url: string,
  config: BrowserRenderingConfig
): Promise<BrowserRenderingResponse<string>> {
  const response = await fetch(
    `https://api.cloudflare.com/client/v4/accounts/${config.accountId}/browser-rendering/markdown`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${config.apiToken}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        url,
        gotoOptions: { waitUntil: 'networkidle0' },
        rejectRequestPattern: ['/^.*\\.(css)/'],  // Skip CSS for cleaner output
      }),
    }
  );

  if (!response.ok) {
    return { success: false, error: `HTTP ${response.status}` };
  }

  const markdown = await response.text();
  return { success: true, data: markdown };
}

Compare this to the Firecrawl flow. Call their API, get HTML, run it through a markdown converter, handle edge cases. Cloudflare does it in one round trip.

The real cost math

Browser Rendering bills by browser-hours, not requests. This changes everything.

TierIncludedOverage
Workers Free10 min/day, 3 concurrentN/A
Workers Paid ($5/mo)10 hours/month, 10 concurrent$0.09/browser hour

Average page render takes 2-5 seconds. Do the math:

VolumeBrowser TimeCloudflare CostFirecrawl Cost
3,000 pages/mo~4 hours$0 (in free tier)$16
10,000 pages/mo~14 hours$0.36$83
100,000 pages/mo~140 hours$11.70$83

Under 10K pages/month, you’re effectively free. The 10 included hours cover it. I’m scraping around 5K pages/month. My Browser Rendering bill is $0.

The billing also rounds favorably. Browser time is totaled daily in seconds, then aggregated monthly and rounded to the nearest hour. Light usage disappears.

Queue integration

The scraping runs through Cloudflare Queues for backpressure:

export default {
  async queue(batch: MessageBatch, env: Env): Promise<void> {
    for (const message of batch.messages) {
      const { url, contentId } = message.body as CrawlJob;

      const result = await getMarkdown(url, {
        accountId: env.CLOUDFLARE_ACCOUNT_ID,
        apiToken: env.BROWSER_RENDERING_TOKEN,
      });

      if (result.success) {
        await storeContent(env.DB, contentId, result.data);
        message.ack();
      } else {
        message.retry();
      }
    }
  }
}

Nothing special here. Queue handles retries, batch processing, and backpressure. Browser Rendering handles the rendering. They compose cleanly.

What doesn’t work

Browser Rendering has real limitations. So does Firecrawl.

Failure ModeCloudflare BRFirecrawl
Bot detectionBlockedBetter (proxy rotation)
Login-requiredNo sessionLimited
Heavy SPAsNeeds configAuto-handles
CAPTCHAsNoNo
PaywallsNoNo
LinkedIn/TwitterBlockedBlocked

Firecrawl’s proxy rotation and anti-detection headers help with sites that actively block scrapers. If you’re scraping e-commerce or social media at scale, that matters.

I’m scraping documentation sites, blogs, research papers, company pages. None of them care. They don’t block scrapers. They want to be indexed.

For 90% of what I ingest, Browser Rendering works identically to Firecrawl at a fraction of the cost.

The escape hatch

If I ever need the features Firecrawl bundles, I can add them:

CapabilityImplementationEffort
Proxy rotationBrightData/Oxylabs API in Worker1-2 hours
User-agent rotationRandom UA per request30 min
Session persistenceDurable Objects for cookies2-3 hours
CAPTCHA solving2Captcha API when detected1-2 hours

Firecrawl is a product. Browser Rendering is infrastructure. Firecrawl bundles solutions into a monthly fee. Cloudflare gives you primitives to build what you need.

I haven’t needed proxy rotation because my sources don’t require it. If that changes, I’ll add it. Until then, I’m not paying for features I don’t use.

The bottom line

If you’re building a RAG pipeline from public technical content docs, papers, blogs then Firecrawl is overkill. You’re paying $83/month for proxy rotation and anti-detection that you’ll never trigger.

Cloudflare’s /markdown endpoint does the same job for effectively $0 under 10K pages/month. One API call. No infrastructure. No parsing.

The only thing Firecrawl gave me was convenience. Browser Rendering gives me control and costs nothing.