> ## 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.

# TypeScript SDK

> Use the Surfsky client from Node.js or Bun to automate pages, pool browsers, and manage profiles.

Requires Node.js 22 or newer, or Bun. The `surfsky` package uses ES modules and includes TypeScript definitions.

```bash theme={null}
npm install surfsky
```

Set `SURFSKY_API_TOKEN` and `SURFSKY_API_BASE_URL` from the [dashboard](https://app.surfsky.io). See [credential setup](/quickstart#before-you-start). You can also pass `apiToken` and `baseUrl` to the constructor.

## Start a browser

Save as `first-browser.mjs` and run `node first-browser.mjs`. The same code can be used in a TypeScript project configured for ESM.

```javascript theme={null}
import { Surfsky } from "surfsky";

const client = new Surfsky();
const browser = await client.browser();
try {
  await browser.goto("https://example.com");
  console.log(await browser.title());
} finally {
  await browser.close();
}
```

Expected output is `Example Domain`. `browser.close()` stops the session. Put it in `finally` so it also runs when a page operation throws. Cleanup cannot complete if the process is forcibly terminated or loses access to the API.

On runtimes that support explicit resource management, such as Node.js 24+ or Bun, `await using` can handle cleanup at scope exit:

```typescript theme={null}
await using browser = await client.browser();
await browser.goto("https://example.com");
```

For Node.js 22, use `try`/`finally` or a TypeScript build that transforms `await using` for your runtime.

<span id="driving-the-page" />

## Interact with the page

Methods include `goto`, `click`, `type`, `hover`, `innerText`, and `waitForSelector`. Run page operations while the browser is open, using selectors from your target page:

```typescript theme={null}
await browser.type('input[name="email"]', "reader@example.com");
await browser.click('button[type="submit"]');
await browser.waitForSelector(".confirmation");
console.log(await browser.innerText(".confirmation"));
```

`evaluate()` runs JavaScript in an isolated execution context by default. Use `isolated: false` to access variables defined by the page. DOM changes remain visible to the page.

```typescript theme={null}
console.log(await browser.evaluate("document.title"));
```

### Save traffic

Choose resource types when starting a browser:

```typescript theme={null}
const browser = await client.browser({
  blockResources: ["image", "font", "media"],
});
```

Close this browser after use. Keep resources required by your target page; see [Speed optimization](/speed-optimization).

<span id="read-the-api-instead-of-the-html" />

### Capture a page's API response

Start capture before navigation. Adapt the URL and response path to your application:

```typescript theme={null}
await browser.captureResponses("/api/search");
await browser.goto("https://your-site.example/search");
const response = await browser.waitForResponse("/api/search");
const data = response.json();
```

### Multiple tabs

`browser` operates on the first tab. `browser.pages` contains other tabs and popups:

```typescript theme={null}
await browser.newPage();
await browser.pages[1].goto("https://example.com");
console.log(await browser.pages[1].title());
```

See the [session page limit](/troubleshooting#session-page-limit).

<span id="process-items-concurrently" />

## Running many browsers

```javascript theme={null}
import { Surfsky } from "surfsky";

const client = new Surfsky();
const outcomes = await client.map(
  async (browser, url) => {
    await browser.goto(url);
    return browser.title();
  },
  ["https://example.com", "https://example.org"],
  { concurrency: 2 },
);

for (const outcome of outcomes) {
  console.log(outcome.ok ? outcome.value : outcome.error);
}
```

The pool collects an outcome for each job and can reuse browsers between jobs. Leases retain browser state, including cookies. Do not assume each item receives an empty browser.

For manual leasing on Node.js 24+ or Bun:

```typescript theme={null}
await using pool = await client.browsers({ concurrency: 2 });
await pool.lease(async (browser) => {
  await browser.goto("https://example.com");
  console.log(await browser.title());
});
```

See [Concurrency](/concurrency) for account limits and work distributed across processes.

## Proxies

Pass `proxy` to `browser()` or `session()`:

```typescript theme={null}
const browser = await client.browser({
  proxy: { tier: "premium", country: "us" },
});
```

You can also select `tier: "shared"` or supply a proxy URL. Close the browser after use. `client.proxies` provides location and quota lookups; see [Proxies](/proxies).

## Profiles

```typescript theme={null}
const profile = await client.profiles.create({
  title: "my profile",
  fingerprint: { os: "win" },
  storage_options: { cookies: true, localstorage: true },
});

const browser = await client.browser({ profileUuid: profile.uuid });
try {
  await browser.goto("https://example.com");
} finally {
  await browser.close();
}
```

Save `profile.uuid` and start that profile on later runs. See [Sessions](/sessions#reuse-a-login) for login persistence.

<span id="escape-hatch" />

<span id="full-reference" />

## Raw requests

```typescript theme={null}
const response = await client.request("GET", "/proxies/shared/quota");
if (!response.ok) throw new Error(`HTTP ${response.status}`);
console.log(await response.json());
```

`request()` returns the raw response and does not throw on HTTP status. The [SDK repository](https://github.com/surfskyio/surfsky-js) contains the complete method reference and examples.
