Connect with Puppeteer
Drive a session's Chrome with Puppeteer, or with any other client that speaks the Chrome DevTools Protocol.
Puppeteer connects to the session’s connectUrl with puppeteer.connect. Use puppeteer-core: it does not download a browser, and you do not need one. The session needs a browser, which is the default: one created with browser: false has no connectUrl (see Three kinds of session).
Connect
puppeteer.connect({ browserWSEndpoint: session.connectUrl, defaultViewport: null }); defaultViewport: null keeps the session’s own viewport. This is the tested example from the platform’s examples/integrations/puppeteer: it clicks into a category of books.toscrape.com (a demo shop), reads its books and prices, and saves a screenshot.
/** * Puppeteer with Boxline: connect Puppeteer to a Boxline session's browser with its CDP address and use it as usual. * puppeteer-core is enough: nothing is downloaded or launched on this computer. No AI, no model cost. * * npm install && npx tsx index.ts (BOXLINE_API_KEY in the environment) * * Writes output/result.json and output/poetry.png. */import { mkdirSync, writeFileSync } from "node:fs";import { join } from "node:path";import { Boxline } from "@boxline/sdk";import puppeteer from "puppeteer-core";const out = process.env.OUTPUT_DIR ?? "output";const bx = new Boxline();const session = await bx.sessions.create({ timeout: 300, userMetadata: { example: "integrations/puppeteer" } });console.log(`Session: ${session.id}`);// connectUrl is a signed WebSocket address: treat it like a password. defaultViewport null keeps the session's own.const browser = await puppeteer.connect({ browserWSEndpoint: session.connectUrl!, defaultViewport: null });try { const [page] = await browser.pages(); await page!.goto("https://books.toscrape.com/", { waitUntil: "domcontentloaded" }); await Promise.all([page!.waitForNavigation({ waitUntil: "domcontentloaded" }), page!.click('a[href*="category/books/poetry_23"]')]); const heading = await page!.$eval(".page-header h1", (h) => h.textContent!.trim()); const books = await page!.$$eval("article.product_pod", (items) => items.map((item) => ({ title: item.querySelector("h3 a")!.getAttribute("title")!, price: item.querySelector(".price_color")!.textContent!.trim() })), ); mkdirSync(out, { recursive: true }); const png = await page!.screenshot({ path: join(out, "poetry.png") as `${string}.png` }); console.log(`${heading}: ${books.length} books, e.g. ${books[0]?.title} ${books[0]?.price}`); writeFileSync(join(out, "result.json"), JSON.stringify({ sessionId: session.id, url: page!.url(), heading, books, screenshotBytes: png.length }, null, 2));} finally { await session.stop(); // saves the session and ends its billing; the connection closes with it await browser.disconnect().catch(() => undefined);}Release the session, then disconnect. Everything about lifetime, downloads and the live view is the same as with Playwright: see Connect with Playwright.
Any other CDP client
You can also speak the protocol directly over a WebSocket. This is useful for tools that do not use Playwright or Puppeteer:
import WebSocket from "ws";const ws = new WebSocket(session.connectUrl!); // a signed URL: treat it like a passwordws.on("open", () => { ws.send(JSON.stringify({ id: 1, method: "Browser.getVersion" }));});ws.on("message", (data) => console.log(JSON.parse(data.toString())));Disconnecting matters for browser-only sessions: without keepAlive, they stop about 5 seconds after the last CDP client leaves. See Lifecycle.
The connectUrl carries a signed token and works without your API key. Treat it like a password. If it leaks, rotate the session’s URLs with rotateUrls: true.
Install and run
The packages are pinned at the versions the example’s check ran with. It writes to output/ and stops its session when it finishes.
# Node 22 or newerexport BOXLINE_API_KEY=… # your Boxline API keynpm install @boxline/sdk puppeteer-core@25.12.0 tsx@4.23.15npx tsx index.tsWhat its check proves
It checks that Puppeteer clicked into the Poetry category, read all its books with prices (the same as the site's own page), and saved its screenshot. To run it from a clone of the examples (git clone https://github.com/Boxline-dev/examples.git), go to examples/integrations/puppeteer, run the commands above, then check the result from the same folder: npx tsx check.ts output.
Source: github.com/Boxline-dev/examples/integrations/puppeteer on GitHub. See all integrations.
These docs describe a beta API. Endpoints, fields and SDK methods may change before general availability; breaking changes will be listed in the changelog.