Scrapeless Cover Image

TL;DR:

  • Use chromium.connectOverCDP() for a Chromium CDP endpoint. A Playwright protocol endpoint belongs to a different connection method.
  • Configure routing where the remote browser starts. A local HTTP client's proxy settings do not automatically configure the remote browser.
  • Validate content after navigation and save artifacts before closing. A successful connection establishes control, not a successful extraction.

Playwright can control a browser that runs on another machine. Your Node.js process sends commands through a control connection; the remote browser loads pages, executes JavaScript, and maintains browser state.

With Scrapeless Scraping Browser, you can use that pattern without operating the browser host yourself. The current product site calls the service Agent Browser. This tutorial uses the official SDK to build its connection URL and Playwright to drive the browser.

Understand Which Endpoint You Have

CDP and Playwright's native protocol require different connection methods.

Use the current Scrapeless Node.js SDK interface when adapting service configuration; older examples can expose different class names or option styles.

Playwright's connectOverCDP() API connects to Chromium through the Chrome DevTools Protocol. Playwright's connect() method expects a Playwright protocol endpoint. Changing the method name does not convert one protocol into the other.

CDP also has lower fidelity than a native Playwright protocol connection. If a workflow depends on advanced context behavior, tracing, downloads, or other provider-sensitive features, test those operations on the intended service. A basic navigation check cannot establish feature parity.

For Scrapeless, the SDK's browser.create() method returns a browserWSEndpoint. In the SDK used here, this method constructs the connection URL locally. Receiving that string does not prove that your API key is accepted or a cloud browser has started. The network connection is the next step.

Prerequisites

You need Node.js, a Scrapeless API key with Browser access, and an approved public target page.

The example defaults to https://example.com and expects its Example Domain heading. When adapting it, change the URL and expected heading together. Choose an expected value that distinguishes the intended content from a generic login or error screen.

Note: The SDK URL construction and browser logic were checked locally. Running this example against Scrapeless requires your own configured API key; the cloud connection and residential egress are not presented here as a completed service test.

Install the packages in a new project. These exact package versions are the reference environment for the example:

npm init -y npm install playwright-core@1.64.1 @scrapeless-ai/sdk@1.12.1 

playwright-core supplies the client library. You do not need to download a local Chromium binary to connect to a managed remote browser.

Connect, Check the Page, and Save Evidence

A useful connection example should validate a page and release the browser even if validation fails.

Save the following as connect-cdp.mjs. Configure SCRAPELESS_API_KEY through your environment or secret manager, then run node connect-cdp.mjs.

Note: Cloud execution requires the Browser prerequisite above. CDP_WS_ENDPOINT is an optional alternative for a CDP browser you control; leave it unset when using the SDK-created Scrapeless connection.

import { chromium } from 'playwright-core'; import { Scrapeless } from '@scrapeless-ai/sdk'; import { mkdir, writeFile } from 'node:fs/promises'; import { createHash } from 'node:crypto'; import path from 'node:path'; const directEndpoint = process.env.CDP_WS_ENDPOINT; const apiKey = process.env.SCRAPELESS_API_KEY; if (!directEndpoint && !apiKey) { throw new Error('Set SCRAPELESS_API_KEY or an authorized CDP_WS_ENDPOINT'); } const target = new URL(process.env.TARGET_URL ?? 'https://example.com'); const expectedHeading = process.env.EXPECTED_HEADING ?? 'Example Domain'; const output = process.env.OUTPUT_DIR ?? './artifacts'; const endpoint = directEndpoint ?? new Scrapeless({ apiKey }).browser.create({ sessionName: 'cdp-public-page', sessionTTL: 180, proxyCountry: 'US', }).browserWSEndpoint; await mkdir(output, { recursive: true }); const browser = await chromium.connectOverCDP(endpoint, { timeout: 30000 }); try { const context = browser.contexts()[0]; if (!context) throw new Error('Provider default context is unavailable'); const page = await context.newPage(); const response = await page.goto(target.href, { waitUntil: 'domcontentloaded', timeout: 30000, }); if (!response || !response.ok()) throw new Error('Document request failed'); if (new URL(page.url()).origin !== target.origin) { throw new Error('Navigation left the approved origin'); } const heading = page.locator('h1').first(); await heading.waitFor({ state: 'visible', timeout: 15000 }); const h1 = (await heading.innerText()).trim(); if (h1 !== expectedHeading) throw new Error('Expected page content is absent'); const capturedAt = new Date().toISOString(); const html = await page.content(); await writeFile(path.join(output, 'page.html'), html); await page.screenshot({ path: path.join(output, 'page.png'), fullPage: true }); const result = { requestedUrl: target.href, finalUrl: page.url(), title: await page.title(), h1, documentStatus: response.status(), capturedAt, htmlSha256: createHash('sha256').update(html).digest('hex'), artifacts: { html: 'page.html', screenshot: 'page.png' }, }; await writeFile(path.join(output, 'result.json'), JSON.stringify(result, null, 2)); console.log('Accepted page capture; artifacts saved'); } finally { await browser.close(); } 

