> ## Documentation Index
> Fetch the complete documentation index at: https://docs.surfsky.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Puppeteer

> Connect Puppeteer to a Surfsky browser, open a page, and stop the session.

Puppeteer attaches to a Surfsky browser with `puppeteer.connect()` and the session's `ws_url`. The browser runs in Surfsky's cloud, so `puppeteer-core` is enough and no local Chrome is needed.

<Warning>
  Standard Puppeteer creates a utility world, helper scripts, and bindings that detection scripts can identify. Use a patched fork such as rebrowser-puppeteer for sites with bot protection.
</Warning>

<span id="using-surfsky-with-puppeteer" />

## Prerequisites

Node.js 22 or newer. Set your [API token and base URL](/quickstart#before-you-start), then install the package:

```bash theme={null}
npm install puppeteer-core
```

The example starts a browser with an empty request body, which uses your account's default proxy pool. Proxy, fingerprint, and other start options are in the [API reference](/api-reference/profiles/start-one-time-session).

<span id="code-example" />

## Run an example

The example starts a one-time browser, connects, opens a page, prints its title, and closes the browser. To keep cookies and login state between runs, start a [persistent profile](/sessions#persistent-profiles) instead; the connection code is the same.

Save as `surfsky-puppeteer.mjs`:

```javascript theme={null}
import puppeteer from "puppeteer-core";

const baseUrl = process.env.SURFSKY_API_BASE_URL.replace(/\/+$/, "");
const headers = {
  "X-Cloud-Api-Token": process.env.SURFSKY_API_TOKEN,
  "Content-Type": "application/json",
};

const response = await fetch(`${baseUrl}/profiles/one_time`, {
  method: "POST", headers, body: "{}", signal: AbortSignal.timeout(120_000),
});
if (!response.ok) throw new Error(`Start failed: ${response.status} ${await response.text()}`);
const session = await response.json();

const browser = await puppeteer.connect({
  browserWSEndpoint: session.ws_url,
  defaultViewport: null,
});
const page = (await browser.pages())[0] ?? await browser.newPage();
await page.goto("https://example.com");
console.log(await page.title());
await browser.close();
```

```bash theme={null}
node surfsky-puppeteer.mjs
```

Expected output:

```text theme={null}
Example Domain
```

`defaultViewport: null` keeps the window size from the profile's fingerprint. Without it Puppeteer forces its own 800x600 viewport.

<span id="important-notes" />

## Stop the session

`browser.close()` closes the remote browser and ends the session. `browser.disconnect()` only drops the connection and leaves the browser running. You can also stop through the API with the `internal_uuid` from the start response:

```bash theme={null}
curl --fail-with-body -X POST \
  "$SURFSKY_API_BASE_URL/profiles/INTERNAL_UUID/stop" \
  -H "X-Cloud-Api-Token: $SURFSKY_API_TOKEN"
```

Otherwise the browser stops after the inactivity timeout, 30 seconds by default.
