Files
gh-jezweb-claude-skills-ski…/references/pricing-and-limits.md
2025-11-30 08:24:08 +08:00

14 KiB
Raw Permalink Blame History

Pricing and Limits Reference

Complete breakdown of Cloudflare Browser Rendering pricing, limits, and cost optimization strategies.


Pricing Overview

Browser Rendering is billed on two metrics:

  1. Duration - Total browser hours used
  2. Concurrency - Monthly average of concurrent browsers (Workers Bindings only)

Free Tier (Workers Free Plan)

Feature Limit
Browser Duration 10 minutes per day
Concurrent Browsers 3 per account
New Browsers per Minute 3 per minute
REST API Requests 6 per minute
Browser Timeout (Idle) 60 seconds
Max Session Duration No hard limit (closes on idle timeout)

Free Tier Use Cases

Good for:

  • Development and testing
  • Personal projects
  • Low-traffic screenshot services (<100 requests/day)
  • Learning and experimentation

Not suitable for:

  • Production applications
  • High-traffic services
  • Long-running scraping jobs
  • Batch operations (>3 concurrent browsers)

Paid Tier (Workers Paid Plan)

Included Limits

Feature Included
Browser Duration 10 hours per month
Concurrent Browsers 10 (monthly average)
New Browsers per Minute 30 per minute
REST API Requests 180 per minute
Max Concurrent Browsers 30 per account
Browser Timeout 60 seconds (extendable to 10 minutes with keep_alive)

Beyond Included Limits

Metric Price
Additional Browser Hours $0.09 per hour
Additional Concurrent Browsers $2.00 per browser (monthly average)

Requesting Higher Limits

If you need more than:

  • 30 concurrent browsers
  • 30 new browsers per minute
  • 180 REST API requests per minute

Request higher limits: https://forms.gle/CdueDKvb26mTaepa9


Rate Limits

Per-Second Enforcement

Rate limits are enforced per-second, not per-minute.

Example: 180 requests per minute = 3 requests per second

This means:

  • Cannot send all 180 requests at once
  • Must spread evenly over the minute (3/second)

Implementation:

async function rateLimitedLaunch(env: Env): Promise<Browser> {
  const limits = await puppeteer.limits(env.MYBROWSER);

  if (limits.allowedBrowserAcquisitions === 0) {
    const delay = limits.timeUntilNextAllowedBrowserAcquisition;
    await new Promise(resolve => setTimeout(resolve, delay));
  }

  return await puppeteer.launch(env.MYBROWSER);
}

Free Tier Rate Limits

  • Concurrent browsers: 3
  • New browsers/minute: 3 (= 1 every 20 seconds)
  • REST API requests/minute: 6 (= 1 every 10 seconds)

Paid Tier Rate Limits

  • Concurrent browsers: 30 (default, can request higher)
  • New browsers/minute: 30 (= 1 every 2 seconds)
  • REST API requests/minute: 180 (= 3 per second)

Duration Billing

How It Works

  1. Daily Totals: Cloudflare sums all browser usage each day (in seconds)
  2. Monthly Total: Sum of all daily totals
  3. Rounded to Hours: Total rounded to nearest hour
  4. Billed: Total hours minus 10 included hours

Example:

  • Day 1: 60 seconds (1 minute)
  • Day 2: 120 seconds (2 minutes)
  • ...
  • Day 30: 90 seconds (1.5 minutes)
  • Monthly Total: 45 minutes = 0.75 hours (rounded to 1 hour)
  • Billable: 1 hour - 10 included = 0 hours (still within free allowance)

Failed Requests

Failed requests are NOT billed if they fail with waitForTimeout error.

Example:

try {
  await page.goto(url, { timeout: 30000 });
} catch (error) {
  // If this times out, browser time is NOT charged
  console.log("Navigation timeout - not billed");
}

Duration Optimization

Minimize browser time:

  1. Close browsers promptly

    await browser.close(); // Don't leave hanging
    
  2. Use session reuse

    // Reuse session instead of launching new browser
    const browser = await puppeteer.connect(env.MYBROWSER, sessionId);
    
  3. Timeout management

    // Set appropriate timeouts (don't wait forever)
    await page.goto(url, { timeout: 30000 });
    
  4. Cache aggressively

    // Cache screenshots in KV to avoid re-rendering
    const cached = await env.KV.get(url, { type: "arrayBuffer" });
    if (cached) return new Response(cached);
    

Concurrency Billing

How It Works

  1. Daily Peak: Cloudflare records highest concurrent browsers each day
  2. Monthly Average: Average of all daily peaks
  3. Billed: Average - 10 included browsers

Formula:

monthly_average = sum(daily_peaks) / days_in_month
billable = max(0, monthly_average - 10)
cost = billable * $2.00

