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

594 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:**
```typescript
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:**
```typescript
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**
```typescript
await browser.close(); // Don't leave hanging
```
2. **Use session reuse**
```typescript
// Reuse session instead of launching new browser
const browser = await puppeteer.connect(env.MYBROWSER, sessionId);
```
3. **Timeout management**
```typescript
// Set appropriate timeouts (don't wait forever)
await page.goto(url, { timeout: 30000 });
```
4. **Cache aggressively**
```typescript
// 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**
```typescript
// ❌ 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**
```typescript
// Maintain pool of warm browsers
// Reuse instead of launching new ones
```
3. **Queue requests**
```typescript
// Limit concurrent operations
const queue = new PQueue({ concurrency: 3 });
await Promise.all(urls.map(url => queue.add(() => process(url))));
```
4. **Incognito contexts**
```typescript
// 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**:
```typescript
// 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**:
```typescript
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**:
```typescript
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**:
```typescript
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
```typescript
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**
```typescript
const limits = await puppeteer.limits(env.MYBROWSER);
if (limits.activeSessions.length > 15) {
console.warn("High concurrency detected:", limits.activeSessions.length);
}
```
2. **Track hourly usage**
```typescript
const usage = await getHourlyUsage();
if (usage.browserHours > 1) {
console.warn("High browser usage this hour:", usage.browserHours);
}
```
3. **Set budget limits**
```typescript
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
- **Official Pricing Docs**: https://developers.cloudflare.com/browser-rendering/platform/pricing/
- **Limits Docs**: https://developers.cloudflare.com/browser-rendering/platform/limits/
- **Dashboard**: https://dash.cloudflare.com/?to=/:account/workers/browser-rendering
- **Request Higher Limits**: https://forms.gle/CdueDKvb26mTaepa9
---
**Last Updated**: 2025-10-22