The script selects the provider's existing default context. It deliberately fails when that context is unavailable. Automatically creating a different context could change the state assumptions of a provider-managed browser.

The page check has several independent conditions. The document request must succeed. The final origin must remain the approved origin. The heading must be visible and must contain the expected value. This small example validates one public page; a production extractor needs a domain-specific check for its actual output fields.

An origin check after navigation detects an unexpected redirect, but it is not a network firewall: the browser has already followed that navigation. Enforce a strict outbound allowlist at the browser or network layer when your application requires it.

Read the Artifacts as Evidence of a Capture

The output files describe what the browser captured during this job.

result.json records the requested URL, final URL, document status, heading, observation time, and artifact names. page.html stores serialized DOM content. page.png captures a visual representation of the page.

The HTML hash checks the integrity of the saved HTML bytes. It does not prove who published the page or whether its contents are correct. The HTML and screenshot are captured sequentially, so a page that changes continuously can differ between them.

For an extraction workflow, add the selected values and their source locations to the result record. Keep the raw observation separate from any model-written interpretation. That separation makes a later review possible without rerunning the model.

Configure the Destination Proxy on the Remote Side

The remote browser's routing determines the destination-visible exit path.

The example requests a US browser proxy country through the SDK. This is a configuration request, not a measurement of the actual exit. If geography affects your result, check the browser's observed public IP through an approved diagnostic page and record the relevant location evidence.

The Scrapeless browser proxy controls cover built-in routing and a custom proxy URL. If you already operate a local browser, Scrapeless Proxies provide a separate configuration path for that fleet. These two deployments should be evaluated separately.

Avoid setting a local Playwright launch proxy and assuming it applies to a remote browser that has already been allocated. In this tutorial there is no local launch() call: browser allocation and routing are requested through the remote service.

The distinction follows the general separation between a client connection and proxy routing to the destination. The machine running the browser makes the page requests.

Use the Right Readiness Condition

Page readiness should describe the content you need, rather than an arbitrary delay.

domcontentloaded establishes an initial document milestone. The example then waits for a visible heading. For a product page, the useful condition might be a populated price field. For a research page, it might be the main article region and its expected title.

Playwright actionability checks help interactions wait for usable elements. They do not certify business correctness. A visible “Submit” button can still belong to the wrong account or form.

Avoid treating an empty result as proof that a field does not exist. Record whether the target element was absent, the document was a different page, or the extractor could not establish readiness. Those outcomes need different downstream handling.

Diagnose Failures at the Correct Layer

Connection, navigation, readiness, and validation failures point to different parts of the system.

Connection failure: check whether the endpoint is CDP, the API key is configured, Browser access is available, and the network permits a secure WebSocket connection. Keep token-bearing URLs out of logs.

Navigation failure: inspect the approved target URL, document response, and destination accessibility from the browser's egress.

Readiness failure: inspect the page state and locator. A login screen, consent screen, or changed DOM can explain why the expected content never appeared.

Validation failure: compare the captured state with the task contract. A valid response from the wrong application page should remain an unsuccessful task.

The Playwright proxy integration discussion provides additional context for connecting browser configuration to routing. Use the current connection parameters demonstrated above when adapting older examples.

Close the Browser Within the Task Boundary

Browser cleanup belongs in a finally block so extraction errors still reach the close operation.

Save artifacts before closing. Do not put close calls only after the successful result path. At the application level, also release queue slots and state locks when a worker exits.

The lifetime of the provider session is separate from the Playwright object in your process. Follow the provider's documented close and session-lifetime behavior. For persistent profiles, verify the storage behavior separately rather than assuming that a disconnected client saved all state.

Use Scrapeless pricing to budget for the browser workload and any separately configured Scrapeless proxy product. A functional CDP example does not establish the cost of your page set.

Conclusion

Connecting Playwright over CDP requires the correct protocol endpoint, an explicit browser-context choice, and a content check after navigation. The complete workflow also saves inspectable artifacts and ends its resources predictably.

Start with the bounded example on Scrapeless Browser, then replace its heading assertion with the actual acceptance conditions of your application.

FAQ

Q: Does CDP work with every Playwright browser engine?

Playwright's CDP connection supports Chromium-based browsers. It is not the connection method for Firefox or WebKit.

Q: Does browser.create() prove the cloud session exists?

The SDK method used here constructs the WebSocket URL locally. The subsequent connection is required to contact the browser service.

Q: Can an HTTP 200 response still be the wrong page?

An HTTP 200 response can contain a login page or other unexpected content. Validate the final page state and required values.

Q: Can this script run without an AI model?

This example is deterministic Playwright code and needs no model call. An agent can use the same connection inside a separate, controlled orchestration loop.