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

# Node SDK

> Complete guide to the Hyperbrowser Node.js SDK

<CardGroup col={2}>
  <Card horizontal icon="github" href="https://github.com/hyperbrowserai/node-sdk">
    View on GitHub
  </Card>

  <Card horizontal icon="npm" href="https://www.npmjs.com/package/@hyperbrowser/sdk">
    View on NPM
  </Card>
</CardGroup>

## Installation

Install the Hyperbrowser SDK via npm or yarn:

<CodeGroup>
  ```bash npm theme={null}
  npm install @hyperbrowser/sdk dotenv
  ```

  ```bash yarn theme={null}
  yarn add @hyperbrowser/sdk dotenv
  ```
</CodeGroup>

## Quick Start

Initialize the client with your API key:

```typescript theme={null}
import { Hyperbrowser } from "@hyperbrowser/sdk";
import { config } from "dotenv";

config();

const client = new Hyperbrowser({
  apiKey: process.env.HYPERBROWSER_API_KEY,
});
```

### Configuration Options

```typescript theme={null}
interface HyperbrowserConfig {
  apiKey?: string; // API key (can also use HYPERBROWSER_API_KEY env var)
  baseUrl?: string; // Base API URL (default: "https://api.hyperbrowser.ai")
  timeout?: number; // Request timeout in milliseconds (default: 30000)
  runtimeProxyOverride?: string; // Optional host override for sandbox runtime traffic
}
```

## TypeScript Support

The SDK is fully typed with TypeScript. Import types from `@hyperbrowser/sdk/types`:

```typescript theme={null}
import {
  SessionDetail,
  CreateSessionParams,
  CreateSandboxParams,
  ScrapeJobResponse,
  CrawlJobResponse,
  ExtractJobResponse,
  BrowserUseTaskResponse,
  // ... and many more
} from "@hyperbrowser/sdk/types";
```

## Sandboxes

Sandbox APIs work in two layers.

Sandbox control methods use your API key to create, inspect, connect to, and stop sandboxes. When you create or connect to a sandbox, the SDK also retrieves a sandbox-scoped runtime token and returns an authenticated `SandboxHandle`.

Sandbox VM operations run inside that started sandbox and use the runtime token on the handle automatically. The SDK handles runtime token refreshes for you, so once you have a running `SandboxHandle`, you can call files, processes, terminal, networking, and snapshot methods without managing runtime auth yourself.

## Sandbox Control

Use these methods to inspect existing sandboxes and discover reusable sandbox
resources.

### Details

#### List Sandboxes

```typescript theme={null}
const response = await client.sandboxes.list({
  status: "active", // optional: sandbox status filter
  search: "sdk", // optional: search term
  page: 1, // optional: page number
  limit: 20, // optional: results per page
});

console.log(response.totalCount);
console.log(response.sandboxes.map((entry) => entry.id));
```

#### List Images

```typescript theme={null}
const { images } = await client.sandboxes.listImages();
console.log(images.map((image) => image.imageName));
```

#### List Snapshots

```typescript theme={null}
const { snapshots } = await client.sandboxes.listSnapshots({
  imageName: "node", // optional: only snapshots for this image
  status: "created", // optional: snapshot status filter
  limit: 10, // optional: max results
});

console.log(snapshots.map((snapshot) => snapshot.snapshotName));
```

#### Get Sandbox Info

```typescript theme={null}
const detail = await sandbox.info();
console.log(detail.runtime.baseUrl);
```

### Lifecycle

Use these methods to create, connect to, and stop sandbox instances.

#### Create Sandbox

Use `client.sandboxes.create(...)` to start a sandbox from an image.

```typescript theme={null}
const sandbox = await client.sandboxes.create({
  imageName: "node", // required unless restoring from a snapshot
  region: "us-west", // optional: sandbox region
  timeoutMinutes: 30, // optional: max sandbox lifetime
  enableRecording: true, // optional: record the sandbox
  exposedPorts: [{ port: 3000, auth: true }], // optional: pre-expose ports
});
```

#### Start From A Snapshot

Start a new sandbox from a memory checkpoint.

```typescript theme={null}
const sandbox = await client.sandboxes.create({
  snapshotName: "node-after-setup", // required: snapshot name
  snapshotId: "snapshot-id", // optional: pin a specific snapshot version
});
```

#### Connect To A Running Sandbox

