The executable and package disagree
Playwright versions are coupled to browser revisions. A runtime layer that silently updates one side can turn a deploy into a launch failure.
The screenshot works locally, then the function cannot find Chromium, exceeds its compressed bundle limit, times out during launch or dies when two browser contexts overlap.
Intermittent 5xx responses around deploys and traffic spikes, with most of the request budget spent before `page.goto()` starts.
Start with one browser per invocation because it is easier to reason about. Reuse introduces lifecycle and isolation decisions that should be measured, not assumed.
import { chromium } from "playwright-core";
export async function POST(request: Request) {
const { url } = await request.json();
const browser = await chromium.launch({ headless: true });
try {
const context = await browser.newContext({ viewport: { width: 1280, height: 720 } });
const page = await context.newPage();
await page.goto(url, { waitUntil: "load", timeout: 30_000 });
const image = await page.screenshot({ type: "png" });
return new Response(image, { headers: { "content-type": "image/png" } });
} finally {
await browser.close();
}
}Playwright versions are coupled to browser revisions. A runtime layer that silently updates one side can turn a deploy into a launch failure.
Downloading, unpacking or launching Chromium happens before useful navigation. Raising the page timeout does not extend the platform request lifetime.
A single successful invocation says little about two overlapping browsers. Platform concurrency and in-process contexts must share a deliberate capacity budget.
Keeping a browser warm helps latency, but contexts, downloads, service workers and credentials need explicit per-job isolation and cleanup.
A PNG response from a pinned Chromium engine. For a caller with a shorter request window, the same request can use async delivery and a signed webhook.
One successful, non-cached capture costs one credit. Failures, including platform failures, cost zero; cache hits cost zero.
The render pool has no safe capacity for a new job; use `Retry-After`.
HTTP 503 · retryable
Navigation or the page readiness rule exceeds its declared budget.
HTTP 500 · retryable
The runner above carries this exact configuration into the playground — no retyping, no starting over.
Written by PageCapture Engineering. Reviewed by PageCapture runtime maintainers. Verified 2026-08-12 by running the published code against a fixture.
The recipe is normalized and all generated language variants are syntax-checked; browser-version and timeout claims come from the installed Playwright contract and PageCapture parameter inventory.
Sources support the browser and API behaviors named above. PageCapture-specific limits and billing are taken from the public contract; external sources are used for the underlying browser behavior.
Documents browser installation and version coupling.
Documents the supported container model and security considerations.
Defines the API timeout and readiness contract.