How to Take Screenshots in Playwright Without Managing a Browser
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.
Here's how to replace self-hosted Playwright screenshots with a hosted alternative.
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:
- Memory: 100–300MB per browser instance
- Time: 2–5 seconds to launch + navigate
- Complexity: Error handling, cleanup, retry logic
- Scalability: CI runners choke on multiple simultaneous screenshots
- Cost: Serverless functions are billed for entire browser lifetime
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://api.pagebolt.dev/v1/screenshot', {
method: 'POST',
headers: {
'Authorization': `Bearer ${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:
- Speed: One HTTP request (50–200ms)
- Simplicity: No browser management
- Scalability: Unlimited concurrent screenshots
- Reliability: Automatic retries, global CDN
- Cost: $0–199/month based on volume
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://api.pagebolt.dev/v1/screenshot', {
method: 'POST',
headers: {
'Authorization': `Bearer ${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://api.pagebolt.dev/v1/screenshot', {
method: 'POST',
headers: {
'Authorization': `Bearer ${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:
- ✅ Playwright's reliability for navigation
- ✅ API's speed for screenshots
- ✅ One browser instance (not two)
- ✅ Fast, lightweight workflow
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://api.pagebolt.dev/v1/screenshot', {
method: 'POST',
headers: {
'Authorization': `Bearer ${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 (lazy content) |
| 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://api.pagebolt.dev/v1/screenshot', {
method: 'POST',
headers: {
'Authorization': `Bearer ${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);
// Fallback: return null or use Playwright screenshot instead
return null;
}
}
Migration: From Self-Hosted to API
Before:
const screenshot = await page.screenshot({ path: 'out.png' });
After:
const response = await fetch('https://api.pagebolt.dev/v1/screenshot', {
method: 'POST',
headers: {
'Authorization': `Bearer ${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:
- ✅ Faster (50ms vs. 2-5 sec)
- ✅ Lighter (0MB vs. 150-300MB per screenshot)
- ✅ Scales to unlimited concurrent requests
- ✅ Works in serverless, CI, containers without browser installation
- ✅ No complex retry logic needed
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.
- ✅ Use Playwright for navigation, interaction, testing
- ✅ Use Screenshot API for visual proof (no browser)
- ✅ One API key, one HTTP request per screenshot
- ✅ No memory overhead, no CI timeout issues
- ✅ Scales from 10 screenshots to 10,000
Get started free: pagebolt.dev — 100 requests/month, no credit card required.
Top comments (0)