Use `connect(...)` to re-authenticate a running sandbox, refresh its runtime token, and return a `SandboxHandle` for runtime operations. If you already have a handle, call `sandbox.connect()` to re-authenticate it in place.

```typescript theme={null}
const sandbox = await client.sandboxes.connect(
  "sandbox-id" // running sandbox ID
);
await sandbox.connect(); // re-authenticate an existing handle in place
```

#### Stop Sandbox

```typescript theme={null}
await sandbox.stop(); // stop the sandbox when finished
```

## Sandbox VM Operations

These methods operate on the sandbox VM itself when the sandbox is running.

### Networking

#### Expose Port

```typescript theme={null}
const exposure = await sandbox.expose({
  port: 3000, // required: port inside the sandbox
  auth: true, // optional: require the sandbox bearer token
});

console.log(exposure.url);
```

#### Unexpose Port

```typescript theme={null}
await sandbox.unexpose(
  3000 // exposed port to remove
);
```

#### Get Exposed URL

```typescript theme={null}
const url = sandbox.getExposedUrl(
  3000 // exposed port
);

console.log(url);
```

### Processes

#### Run A Command

Use `sandbox.exec(...)` for one-shot commands. You can also pass a plain string
like `await sandbox.exec("node -v")`. Commands are executed through `/bin/sh -lc`.

```typescript theme={null}
const result = await sandbox.exec("pwd && echo $FOO && whoami", {
  cwd: "/tmp", // optional: working directory
  env: { FOO: "bar" }, // optional: environment variables
  timeoutMs: 5_000, // optional: runtime limit in milliseconds
  runAs: "root", // optional: run as a specific sandbox user
});

console.log(result.stdout.trim());
```

If stdout contains JSON, parse it explicitly:

```typescript theme={null}
const result = await sandbox.exec(
  `python3 -c 'import json; print(json.dumps({"user": "root", "ok": True}))'`,
  { runAs: "root" }
);

const data = JSON.parse(result.stdout.trim());
console.log(data.user);
```

#### Start A Process

Use `sandbox.processes.start(...)` for long-running processes.

```typescript theme={null}
const process = await sandbox.processes.start("sleep 30", {
  cwd: "/tmp", // optional: working directory
  env: { FOO: "bar" }, // optional: environment variables
  runAs: "root", // optional: run as a specific sandbox user
});

console.log(process.id);
```

#### Get A Process

```typescript theme={null}
const process = await sandbox.getProcess(
  "process-id" // process ID
);
```

#### List Processes

```typescript theme={null}
const response = await sandbox.processes.list({
  status: ["queued", "running"], // optional: one status or multiple statuses
  limit: 20, // optional: max results
  cursor: "next-cursor", // optional: pagination cursor
  createdAfter: Date.now() - 60_000, // optional: lower timestamp bound
  createdBefore: Date.now(), // optional: upper timestamp bound
});

console.log(response.data.map((entry) => entry.id));
```

#### Write Process Stdin

```typescript theme={null}
await process.writeStdin({
  data: "hello\n", // optional: stdin payload
  encoding: "utf8", // optional: "utf8" or "base64"
  eof: true, // optional: close stdin after this write
});
```

### Files

#### Read Text File

```typescript theme={null}
const text = await sandbox.files.readText(
  "/tmp/hello.txt", // path inside the sandbox
  {
    offset: 0, // optional: byte offset
    length: 128, // optional: max bytes to read
  }
);

console.log(text);
```

#### Write Text File

```typescript theme={null}
await sandbox.files.writeText(
  "/tmp/hello.txt", // path inside the sandbox
  "hello from sandbox", // file contents
  {
    append: true, // optional: append instead of overwrite
    mode: "0640", // optional: chmod-style mode string
  }
);
```

#### List Files

```typescript theme={null}
const entries = await sandbox.files.list(
  "/tmp", // directory path
  {
    depth: 2, // optional: traversal depth, minimum 1
  }
);

console.log(entries.map((entry) => entry.path));
```

#### Watch A Directory

```typescript theme={null}
const handle = await sandbox.files.watchDir(
  "/tmp/watch", // directory to watch
  (event) => {
    console.log(event.type, event.name);
  }, // callback for file events
  {
    recursive: true, // optional: watch nested directories
    timeoutMs: 30_000, // optional: auto-stop after this many ms
  }
);

await handle.stop();
```

#### Create Upload URL

