How to Stop and Save a Puppeteer Trace
Start tracing before the browser work you want to inspect, then await page.tracing.stop(). Set path to save the trace directly to disk.
Use Puppeteer’s page.tracing API. Start tracing with a path, perform the browser work you want to inspect, and await page.tracing.stop(). The trace covers activity between those calls.
import puppeteer from 'puppeteer';
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: 'networkidle0' });
// Perform the interactions you want to include in the trace.
await page.tracing.stop();
} finally {
await browser.close();
}
With path, Puppeteer writes the trace file. Without it, Puppeteer does not write a file automatically; the stop call can return trace bytes for your application to save. See the official Tracing API, TracingOptions, start() and stop() references. API behavior and options can vary by installed Puppeteer version, so consult the docs matching your dependency.
Save directly to a file
- Install Puppeteer in a Node.js project:
npm install puppeteer. - Create a script such as
trace.mjswith the example above. - Run it with
node trace.mjs. The path is relative to the process’s working directory unless you provide an absolute path. - Open
trace.jsonin Chrome DevTools or a timeline viewer.
Use a unique output path for each run if you need to retain earlier traces. The browser must be able to write to the destination directory.
Save returned trace bytes yourself
When you want to inspect, upload, or otherwise process trace data in your own code, omit path and handle the value returned by stop(). Its documented type is Promise<Uint8Array | undefined>, so check that data exists before writing it.
import puppeteer from 'puppeteer';
import { writeFile } from 'node:fs/promises';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.tracing.start();
await page.goto('https://example.com');
const traceData = await page.tracing.stop();
if (!traceData) {
throw new Error('Puppeteer did not return trace data');
}
await writeFile('trace.json', traceData);
} finally {
await browser.close();
}
This approach makes your application responsible for persistence: choosing the filename, ensuring the directory exists, handling write errors, and deciding whether to retain or upload the result.
Make sure stop runs when work fails
If navigation or an interaction throws, normal sequential code may skip stop(). Put trace shutdown in a finally block when later cleanup matters. Preserve the original error if tracing shutdown also fails.
await page.tracing.start({ path: 'trace.json' });
try {
await page.goto('https://example.com');
await page.click('button');
} finally {
await page.tracing.stop();
}
If you need to preserve both a task failure and a stop failure, capture each error separately and report both in your application’s error handling. Avoid starting a second trace on the same browser as a recovery strategy: Puppeteer documents that only one trace can be active at a time per browser.
Tracing options
| Option | What it does | Practical note |
|---|---|---|
path |
File path for direct trace output. | Omit it only when your code will handle returned trace bytes. |
categories |
Tracing categories to include or exclude. | Prefix a category with - to exclude it. The defaults are implementation-dependent; check the docs for your version. |
screenshots |
Whether to include screenshots in the trace. | Defaults to false. Enable when visual frames are useful, and account for larger trace data. |
bufferSize |
Trace buffer size in kilobytes. | The docs state that omitted or zero uses Chromium’s default of 200 MB (200,000 KB). Treat this as version-sensitive behavior. |
await page.tracing.start({
path: 'trace.json',
screenshots: true,
categories: ['devtools.timeline', 'v8.execute', '-toplevel'],
bufferSize: 200000,
});
Choose categories for the investigation rather than copying an arbitrary list. A smaller trace is easier to handle, while excluding a category can omit events relevant to a question. Screenshot frames can help correlate visual changes with timeline events, but add data to the trace.
cURL, Python, and Node.js: what applies
Puppeteer tracing is a browser-control API for Node.js, so cURL and Python do not directly start or stop a Puppeteer trace. Use the Node.js examples above to create the trace. If a Python program needs the result, have the Node.js process save the file and then let Python consume that file.
# cURL cannot operate Puppeteer's in-process tracing API.
# Run the Node.js script that calls page.tracing.start() and stop() instead.
node trace.mjs
# Python can read a trace file after the Node.js script creates it.
from pathlib import Path
trace = Path("trace.json").read_bytes()
print(f"Loaded {len(trace)} bytes")
For a remote capture service that returns a webpage screenshot, cURL and Python are suitable, but that output is an image or PDF rather than a Puppeteer performance trace.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server for developers. It produces screenshots or PDFs, not Puppeteer trace files. Use it when the task is to capture a page’s appearance without managing a browser process. See ScreenshotNeo and the API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(({ writeFile }) => writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));
Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, with response headers identifying the page verdict and billing status. Its MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up free for 1,000 screenshots a month, no card required.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| No trace file appears. | path was omitted, or the path points somewhere unexpected. |
Set path explicitly, check the process working directory and confirm the destination is writable. Without a path, save the returned bytes yourself. |
| The trace is empty or misses the action. | The action happened before start() or after stop(), or the action did not complete. |
Start immediately before the work to inspect, await each browser action, then stop afterward. |
| Starting a trace fails while another capture is active. | Only one trace can be active per browser. | Stop the previous trace before starting another, or sequence capture sessions. |
| The saved bytes are missing. | stop() returned undefined, or the caller did not await it. |
Await stop, check its result before writing, and handle the missing-data case. |
| File writing fails. | The directory does not exist, permissions deny access, or the disk is full. | Use an accessible directory, create it before capture if needed, and handle filesystem errors. |
| Trace file is too large or hard to inspect. | Too many events or screenshot frames were collected. | Shorten the capture window, review categories, and disable screenshots unless they are needed. |
| An option is rejected or appears ignored. | The installed Puppeteer version may not support that option or may differ from current docs. | Check the API reference for the installed version and upgrade deliberately if the feature is required. |
Performance, reliability, and cost
Tracing adds collection and file or buffer handling to a browser session. Keep captures focused on the operation under investigation. Screenshot frames and broad category selection can increase the amount of data. The documented buffer default is Chromium-dependent and version-sensitive; do not assume a particular trace size or duration will fit every workload.
For reliable cleanup, await both start and stop, use try/finally, and write to a path your process can access. If traces are retained or uploaded, apply your own retention and access controls: traces can contain page activity and should be handled as diagnostic artifacts. Puppeteer’s documented workflow does not introduce a per-trace service fee; operational costs depend on the browser infrastructure, storage, and processing your application uses.
FAQ
Can I stop tracing from a different page?
Tracing is exposed on a page, and Puppeteer permits only one active trace per browser. Keep a reference to the page whose trace you started and stop that capture before beginning another in the same browser.
Does stop() always save the file?
No. Direct file output is controlled by path in the start options. Without it, stop may return a Uint8Array for your code to persist.
Can I view a trace in Chrome DevTools?
Yes. Puppeteer documents trace output as openable in Chrome DevTools or a timeline viewer.
Do I need screenshot capture enabled?
No. It defaults to off. Enable it when visual frames help explain the recorded timeline.


