How to Create a Puppeteer CDP Session
Create a page-attached Chrome DevTools Protocol session with Puppeteer, send commands, listen for events, and handle cleanup and compatibility issues.
Use await page.createCDPSession() to create a Chrome DevTools Protocol (CDP) session attached to a Puppeteer page. Call cdp.send(method, params) to send protocol commands, subscribe to events with cdp.on(event, listener), and call cdp.detach() when you are done.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com');
const cdp = await page.createCDPSession();
try {
await cdp.send('Animation.enable');
cdp.on('Animation.animationCreated', event => {
console.log('Animation created:', event);
});
const { playbackRate } = await cdp.send('Animation.getPlaybackRate');
console.log('Playback rate:', playbackRate);
await cdp.send('Animation.setPlaybackRate', { playbackRate });
} finally {
await cdp.detach();
}
} finally {
await browser.close();
}
This example uses Puppeteer’s page-level API and the Animation-domain command pattern documented by Puppeteer. Consult the Page.createCDPSession reference and CDPSession reference for the API details. Protocol commands and events depend on the browser’s supported protocol version.
1. Choose where the session attaches
Pick the attachment point that matches the target you need to control:
| API | Use it when | Notes |
|---|---|---|
page.createCDPSession() |
You need a session for a Puppeteer page. | Direct, current page-level API. It returns a session attached to that page. |
target.createCDPSession() |
You already have a specific debuggable target, such as a frame, page, or worker. | Use the target whose scope fits the protocol work. |
For a page session, do not route through page.target().createCDPSession(). Puppeteer marks Page.target() obsolete and directs users to Page.createCDPSession().
2. Install Puppeteer and run a complete example
In a new project, install Puppeteer and save the following as cdp-session.js:
npm install puppeteer
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
let cdp;
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
cdp = await page.createCDPSession();
// Enable a protocol domain before expecting its events.
await cdp.send('Animation.enable');
const onAnimationCreated = event => console.log(event);
cdp.on('Animation.animationCreated', onAnimationCreated);
// Send a command and use its returned result.
const result = await cdp.send('Animation.getPlaybackRate');
console.log(result.playbackRate);
await cdp.send('Animation.setPlaybackRate', {
playbackRate: result.playbackRate,
});
// Remove a listener if the session will continue to be used.
cdp.off('Animation.animationCreated', onAnimationCreated);
} finally {
if (cdp && !cdp.detached) {
await cdp.detach();
}
await browser.close();
}
Run it with a Node.js version that supports ES modules:
node cdp-session.js
If your project uses CommonJS, use a dynamic import or configure the project for ES modules. Do not create a CDPSession directly; Puppeteer documents its constructor as internal.
3. Send commands and listen for events
A CDPSession is Puppeteer’s interface for raw Chrome DevTools Protocol communication:
send(method, params)sends a protocol method and resolves with that method’s result.on(event, listener)registers a listener for a protocol event.off(event, listener)removes a listener when it is no longer needed.detachedreports whether the session has been detached.detach()detaches the session from its target.
Many protocol domains require an enable command before their events are delivered. For example, enable the Animation domain with Animation.enable before subscribing to animation events. Check the protocol documentation for the particular command’s required parameters and result shape.
4. Check browser and protocol compatibility
CDP is not available in every Puppeteer browser configuration. Puppeteer’s current ConnectOptions documentation describes runtime protocol selection: launching Chrome selects CDP, launching Firefox selects WebDriver BiDi, and connecting to a browser selects CDP. These defaults can change as Puppeteer evolves, so confirm the configuration for your installed version.
Command support also depends on the browser’s protocol version. Before relying on a command, check the relevant protocol definition and the Chrome or Chromium version used by your application. A session can be created successfully even if a particular command is unsupported by that browser.
5. Handle session lifetime and failures
- Create the session after you have the page or target you intend to control.
- Register event handlers and enable the protocol domains they use.
- Send commands while the target and session remain connected.
- Remove listeners you no longer need, especially in long-lived processes.
- Detach when the session’s work is complete. A detached session no longer emits events and cannot send messages.
Use try/finally so cleanup runs if navigation or a protocol command fails. If the browser or target closes first, treat the session as unusable and create a new session from a live target rather than trying to reuse a detached one.
6. Troubleshoot common problems
| Symptom | Likely cause | What to do |
|---|---|---|
page.createCDPSession is not a function |
The object is not a Puppeteer Page, or the installed Puppeteer version/API differs from the expected one. |
Confirm the object came from Puppeteer’s page API and check the installed version’s reference. Upgrade or adjust the code to that version’s documented API. |
Protocol error: ... Method not found |
The command is unavailable in that browser or protocol version, or the method name is incorrect. | Verify spelling and check the protocol definition for the exact browser build. Use a compatible Chrome/Chromium version or another supported method. |
| An event never arrives | The protocol domain may not be enabled, the event may not occur, or the listener may have been added after it occurred. | Enable the domain first, register the listener before triggering the behavior, and verify that the event belongs to the attached target. |
| Sending fails after cleanup | The session was detached, or its page, target, or browser was closed. | Check cdp.detached, keep the target alive during protocol work, and create a fresh session for a live target. |
| CDP methods are unavailable with a browser setup | The selected browser or protocol is not CDP-compatible; Puppeteer can select WebDriver BiDi for Firefox. | Use a CDP-compatible Chrome connection or launch configuration, and confirm the protocol choice for your Puppeteer version. |
| Commands work locally but fail in deployment | The deployed browser version may expose a different protocol surface. | Pin or record the browser version and validate each required command against that version’s protocol definition. |
7. Performance, reliability, and cost considerations
Creating a CDP session does not itself perform a screenshot or navigation; the work and overhead depend on the commands you send and the events you subscribe to. Keep listeners narrow, remove them when finished, and avoid unnecessary polling when an event or a direct command result can serve the use case.
For reliable automation, handle rejected command promises, keep the target open for the session lifetime, and detach during cleanup. CDP commands are browser-specific protocol operations, so pinning the browser version and checking command availability reduces surprises across environments.
There is no separate CDP-session charge in the Puppeteer API. The browser and infrastructure you run have their own costs; the research sources provide no benchmark or fixed cost estimate for a session.
Or skip the browser setup
If your goal is to capture a page rather than control Chrome directly, ScreenshotNeo offers a website screenshot API and MCP server. Its one-call API returns an image or PDF, and its documentation covers the available parameters.
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)
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 Bun.write('shot.webp', res);
ScreenshotNeo accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, 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. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000 screenshots.
Sign up for ScreenshotNeo’s free plan to get 1,000 screenshots a month with no card.
FAQ
Can one page have more than one CDP session?
A session is created by calling the page API; whether multiple sessions suit your workflow depends on how you divide protocol work. Keep session ownership and cleanup explicit.
Does detaching close the page?
No. Detaching ends that CDP session’s connection to its target. The page lifecycle is managed separately by Puppeteer.
Can I use this API with Firefox?
The session API here is for CDP. Puppeteer’s documented runtime protocol selection uses WebDriver BiDi when launching Firefox, so CDP-specific calls may not be available in that configuration.