```typescript theme={null}
const upload = await sandbox.files.uploadUrl(
  "/tmp/upload.txt", // target path inside the sandbox
  {
    oneTime: true, // optional: invalidate the URL after one use
    expiresInSeconds: 60, // optional: URL lifetime
  }
);

console.log(upload.method, upload.url);
```

#### Create Download URL

```typescript theme={null}
const download = await sandbox.files.downloadUrl(
  "/tmp/hello.txt", // source path inside the sandbox
  {
    oneTime: true, // optional: invalidate the URL after one use
    expiresInSeconds: 60, // optional: URL lifetime
  }
);

console.log(download.method, download.url);
```

### Terminal

#### Create A Terminal

Use `sandbox.terminal.create(...)` or the alias `sandbox.pty.create(...)`.

```typescript theme={null}
const terminal = await sandbox.terminal.create({
  command: "bash", // required: command to launch
  args: ["-l"], // optional: command arguments
  cwd: "/tmp", // optional: working directory
  env: { FOO: "bar" }, // optional: environment variables
  rows: 24, // optional: terminal rows
  cols: 80, // optional: terminal columns
  timeoutMs: 60_000, // optional: PTY timeout in milliseconds
});

console.log(terminal.id);
```

#### Get A Terminal

```typescript theme={null}
const terminal = await sandbox.terminal.get(
  "terminal-id", // terminal ID
  true // optional: include buffered output
);

console.log(terminal.current.output?.length ?? 0);
```

### Snapshots

#### Create A Memory Snapshot

```typescript theme={null}
const snapshot = await sandbox.createMemorySnapshot({
  snapshotName: "node-after-setup", // optional: custom snapshot name
});

console.log(snapshot.snapshotId);
```

Sandbox guides:

* [Creating Sandboxes](/sandboxes/create)
* [Sandbox Lifecycle](/sandboxes/lifecycle)
* [Sandbox Processes](/sandboxes/processes)
* [Local Filesystem](/sandboxes/filesystem/overview)
* [Sandbox Terminal](/sandboxes/terminal)
* [Sandbox Snapshots](/sandboxes/snapshots)

## Session Runtime Updates

Use session update helpers to change supported settings on an active browser without recreating it.

### CAPTCHA Solving

```typescript theme={null}
await client.sessions.startCaptchaSolving("session-id", {
  solverType: "visual",
});

await client.sessions.stopCaptchaSolving("session-id");
```

`solverType: "visual"` enables the visual reCAPTCHA solver. Omit `solverType` to use the default automatic CAPTCHA solver configuration.

### Manual CAPTCHA Evaluation

```typescript theme={null}
const result = await client.sessions.evaluateCaptcha("session-id", {
  captchaType: "recaptcha",
  iterations: 2,
});
```

Supported `captcha` and `captchaType` values are `turnstile`, `cloudflare-challenge`, `aliexpress`, `recaptcha`, and `amazon`.

## Integration Examples