Example:

  • Days 1-15: 10 concurrent browsers (daily peak)
  • Days 16-30: 20 concurrent browsers (daily peak)
  • Monthly average: ((10 × 15) + (20 × 15)) / 30 = 15 browsers
  • Billable: 15 - 10 = 5 browsers
  • Cost: 5 × $2.00 = $10.00

Concurrency vs Duration

Scenario Concurrency Impact Duration Impact
1 browser for 10 hours 1 concurrent browser 10 browser hours
10 browsers for 1 hour 10 concurrent browsers 10 browser hours
100 browsers for 6 minutes 100 concurrent browsers (!!) 10 browser hours

Key Insight: Short bursts of high concurrency are EXPENSIVE.

Concurrency Optimization

Minimize concurrent browsers:

  1. Use multiple tabs

    // ❌ Bad: 10 browsers
    for (const url of urls) {
      const browser = await puppeteer.launch(env.MYBROWSER);
      // ...
    }
    
    // ✅ Good: 1 browser, 10 tabs
    const browser = await puppeteer.launch(env.MYBROWSER);
    await Promise.all(urls.map(async url => {
      const page = await browser.newPage();
      // ...
    }));
    
  2. Session reuse

    // Maintain pool of warm browsers
    // Reuse instead of launching new ones
    
  3. Queue requests

    // Limit concurrent operations
    const queue = new PQueue({ concurrency: 3 });
    await Promise.all(urls.map(url => queue.add(() => process(url))));
    
  4. Incognito contexts

    // Share browser, isolate sessions
    const context1 = await browser.createBrowserContext();
    const context2 = await browser.createBrowserContext();
    

Cost Examples

Example 1: Screenshot Service

Scenario:

  • 10,000 screenshots per month
  • 3 second average per screenshot
  • No caching, no session reuse

Duration:

  • 10,000 × 3 seconds = 30,000 seconds = 8.33 hours
  • Billable: 8.33 - 10 = 0 hours (within free allowance)
  • Duration Cost: $0.00

Concurrency:

  • Assume 100 requests/hour during peak (9am-5pm weekdays)
  • 100 requests/hour ÷ 3600 seconds = 0.028 browsers/second
  • Peak: ~3 concurrent browsers
  • Daily peak (weekdays): 3 browsers
  • Daily peak (weekends): 1 browser
  • Monthly average: ((3 × 22) + (1 × 8)) / 30 = 2.5 browsers
  • Billable: 2.5 - 10 = 0 (within free allowance)
  • Concurrency Cost: $0.00

Total: $0.00 (within free tier!)


Example 2: Heavy Scraping

Scenario:

  • 1,000 URLs per day
  • 10 seconds average per URL
  • Batch processing (10 concurrent browsers)

Duration:

  • 1,000 × 10 seconds × 30 days = 300,000 seconds = 83.33 hours
  • Billable: 83.33 - 10 = 73.33 hours
  • Duration Cost: 73.33 × $0.09 = $6.60

Concurrency:

  • Daily peak: 10 concurrent browsers (every day)
  • Monthly average: 10 browsers
  • Billable: 10 - 10 = 0 (within free allowance)
  • Concurrency Cost: $0.00

Total: $6.60/month


Example 3: Burst Traffic

Scenario:

  • Newsletter sent monthly with screenshot links
  • 10,000 screenshots in 1 hour
  • Each screenshot: 3 seconds

Duration:

  • 10,000 × 3 seconds = 30,000 seconds = 8.33 hours
  • Billable: 8.33 - 10 = 0 hours
  • Duration Cost: $0.00

Concurrency:

  • 10,000 screenshots in 1 hour = 166 requests/minute
  • At 3 seconds each: ~8.3 concurrent browsers
  • But limited to 30 max, so likely queueing
  • Daily peak: 30 browsers (rate limit)
  • Monthly average: (30 × 1 day + 1 × 29 days) / 30 = 1.97 browsers
  • Billable: 1.97 - 10 = 0
  • Concurrency Cost: $0.00

Total: $0.00

Note: Would hit rate limits. Better to spread over longer period or request higher limits.


Example 4: Production API (Optimized)

Scenario:

  • 100,000 screenshots per month
  • Session reuse + KV caching (90% cache hit rate)
  • 10,000 actual browser renderings
  • 5 seconds average per render
  • Maintain pool of 5 warm browsers

Duration:

  • 10,000 × 5 seconds = 50,000 seconds = 13.89 hours
  • Billable: 13.89 - 10 = 3.89 hours
  • Duration Cost: 3.89 × $0.09 = $0.35

Concurrency:

  • Maintain pool of 5 browsers (keep_alive)
  • Daily peak: 5 browsers
  • Monthly average: 5 browsers
  • Billable: 5 - 10 = 0
  • Concurrency Cost: $0.00

Total: $0.35/month for 100k requests!

