How to Stop a Puppeteer Performance Trace
Stop a Puppeteer trace with `await page.tracing.stop()`. Save it to disk with a start-time path, or use the returned trace buffer in memory.
Call await page.tracing.stop() on the page after the actions you want to record. To save the trace to disk, pass a path when you start tracing. Without a path, use the value returned by stop() as the in-memory trace data.
const trace = await page.tracing.stop();
The stop method returns a promise that resolves to a Uint8Array containing trace data, or undefined. Await it before reading the output, closing the browser, or starting another trace. See the Puppeteer Tracing.stop() API reference.
Complete example: save a trace to a file
Set the output path in page.tracing.start(), perform the page actions to profile, then await stop(). Puppeteer writes the trace to the specified path.
const puppeteer = require('puppeteer');
async function main() {
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.tracing.start({ path: 'trace.json' });
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.tracing.stop();
console.log('Trace saved to trace.json');
} finally {
await browser.close();
}
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Use a path that the process can write to. The path is a start option; stop() does not take output options. The documented trace can be opened in Chrome DevTools or a timeline viewer. Refer to the Puppeteer Tracing API for the version used by your project.
Get trace data in memory
Omit path when starting, then capture the result of stop(). This is useful when a later step uploads or processes the bytes instead of reading a local file.
const puppeteer = require('puppeteer');
const fs = require('node:fs/promises');
async function main() {
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.tracing.start();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
const trace = await page.tracing.stop();
if (trace) {
await fs.writeFile('trace.json', trace);
console.log(`Wrote ${trace.byteLength} trace bytes`);
} else {
console.log('Puppeteer returned no trace buffer');
}
} finally {
await browser.close();
}
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
The return type is Uint8Array | undefined. Check for a missing buffer before using it. If you supplied a path, the file output is the intended persistence route; the return value is not needed to locate that file.
Configure the trace when starting it
Choose the capture options in start(). The documented options include:
| Option | What it controls | Practical note |
|---|---|---|
path |
File path for trace output | Omit it when you want to use the buffer returned by stop(). |
categories |
Trace categories to include or exclude | Prefix a category with - to exclude it. Choose only the categories useful for the investigation. |
screenshots |
Whether screenshots are captured in the trace | Defaults to false. |
bufferSize |
Trace buffer capacity | If omitted or set to zero, the documented Chromium default is 200 MB (200,000 KB). |
For example, supply a path and explicit categories at the start. Replace category names with those relevant to your profiling question:
await page.tracing.start({
path: 'trace.json',
categories: ['devtools.timeline', 'v8.execute'],
screenshots: true,
bufferSize: 50_000
});
// Perform the actions to record.
await page.goto('https://example.com');
await page.tracing.stop();
Check the TracingOptions reference for the exact option types and defaults in your installed Puppeteer version. The documentation pages may describe different releases, so align the reference with your dependency.
Stop multiple traces safely
Only one trace can be active at a time per browser. For multiple captures, stop and await each trace before starting the next one. Separate pages do not remove this browser-level constraint.
for (const [index, url] of urls.entries()) {
const page = await browser.newPage();
try {
await page.tracing.start({ path: `trace-${index}.json` });
await page.goto(url, { waitUntil: 'networkidle2' });
await page.tracing.stop();
} finally {
await page.close();
}
}
If an action fails after tracing starts, make sure your cleanup path still stops the trace before another capture begins. A simple pattern is to keep the trace lifecycle in a try/finally block:
await page.tracing.start({ path: 'trace.json' });
try {
await page.goto('https://example.com');
// Run the interactions you want to profile.
} finally {
await page.tracing.stop();
}
cURL, Python, and Node.js alternatives
Puppeteer’s tracing control is a JavaScript browser API; cURL and Python do not call page.tracing.stop() directly. They can, however, call a separate screenshot API when the task is to capture a page image rather than collect a Chrome performance trace. The following examples use ScreenshotNeo, which returns screenshots or PDFs and does not produce a Puppeteer performance trace. See the ScreenshotNeo API documentation for request options.
cURL screenshot
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com \
-o shot.webp
Python screenshot
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
timeout=90,
)
r.raise_for_status()
with open("shot.webp", "wb") as image_file:
image_file.write(r.content)
Node.js screenshot
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://example.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await import('node:fs/promises').then(({ writeFile }) => writeFile('shot.webp', bytes));
Or skip the browser setup
If you need a page screenshot rather than a performance trace, ScreenshotNeo captures a URL with one request. Cookie banners, newsletter popups, and chat widgets are removed before the shot; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies page verdict and billing status. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com \
-o shot.webp
There are 1,000 screenshots a month free with no card; paid plans start at $5 for 3,000. Every feature is on every plan. Read the API documentation and sign up free for 1,000 screenshots a month.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| No trace file appears | No path was supplied to start(), or the process cannot write to that location. |
Set a writable path when starting tracing, or omit it and save the returned buffer yourself. |
| The in-memory trace is missing | The stop result was ignored, or it resolved to undefined. |
Assign and await the result of stop(), then check it before writing or uploading. |
| The next capture cannot start | A previous trace is still active; only one can run per browser. | Await page.tracing.stop() for the active trace before starting another. |
| The trace does not contain the expected period | Tracing began after the relevant page action or stopped before it. | Start tracing before the action and stop only after the action completes. |
| The trace is unexpectedly large | The recording may include more categories, screenshots, or time than needed. | Shorten the capture and tune categories or screenshot capture at start(); buffer size is also configurable. |
Performance, reliability, and storage
- Keep captures focused. Record the interaction or navigation you need to diagnose, then stop promptly. This limits unnecessary trace data.
- Await the stop operation. It is asynchronous; waiting ensures the stop call and its returned data have settled before you consume the output or close the browser.
- Plan for buffer size. The documented default is 200 MB when
bufferSizeis omitted or zero. Select a suitable value for the capture and available memory, and avoid assuming a trace will be small. - Choose file or memory deliberately. A path is convenient for a persistent local artifact. The returned byte buffer supports in-process handling but occupies memory until released.
- Keep the browser lifecycle predictable. Stop the trace before closing the browser and before a new trace starts. Use cleanup logic so errors during navigation do not leave the trace active.
FAQ
Does stop() return a file path?
No. It resolves to a trace-data buffer or undefined. The path is configured when tracing starts.
Can a single browser record traces on two pages simultaneously?
No. Puppeteer documents one active trace per browser.
Can I open the trace outside Chrome DevTools?
Puppeteer also points to timeline viewers. The trace is a data artifact; use a viewer that supports its format.
Does ScreenshotNeo replace a performance trace?
No. ScreenshotNeo captures page images or PDFs. Use Puppeteer tracing when you need performance trace data.


