Sessions
A real Chrome with a window of its own, held for as long as you asked for, driven by your own Playwright or Puppeteer over a CDP socket. We hand back a URL, not another client to learn.
Start one
curl -X POST https://api.scrapa.app/v1/sessions \
-H "authorization: Bearer sc_live_your_key" \
-H "content-type: application/json" \
-d '{"timeoutMin": 20}'
{"data":{"id":"c0a8f2e1-5b74-4a3d-9e21-7d6f0b3c9a44","status":"live",
"connectUrl":"wss://api.scrapa.app/v1/sessions/c0a8f2e1-…/cdp?token=…",
"pool":"residential","timeoutMin":20,"expiresAt":"2026-01-01T12:20:00Z",
"creditsReserved":40,"bytes":0},
"meta":{"cost":40,"creditsRemaining":960,"requestId":"0f3c…"}}The answer carries connectUrl, the socket your CDP client dials, with the session's token already in it. It is shown once and cannot be rebuilt from the id, so keep it for as long as you need the browser.
Playwright
import { Scrapa } from "@scrapa/sdk";
import { chromium } from "playwright";
const scrapa = new Scrapa(process.env.SCRAPA_API_KEY!);
const session = await scrapa.sessions.create({ timeoutMin: 20 });
const browser = await chromium.connectOverCDP(session.connectUrl);
try {
const page = await browser.newPage();
await page.goto("https://example.com");
console.log(await page.title());
} finally {
await browser.close(); // disconnects you
await scrapa.sessions.close({ id: session.id }); // ends it, hands the rest back
}Everything you already do with a local browser still works: contexts, pages, cookies, your own request routing. The one command we rewrite is the one that would ask for a different exit. A context you create keeps the session's proxy, whatever it asks for.
Puppeteer
import puppeteer from "puppeteer-core";
const session = await scrapa.sessions.create({ timeoutMin: 20 });
const browser = await puppeteer.connect({ browserWSEndpoint: session.connectUrl });
const page = await browser.newPage();
await page.goto("https://example.com");
await browser.disconnect();
await scrapa.sessions.close({ id: session.id });Disconnecting is not closing
browser.close() in Playwright and browser.disconnect() in Puppeteer end your connection, not the session. The browser stays up, and stays reserved, until you close the session or its window runs out.
That is deliberate: a script can drop, reconnect and find its pages where it left them. It also means a session nobody closes bills for the whole window it asked for, which is the one mistake worth a finally block.
What it costs
curl -X POST https://api.scrapa.app/v1/sessions/quote \
-H "authorization: Bearer sc_live_your_key" \
-H "content-type: application/json" \
-d '{"timeoutMin": 20}'
{"data":{"pool":"residential","timeoutMin":20,"perMinute":2,
"maxCost":40,"freeMb":1,"perMb":10}}Two credits a minute, reserved in full when the session starts and settled by the minutes you actually held it when it closes. So a twenty-minute window you finish in three minutes costs six credits. A browser that never came up costs nothing at all. Managed-pool bandwidth lands at close, the first megabyte included; your own proxy is never metered.
Where it leaves from
The pool you pick when you start the session is where every one of its bytes leaves from, for its whole life: pages, subresources, XHR, downloads. It cannot be changed from inside the browser: the one CDP command that could ask for another proxy has that argument stripped, and a page that tries to reach past the proxy reaches nothing at all. Bring your own with the custom pool, free on every plan. The browser's clock and languages follow the exit country you chose (`us` unless you say otherwise), so a page sees one place.
How long it lives
timeoutMin is the window, and your plan sets its ceiling along with how many sessions you may hold at once. When it runs out the browser is killed and the session settles itself. You do not have to be connected for that, and you never pay more than you reserved.
A session survives our deploys. The CDP socket goes straight to the browser rather than through the API, so shipping a version does not drop a connection you are in the middle of.