<CodeGroup>
  ```typescript Playwright theme={null}
  import { chromium } from "playwright-core";
  import { Hyperbrowser } from "@hyperbrowser/sdk";
  import { config } from "dotenv";

  config();

  const client = new Hyperbrowser({
    apiKey: process.env.HYPERBROWSER_API_KEY,
  });

  async function main() {
    const session = await client.sessions.create({
      acceptCookies: true,
    });

    try {
      const browser = await chromium.connectOverCDP(session.wsEndpoint);
      const defaultContext = browser.contexts()[0];
      const page = defaultContext.pages()[0];

      await page.goto("https://example.com");
      console.log(await page.title());
    } catch (err) {
      console.error(`Encountered error: ${err}`);
    } finally {
      await client.sessions.stop(session.id);
    }
  }

  main().catch(console.error);
  ```

  ```typescript Puppeteer theme={null}
  import { connect } from "puppeteer-core";
  import { Hyperbrowser } from "@hyperbrowser/sdk";
  import { config } from "dotenv";

  config();

  const client = new Hyperbrowser({
    apiKey: process.env.HYPERBROWSER_API_KEY,
  });

  async function main() {
    const session = await client.sessions.create({
      acceptCookies: true,
    });

    try {
      const browser = await connect({
        browserWSEndpoint: session.wsEndpoint,
        defaultViewport: null,
      });
      const defaultContext = browser.defaultBrowserContext();
      const page = (await defaultContext.pages())[0];

      await page.goto("https://example.com");
      console.log(await page.title());
    } catch (err) {
      console.error(`Encountered error: ${err}`);
    } finally {
      await client.sessions.stop(session.id);
    }
  }

  main().catch(console.error);
  ```

  ```typescript Selenium theme={null}
  import dotenv from "dotenv";
  import https from "https";
  import { Builder, WebDriver } from "selenium-webdriver";
  import fs from "fs";
  import { Options } from "selenium-webdriver/chrome";
  import { Hyperbrowser } from "@hyperbrowser/sdk";

  // Load environment variables from .env file
  dotenv.config();

  const client = new Hyperbrowser({ apiKey: process.env.HYPERBROWSER_API_KEY });

  async function main() {
    const session = await client.sessions.create();

    if (!session.webdriverEndpoint) {
      await client.sessions.stop(session.id);
      throw new Error("No webdriver endpoint found");
    }

    const customHttpsAgent = new https.Agent({});
    (customHttpsAgent as any).addRequest = (req: any, options: any) => {
      req.setHeader("x-hyperbrowser-token", session.token);
      (https.Agent.prototype as any).addRequest.call(
        customHttpsAgent,
        req,
        options
      );
    };

    const driver: WebDriver = await new Builder()
      .forBrowser("chrome")
      .usingHttpAgent(customHttpsAgent)
      .usingServer(session.webdriverEndpoint)
      .setChromeOptions(new Options())
      .build();

    try {
      // Navigate to a URL
      await driver.get("https://www.google.com");
      console.log("Navigated to Google");

      // Search
      const searchBox = await driver.findElement({ name: "q" });
      await searchBox.sendKeys("Selenium WebDriver");
      await searchBox.submit();
      console.log("Performed search");

      // Screenshot
      await driver.takeScreenshot().then((data) => {
        fs.writeFileSync("search_results.png", data, "base64");
      });
      console.log("Screenshot saved");
    } finally {
      await driver.quit();
      await client.sessions.stop(session.id);
    }
  }

  main().catch(console.error);
  ```
</CodeGroup>

## Computer Actions

Programmatically control the browser with low-level actions.
For the `session-id` parameter, you can also just pass in the detailed session object itself, and it is actually recommended to do so.

### Click

```typescript theme={null}
const response = await client.computerAction.click(
  "session-id",  // or session object
  500,  // x coordinate
  300,  // y coordinate
  "left",  // button: "left" | "right" | "middle" | "back" | "forward" | "wheel"
  1,  // number of clicks
  false  // do not return screenshot (default: false)
);

console.log(response.success);
console.log(response.screenshot);  // base64 if requested
```

### Type Text

```typescript theme={null}
const response = await client.computerAction.typeText(
  "session-id",
  "Hello, World!",
  false  // do not return screenshot (default: false)
);
```

### Press Keys

Uses the xdotool format for keys: [https://github.com/sickcodes/xdotool-gui/blob/master/key\_list.csv](https://github.com/sickcodes/xdotool-gui/blob/master/key_list.csv)

```typescript theme={null}
const response = await client.computerAction.pressKeys(
  "session-id",
  ["Control_L", "a"],  // Key combination
  false  // do not return screenshot (default: false)
);
```

### Move Mouse

```typescript theme={null}
const response = await client.computerAction.moveMouse(
  "session-id",
  500,  // x
  300,  // y
  false  // do not return screenshot (default: false)
);
```

### Drag

```typescript theme={null}
const response = await client.computerAction.drag(
  "session-id",
  [
    { x: 100, y: 100 },
    { x: 200, y: 200 },
    { x: 300, y: 300 },
  ],
  false  // do not return screenshot (default: false)
);
```

### Scroll

```typescript theme={null}
const response = await client.computerAction.scroll(
  "session-id",
  500,  // x position
  300,  // y position
  0,  // scroll x delta
  100,  // scroll y delta
  false  // do not return screenshot (default: false)
);
```

### Screenshot

```typescript theme={null}
const response = await client.computerAction.screenshot("session-id");
console.log(response.screenshot);  // base64
```

## Support

* **GitHub Issues**: [https://github.com/hyperbrowserai/node-sdk/issues](https://github.com/hyperbrowserai/node-sdk/issues)
* **Email**: [info@hyperbrowser.ai](mailto:info@hyperbrowser.ai)
