Comparison Mar 23, 2026

How to Take Screenshots in Playwright Without Managing a Browser

Take website screenshots with Playwright's reliability without managing a browser in CI or production. One API call. No browser overhead.

You're using Playwright to automate browser tasks. It's fast, reliable, and works everywhere. But when you add screenshots to your workflow, you hit a wall:

Self-hosted Playwright screenshots are expensive.

Browser instances consume memory. CI pipelines timeout. Serverless functions can't spin up Chromium. Every screenshot adds seconds to your automation.

There's a simpler way: use a screenshot API.

One HTTP request. Binary PNG back. No browser management. No memory overhead. No vendor lock-in.

The Problem: Self-Hosted Playwright Screenshots Are Heavy

Playwright is great for automation. But screenshots come with a cost.

// Self-hosted Playwright: screenshot costs resources
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com');

// This keeps the browser running in memory
const screenshot = await page.screenshot({ path: 'screenshot.png' });

await browser.close();

What this costs:

If you're taking 100 screenshots, you're spinning up 100 browser instances. Each one eats memory and time.

The Solution: Screenshot API

One API call. No browser.

// Hosted API: screenshot in one request
const response = await fetch('https://pagebolt.dev/api/v1/screenshot', {
  method: 'POST',
  headers: {
    'x-api-key': `${process.env.PAGEBOLT_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    url: 'https://example.com',
    format: 'png',
    width: 1280,
    height: 720
  })
});

const buffer = await response.arrayBuffer();
const screenshot = Buffer.from(buffer);
// Done. No browser. No memory overhead.

What you get:

Playwright + API: Best of Both Worlds

Use Playwright for automation. Use API for screenshots.

// Playwright for automation, API for proof
import { chromium } from 'playwright';

const browser = await chromium.launch();
const page = await browser.newPage();

// Navigate and interact with Playwright
await page.goto('https://example.com/checkout');
await page.fill('input[name="email"]', 'user@example.com');
await page.click('button:has-text("Checkout")');
await page.waitForNavigation();

// Take screenshot via API (no extra browser needed)
const finalUrl = page.url();
const screenshotResponse = await fetch('https://pagebolt.dev/api/v1/screenshot', {
  method: 'POST',
  headers: {
    'x-api-key': `${process.env.PAGEBOLT_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    url: finalUrl,
    format: 'png'
  })
});

const buffer = await screenshotResponse.arrayBuffer();
// Use screenshot for verification, logging, etc.

await browser.close();

You still use Playwright. But you don't spin up a separate browser just for screenshots.

Comparison: Self-Hosted vs. API

Factor Self-Hosted Playwright Screenshot API
Setup 10 min (install deps) 2 min (API key)
Code complexity 20+ lines per screenshot 5–8 lines
Memory per screenshot 150–300MB 0MB
Time per screenshot 2–5 sec 0.1–0.3 sec
CI/serverless support Difficult (timeout, resource limits) Works everywhere
Scaling to 1,000 screenshots Requires parallelization, resource limits Unlimited concurrency
Error handling Custom retry logic needed Built-in retries
Cost Free (hardware) $0–199/month

Real Use Case: E-Commerce Order Confirmation

Playwright handles the business logic. API handles the screenshot.

import { chromium } from 'playwright';

async function captureOrderConfirmation(orderId) {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  // Playwright: Navigate to order confirmation
  await page.goto(`https://example.com/orders/${orderId}`);

  // Wait for confirmation to render
  await page.waitForSelector('.order-confirmation');

  // API: Take screenshot of final state
  const screenshotResponse = await fetch('https://pagebolt.dev/api/v1/screenshot', {
    method: 'POST',
    headers: {
      'x-api-key': `${process.env.PAGEBOLT_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      url: page.url(),
      format: 'png',
      fullPage: true,
      blockBanners: true // Hide cookie popups
    })
  });

  const buffer = await screenshotResponse.arrayBuffer();

  // Store for audit trail
  const filename = `order-${orderId}-confirmation.png`;
  fs.writeFileSync(filename, Buffer.from(buffer));

  await browser.close();
  return filename;
}

What this gives you:

Real Use Case: Headless Test Reports

Testing framework + API = visual test reports.

import { test, expect } from '@playwright/test';

test('checkout flow creates confirmation', async ({ page }) => {
  // Test with Playwright
  await page.goto('https://example.com');
  await page.fill('input[name="email"]', 'test@example.com');
  await page.click('button:has-text("Next")');

  // Assert behavior
  await expect(page).toHaveURL(/confirmation/);

  // Capture proof screenshot
  const screenshotResponse = await fetch('https://pagebolt.dev/api/v1/screenshot', {
    method: 'POST',
    headers: {
      'x-api-key': `${process.env.PAGEBOLT_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      url: page.url(),
      format: 'png'
    })
  });

  const buffer = await screenshotResponse.arrayBuffer();
  await fs.promises.writeFile(`test-${Date.now()}-proof.png`, Buffer.from(buffer));
});

Tests pass/fail as usual. But you have visual proof of what the page looked like when the test ran.

API Parameters (Playwright Common Use Cases)

Parameter Example Use Case
url page.url() Screenshot current page
format png PNG format (supports pdf, webp)
fullPage true Capture entire scrollable page
width 1280 Viewport width (desktop)
height 720 Viewport height
blockBanners true Hide cookie consent popups
blockAds true Remove ads for clean screenshots

Error Handling

async function safeScreenshot(url) {
  try {
    const response = await fetch('https://pagebolt.dev/api/v1/screenshot', {
      method: 'POST',
      headers: {
        'x-api-key': `${process.env.PAGEBOLT_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ url, format: 'png' })
    });

    if (!response.ok) {
      throw new Error(`Screenshot failed: ${response.status}`);
    }

    const buffer = await response.arrayBuffer();
    return Buffer.from(buffer);
  } catch (error) {
    console.error('Screenshot error:', error.message);
    return null;
  }
}

Migration: From Self-Hosted to API

Before:

const screenshot = await page.screenshot({ path: 'out.png' });

After:

const response = await fetch('https://pagebolt.dev/api/v1/screenshot', {
  method: 'POST',
  headers: {
    'x-api-key': `${process.env.PAGEBOLT_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ url: page.url(), format: 'png' })
});
const screenshot = Buffer.from(await response.arrayBuffer());

Benefits of switching:

Pricing

Plan Requests/Month Cost Best For
Free 100 $0 Development & testing
Starter 5,000 $29 Small projects, side gigs
Growth 25,000 $79 Production apps, frequent screenshots
Scale 100,000 $199 High-volume automation

Summary

Playwright is excellent for automation. But screenshots don't have to be expensive.

Drop the browser overhead — free

100 requests/month, no credit card. One API call replaces the browser instance in your Playwright workflow.

Get API key free →