ROI: $0.0000035 per screenshot


Cost Optimization Strategies

1. Aggressive Caching

Strategy: Cache screenshots/PDFs in KV or R2

Impact:

  • Reduces browser hours by 80-95%
  • Reduces concurrency needs
  • Faster response times

Implementation:

// Check cache first
const cached = await env.KV.get(url, { type: "arrayBuffer" });
if (cached) return new Response(cached);

// Generate and cache
const screenshot = await generateScreenshot(url);
await env.KV.put(url, screenshot, { expirationTtl: 86400 });

Cost Savings: 80-95% reduction


2. Session Reuse

Strategy: Maintain pool of warm browsers, reuse sessions

Impact:

  • Reduces cold start time
  • Lower concurrency charges
  • Better throughput

Implementation: See session-reuse.ts template

Cost Savings: 30-50% reduction


3. Multiple Tabs

Strategy: Use tabs instead of multiple browsers

Impact:

  • 10-50x reduction in concurrency
  • Minimal duration increase
  • Much cheaper

Implementation:

const browser = await puppeteer.launch(env.MYBROWSER);
await Promise.all(urls.map(async url => {
  const page = await browser.newPage();
  // process
  await page.close();
}));
await browser.close();

Cost Savings: 90%+ reduction in concurrency charges


4. Appropriate Timeouts

Strategy: Set reasonable timeouts, don't wait forever

Impact:

  • Prevents hanging browsers
  • Reduces wasted duration
  • Better error handling

Implementation:

await page.goto(url, {
  timeout: 30000,  // 30 second max
  waitUntil: "networkidle0"
});

Cost Savings: 20-40% reduction


5. Request Queueing

Strategy: Limit concurrent operations to stay within limits

Impact:

  • Avoid rate limit errors
  • Predictable costs
  • Better resource utilization

Implementation:

import PQueue from "p-queue";

const queue = new PQueue({ concurrency: 5 });

await Promise.all(urls.map(url =>
  queue.add(() => processUrl(url))
));

Cost Savings: Avoids rate limit charges


Monitoring Usage

Dashboard

View usage in Cloudflare Dashboard:

https://dash.cloudflare.com/?to=/:account/workers/browser-rendering

Metrics available:

  • Total browser hours used
  • REST API requests
  • Concurrent browsers (graph)
  • Cost estimates

Response Headers

REST API returns browser time used:

X-Browser-Ms-Used: 2340

(Browser time in milliseconds for that request)

Custom Tracking

interface UsageMetrics {
  date: string;
  browserHours: number;
  peakConcurrency: number;
  requests: number;
  cacheHitRate: number;
}

// Track in D1 or Analytics Engine
await env.ANALYTICS.writeDataPoint({
  indexes: [date],
  blobs: ["browser_usage"],
  doubles: [browserHours, peakConcurrency, requests]
});

Cost Alerts

Set Up Alerts

  1. Monitor daily peaks

    const limits = await puppeteer.limits(env.MYBROWSER);
    if (limits.activeSessions.length > 15) {
      console.warn("High concurrency detected:", limits.activeSessions.length);
    }
    
  2. Track hourly usage

    const usage = await getHourlyUsage();
    if (usage.browserHours > 1) {
      console.warn("High browser usage this hour:", usage.browserHours);
    }
    
  3. Set budget limits

    const monthlyBudget = 50; // $50/month
    const currentCost = await estimateCurrentCost();
    if (currentCost > monthlyBudget * 0.8) {
      console.warn("Approaching monthly budget:", currentCost);
    }
    

Best Practices Summary

  1. Always cache screenshots/PDFs in KV or R2
  2. Reuse sessions instead of launching new browsers
  3. Use multiple tabs instead of multiple browsers
  4. Set appropriate timeouts to prevent hanging
  5. Monitor usage in dashboard and logs
  6. Queue requests to stay within rate limits
  7. Test caching to optimize hit rate
  8. Profile operations to identify slow requests
  9. Use incognito contexts for session isolation
  10. Request higher limits if needed for production

Common Questions

Q: Are failed requests billed?

A: No. Requests that fail with waitForTimeout error are NOT billed.

Q: How is concurrency calculated?

A: Monthly average of daily peak concurrent browsers.

Q: Can I reduce my bill?

A: Yes! Use caching, session reuse, and multiple tabs. See optimization strategies above.

Q: What if I hit limits?

A: Implement queueing, or request higher limits: https://forms.gle/CdueDKvb26mTaepa9

Q: Is there a free tier?

A: Yes! 10 minutes/day browser time, 3 concurrent browsers.

Q: How do I estimate costs?

A: Monitor usage in dashboard, then calculate:

  • Duration: (hours - 10) × $0.09
  • Concurrency: (avg - 10) × $2.00

References


Last Updated: 2025-10-22