Back to Blog

Building a Screenshot API on Cloudflare Workers: Architecture Deep Dive

PxShot is built entirely on Cloudflare's edge platform — Workers for logic, Browser Rendering for headless Chrome, D1 for the database, and R2 for storage. This architecture provides sub-second screenshot capture from 300+ edge locations worldwide. Here's how it all fits together.

Architecture Overview

Every screenshot request flows through this pipeline:

  1. Cloudflare Worker receives the request — Validates the API key, checks rate limits, and parses parameters
  2. Browser Rendering API — The Worker launches a headless Chromium instance via Cloudflare's Browser Rendering binding
  3. Page navigation and capture — The browser navigates to the target URL, waits for the page to finish loading, and takes a screenshot
  4. Image encoding — The screenshot is encoded in the requested format (PNG, JPEG, WebP, or PDF)
  5. Response delivery — The image is returned directly to the caller with appropriate caching headers

Why Cloudflare Workers?

Browser Rendering Binding

Cloudflare's Browser Rendering API provides a Puppeteer-compatible interface within Workers:

import puppeteer from '@cloudflare/puppeteer';

export default {
  async fetch(request, env) {
    const browser = await puppeteer.launch(env.BROWSER);
    const page = await browser.newPage();

    await page.setViewport({ width: 1280, height: 720 });
    await page.goto('https://example.com', { waitUntil: 'networkidle0' });

    const screenshot = await page.screenshot({ type: 'png' });
    await browser.close();

    return new Response(screenshot, {
      headers: { 'Content-Type': 'image/png' }
    });
  }
};

Database Layer: D1

User accounts, API keys, usage stats, and billing data are all stored in Cloudflare D1:

// Check API key and rate limits
const keyRow = await env.DB.prepare(
  'SELECT user_id, plan, monthly_usage FROM api_keys WHERE key_hash = ?'
).bind(keyHash).first();

if (!keyRow) return errorResponse('Invalid API key', 401);
if (keyRow.monthly_usage >= planLimits[keyRow.plan]) {
  return errorResponse('Monthly limit exceeded', 429);
}

Performance Characteristics

Challenges and Solutions

Browser session management

Each screenshot uses a fresh browser context to ensure isolation. This prevents state leakage between requests but requires efficient session lifecycle management.

Timeout handling

Some pages load slowly or hang indefinitely. PxShot enforces a maximum navigation timeout and falls back to capturing whatever has loaded so far.

Resource limits

Cloudflare Workers have CPU time limits. Complex pages that trigger heavy JavaScript execution need careful timeout management.

Why This Architecture Wins

Traditional screenshot services run centralized servers in one or two regions. PxShot's edge-native architecture means:

Want to see it in action? Try PxShot free or read the API docs.

Ready to try PxShot?

Capture any webpage as an image with a single API call. Free tier included.

Get Started Free