How to Record Videos with Puppeteer
Record a Puppeteer-controlled page to MP4 with the experimental Page.record() API, then save or stream the recording and troubleshoot common issues.
Use Puppeteer’s experimental Page.record() API to record a browser page to an MP4 stream. Navigate first, start the recorder, perform the actions you want to capture, then call stop() before closing the browser. Because this API is experimental, check the reference for your pinned Puppeteer version before relying on it in a production workflow.
Record a page to MP4
Install Puppeteer in a Node.js project, then save this as record-page.mjs. Puppeteer’s documented flow launches a browser, creates a page, navigates, records, stops the recording, and closes the browser. [Puppeteer getting started] [Page.record() reference]
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
const recorder = await page.record({ path: 'recording.mp4' });
try {
// Replace these actions with the interaction you want to record.
await page.waitForSelector('body');
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
await new Promise(resolve => setTimeout(resolve, 1000));
} finally {
await recorder.stop();
}
} finally {
await browser.close();
}
Run it with node record-page.mjs. The path option writes the recording to that file. Start recording after navigation if you want the video to show the page after it loads; start before navigation only if the load itself is part of the sequence you intend to capture. The API returns a Promise<ScreenRecording>; stop the returned recording explicitly when the desired sequence ends. [Page.record() reference]
Save the recording as a stream
The recording object is stream-oriented: the API type extends ReadableStream<Uint8Array> and documents pipe() and stop(). The path example is convenient for a local file. For other output destinations, use the stream methods supported by the exact Puppeteer version you have installed; the type reference documents the interface, but your destination handling is application-specific. [ScreenRecording reference]
For a Node.js writable stream such as a file stream, use the recorder’s documented pipe() method and still stop the recorder explicitly. Verify the precise stream contract against your installed version before depending on a particular writable-stream integration.
import fs from 'node:fs';
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
const recorder = await page.record();
const output = fs.createWriteStream('recording.mp4');
recorder.pipe(output);
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
await new Promise(resolve => setTimeout(resolve, 1000));
await recorder.stop();
await new Promise((resolve, reject) => {
output.once('finish', resolve);
output.once('error', reject);
});
} finally {
await browser.close();
}
If stream piping fails with your installed release, use the documented { path: 'recording.mp4' } form or consult that release’s reference. Avoid assuming that a browser protocol stream can be consumed as a Node.js stream without checking the supported bridge.
Choose what the video captures
- Navigate to the starting state. Wait for the content you need, using a navigation condition or a specific selector. Sites with ongoing network activity may never become network-idle, so a selector or a deliberate delay can be a better readiness condition.
- Start the recorder. Call
await page.record({ path: 'recording.mp4' })after the initial page setup if the video should begin with the loaded page. - Perform deterministic actions. Use Puppeteer interactions such as clicks, typing, and scrolling. Wait for resulting content before moving to the next action.
- Stop recording. Call
await recorder.stop()after the final action. Usetry/finallyso an exception in the interaction sequence does not skip cleanup. - Close the browser. Close it after stopping the recorder. Puppeteer’s documented recording example follows this order. [Page.record() reference]
To capture a user journey, make the page state reproducible: use stable test data, wait for the relevant UI state, and avoid timing-only assumptions where a selector or application signal is available. Recording begins when you call page.record(); actions before that call are not part of the recording.
Options, versions, and the older screencast API
Page.record(options?) is the current documented recording method in the supplied Puppeteer reference. It is experimental, uses Chrome DevTools Protocol screen recording, and documents MP4 output. Its surfaced reference does not establish a compatibility minimum, frame-rate option, audio behavior, or detailed quality controls. Check the API reference for the exact Puppeteer version in your lockfile rather than inferring those details. [Page.record() reference]
| Method | Status | Documented output and caveats |
|---|---|---|
Page.record() |
Experimental | MP4; use the version-matched reference for supported options. |
Page.screencast() |
Deprecated/obsolete | Its dedicated page documents WebM, VP9, 30 FPS, Chrome 153+, and an FFmpeg system dependency. These are details of the old API, not Page.record(). |
Puppeteer labels screencast() deprecated and recommends record(). The older API’s page calls it obsolete. Do not transfer its WebM, VP9, frame-rate, Chrome-version, or FFmpeg notes to Page.record(). If maintaining older code, first check whether the installed Puppeteer release exposes Page.record(); the available references do not establish a complete compatibility matrix. [Page API reference] [Page.screencast() reference]
Puppeteer’s launch reference says headless: true is the default and guarantees operation only with its bundled browser; a custom executablePath is at the user’s own risk. The recording example does not specify a special hardware requirement. [LaunchOptions reference]
Or skip the browser setup
If your goal is a still image of a webpage rather than a video of browser activity, ScreenshotNeo captures a URL with one GET request. It does not record video; it returns a PNG, JPEG, WebP, or PDF. Its cookie-consent handling accepts banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture. Each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
Also available: [Python example](https://screenshotneo.com/docs/): import requests; r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com"}, timeout=90); open("shot.webp", "wb").write(r.content). Node.js: const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' }); const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);. See the ScreenshotNeo API documentation for request options.
Cookie banners, popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are never billed; an MCP server lets AI agents take screenshots; and 1,000 screenshots a month are free with no card, with paid plans starting at $5 for 3,000. Create a free ScreenshotNeo account.
Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
page.record is not a function |
The installed Puppeteer version does not expose the experimental API, or the page is not the Puppeteer page object expected. | Check the version-matched API reference and the package version resolved by your lockfile. Do not assume a minimum version from the forward reference. |
| The MP4 is missing or incomplete | The recorder was not stopped, the browser closed too early, or the action sequence failed before cleanup. | Await recorder.stop() before browser close and put stop/close operations in finally blocks. |
| The video starts too late or omits the load | Recording started after the page had already navigated or after the interaction of interest. | Move page.record() before the actions that must appear. If the initial navigation must be shown, start recording before that navigation. |
| Navigation hangs waiting for network idle | Analytics, streaming, or long polling can keep network connections active. | Wait for a required selector or use a bounded delay instead of relying on a network-idle condition for that site. |
| Recording fails with a custom browser executable | Puppeteer’s guarantee is for its bundled browser, not a custom executable. | Reproduce with the bundled browser first, then consult the installed release’s compatibility information before using a custom path. |
| Legacy screencast reports a Chrome or FFmpeg problem | The deprecated screencast() API documents Chrome 153+ and FFmpeg requirements. |
Prefer Page.record() when available; treat those requirements as specific to the legacy method. |
Performance, reliability, and cost
Recording adds browser work and produces a video stream or file, so keep the capture interval and page activity limited to what the video needs. The supplied Puppeteer references do not provide recording benchmarks, resource estimates, or a pricing model; runtime and storage costs depend on your browser environment and where you retain the output. Avoid setting a fixed frame-rate or quality expectation for Page.record() unless the reference for your installed version documents it.
For repeatable automation, pin Puppeteer, use its bundled browser, wait on page-specific readiness signals, and always stop the recorder before cleanup. Because the API is experimental, re-check its versioned reference when upgrading. A failed or interrupted run should be treated as an incomplete artifact; make the workflow safe to rerun and verify that the output file exists and is usable before publishing it.
FAQ
Does Puppeteer record audio?
The surfaced Page.record() reference does not specify audio behavior. Do not assume audio is present; verify the documentation for your installed version if audio matters.
Can I choose the frame rate or video codec with Page.record()?
The cited current reference does not establish frame-rate or codec options for Page.record(). The 30 FPS and VP9 details belong to the deprecated screencast() API.
Do I need a webcam or capture card?
No such device is part of the documented Puppeteer page-recording workflow. It records a Puppeteer-controlled browser page.
Is Page.record() stable for production?
Puppeteer labels it experimental. Check the reference matching your pinned version and account for API changes when upgrading.


