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

# Python SDK

> Complete guide to the Hyperbrowser Python SDK

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

  <Card horizontal icon="python" href="https://pypi.org/project/hyperbrowser/">
    View on PyPI
  </Card>
</CardGroup>

## Installation

Install the Hyperbrowser SDK:

<CodeGroup>
  ```bash pip theme={null}
  pip install hyperbrowser python-dotenv
  ```

  ```bash uv theme={null}
  uv add hyperbrowser python-dotenv
  ```
</CodeGroup>

## Quick Start

The Hyperbrowser Python SDK supports both synchronous and asynchronous clients.

### Synchronous Client

```python theme={null}
from hyperbrowser import Hyperbrowser
from dotenv import load_dotenv
import os

load_dotenv()

client = Hyperbrowser(api_key=os.getenv("HYPERBROWSER_API_KEY"))

# Create a session
session = client.sessions.create()
print(session.ws_endpoint)
```

### Asynchronous Client

```python theme={null}
import asyncio
from hyperbrowser import AsyncHyperbrowser
from dotenv import load_dotenv
import os

load_dotenv()

client = AsyncHyperbrowser(api_key=os.getenv("HYPERBROWSER_API_KEY"))

async def main():
    session = await client.sessions.create()
    print(session.ws_endpoint)

asyncio.run(main())
```

### Configuration Options

Both clients accept the same configuration parameters:

```python theme={null}
from hyperbrowser import Hyperbrowser

client = Hyperbrowser(
    api_key="your-api-key",  # Can also use HYPERBROWSER_API_KEY env var
    base_url="https://api.hyperbrowser.ai",  # Optional, default shown
    timeout=30,  # Request timeout in seconds
    runtime_proxy_override="regional-proxy.internal"  # Optional sandbox runtime override
)
```

## Typing Support

The SDK is fully typed end‑to‑end with Pydantic models. Import types from `hyperbrowser.models` and pass method parameters as model instances.

```python theme={null}
from hyperbrowser.models import CreateSessionParams
```

* **Parameter models**: Most methods accept a single Pydantic "params" object (parameter object/DTO). Create an instance and pass only the fields you need — `None`/unset fields are omitted on the wire.
* **Field names and aliases**: Use Pythonic field names in code; the SDK serializes to API field names automatically via `serialization_alias` (e.g., `use_ultra_stealth` → `useUltraStealth`).
* **Responses**: Methods return typed Pydantic models.

### Example: Create a session

```python theme={null}
from hyperbrowser.models import CreateSessionParams, ScreenConfig

session = client.sessions.create(
    CreateSessionParams(
        accept_cookies=True,
        screen=ScreenConfig(width=1920, height=1080)
    )
)
print("session created", session.id)
```

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

```python theme={null}
from hyperbrowser.models import SandboxListParams

response = client.sandboxes.list(
    SandboxListParams(
        status="active",  # optional: sandbox status filter
        search="sdk",  # optional: search term
        page=1,  # optional: page number
        limit=20,  # optional: results per page
    )
)

print(response.total_count)
print([entry.id for entry in response.sandboxes])
```

#### List Images

```python theme={null}
images = client.sandboxes.list_images()
print([image.image_name for image in images.images])
```

#### List Snapshots

```python theme={null}
from hyperbrowser.models import SandboxSnapshotListParams

snapshots = client.sandboxes.list_snapshots(
    SandboxSnapshotListParams(
        image_name="node",  # optional: only snapshots for this image
        status="created",  # optional: snapshot status filter
        limit=10,  # optional: max results
    )
)

print([snapshot.snapshot_name for snapshot in snapshots.snapshots])
```

#### Get Sandbox Info

```python theme={null}
detail = sandbox.info()
print(detail.runtime.base_url)
```

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

```python theme={null}
from hyperbrowser.models import CreateSandboxParams, SandboxExposeParams

sandbox = client.sandboxes.create(
    CreateSandboxParams(
        image_name="node",  # required unless restoring from a snapshot
        region="us-west",  # optional: sandbox region
        timeout_minutes=30,  # optional: max sandbox lifetime
        enable_recording=True,  # optional: record the sandbox
        exposed_ports=[SandboxExposeParams(port=3000, auth=True)],  # optional
    )
)
```

#### Start From A Snapshot

Start a new sandbox from a memory checkpoint.

```python theme={null}
from hyperbrowser.models import CreateSandboxParams

sandbox = client.sandboxes.create(
    CreateSandboxParams(
        snapshot_name="node-after-setup",  # required: snapshot name
        snapshot_id="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.

```python theme={null}
sandbox = client.sandboxes.connect(
    "sandbox-id",  # running sandbox ID
)
sandbox.connect()  # re-authenticate an existing handle in place
```

#### Stop Sandbox

```python theme={null}
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

```python theme={null}
from hyperbrowser.models import SandboxExposeParams

exposure = sandbox.expose(
    SandboxExposeParams(
        port=3000,  # required: port inside the sandbox
        auth=True,  # optional: require the sandbox bearer token
    )
)

print(exposure.url)
```

#### Unexpose Port

```python theme={null}
sandbox.unexpose(
    3000,  # exposed port to remove
)
```

#### Get Exposed URL

```python theme={null}
url = sandbox.get_exposed_url(
    3000,  # exposed port
)

print(url)
```

### Processes

#### Run A Command

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

```python theme={null}
result = sandbox.exec(
    "pwd && echo $FOO && whoami",
    cwd="/tmp",  # optional: working directory
    env={"FOO": "bar"},  # optional: environment variables
    timeout_ms=5000,  # optional: runtime limit in milliseconds
    run_as="root",  # optional: run as a specific sandbox user
)

print(result.stdout.strip())
```

If stdout contains JSON, parse it explicitly:

```python theme={null}
import json

result = sandbox.exec(
    """python3 -c 'import json; print(json.dumps({"user": "root", "ok": True}))'""",
    run_as="root",
)

data = json.loads(result.stdout.strip())
print(data["user"])
```

#### Start A Process

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

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

print(process.id)
```

#### Get A Process

```python theme={null}
process = sandbox.get_process(
    "process-id",  # process ID
)
```

#### List Processes

```python theme={null}
response = sandbox.processes.list(
    status=["queued", "running"],  # optional: one status or multiple statuses
    limit=20,  # optional: max results
    cursor="next-cursor",  # optional: pagination cursor
    created_after=1711929600000,  # optional: lower timestamp bound
    created_before=1712016000000,  # optional: upper timestamp bound
)

print([entry.id for entry in response.data])
```

#### Write Process Stdin

```python theme={null}
process.write_stdin(
    data="hello\n",  # optional: stdin payload
    encoding="utf8",  # optional: "utf8" or "base64"
    eof=True,  # optional: close stdin after this write
)
```

### Files

#### Read Text File

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

print(text)
```

#### Write Text File

```python theme={null}
sandbox.files.write_text(
    "/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

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

print([entry.path for entry in entries])
```

#### Watch A Directory

```python theme={null}
def on_event(event):
    print(event.type, event.name)


watch = sandbox.files.watch_dir(
    "/tmp/watch",  # directory to watch
    on_event,  # callback for file events
    recursive=True,  # optional: watch nested directories
    timeout_ms=30000,  # optional: auto-stop after this many ms
)

watch.stop()
```

#### Create Upload URL

```python theme={null}
upload = sandbox.files.upload_url(
    "/tmp/upload.txt",  # target path inside the sandbox
    one_time=True,  # optional: invalidate the URL after one use
    expires_in_seconds=60,  # optional: URL lifetime
)

print(upload.method, upload.url)
```

#### Create Download URL

```python theme={null}
download = sandbox.files.download_url(
    "/tmp/hello.txt",  # source path inside the sandbox
    one_time=True,  # optional: invalidate the URL after one use
    expires_in_seconds=60,  # optional: URL lifetime
)

print(download.method, download.url)
```

### Terminal

#### Create A Terminal

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

```python theme={null}
from hyperbrowser.models import SandboxTerminalCreateParams

terminal = sandbox.terminal.create(
    SandboxTerminalCreateParams(
        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
        timeout_ms=60000,  # optional: PTY timeout in milliseconds
    )
)

print(terminal.id)
```

#### Get A Terminal

```python theme={null}
terminal = sandbox.terminal.get(
    "terminal-id",  # terminal ID
    include_output=True,  # optional: include buffered output
)

print(len(terminal.current.output or []))
```

### Snapshots

#### Create A Memory Snapshot

```python theme={null}
from hyperbrowser.models import SandboxMemorySnapshotParams

snapshot = sandbox.create_memory_snapshot(
    SandboxMemorySnapshotParams(
        snapshot_name="node-after-setup",  # optional: custom snapshot name
    )
)

print(snapshot.snapshot_id)
```

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

```python theme={null}
from hyperbrowser.models import UpdateSessionSolveCaptchasParams

client.sessions.start_captcha_solving(
    "session-id",
    UpdateSessionSolveCaptchasParams(solver_type="visual"),
)

client.sessions.stop_captcha_solving("session-id")
```

`solver_type="visual"` enables the visual reCAPTCHA solver. Omit `solver_type` to use the default automatic CAPTCHA solver configuration.

### Manual CAPTCHA Evaluation

```python theme={null}
from hyperbrowser.models import CaptchaEvaluationParams

result = client.sessions.evaluate_captcha(
    "session-id",
    CaptchaEvaluationParams(
        captcha_type="recaptcha",
        iterations=2,
    ),
)
```

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

## Integration Examples

<CodeGroup>
  ```python Playwright (Sync) theme={null}
  from playwright.sync_api import sync_playwright
  from hyperbrowser import Hyperbrowser
  from hyperbrowser.models import CreateSessionParams
  from dotenv import load_dotenv
  import os

  load_dotenv()

  client = Hyperbrowser(api_key=os.getenv("HYPERBROWSER_API_KEY"))


  def main():
      # Create session
      session = client.sessions.create(params=CreateSessionParams(accept_cookies=True))

      try:
          # Connect with Playwright
          with sync_playwright() as p:
              browser = p.chromium.connect_over_cdp(session.ws_endpoint)
              default_context = browser.contexts[0]
              page = default_context.pages[0]

              page.goto("https://example.com")
              print(f"Page title: {page.title()}")

      except Exception as e:
          print(f"Error: {e}")
      finally:
          # Stop session
          client.sessions.stop(session.id)


  if __name__ == "__main__":
      main()
  ```

  ```python Playwright (Async) theme={null}
  import asyncio
  from playwright.async_api import async_playwright
  from hyperbrowser import AsyncHyperbrowser
  from hyperbrowser.models import CreateSessionParams
  from dotenv import load_dotenv
  import os

  load_dotenv()

  client = AsyncHyperbrowser(api_key=os.getenv("HYPERBROWSER_API_KEY"))


  async def main():
      # Create session
      session = await client.sessions.create(
          params=CreateSessionParams(accept_cookies=True)
      )

      try:
          # Connect with Playwright
          async with async_playwright() as p:
              browser = await p.chromium.connect_over_cdp(session.ws_endpoint)
              default_context = browser.contexts[0]
              page = default_context.pages[0]

              await page.goto("https://example.com")
              print(f"Page title: {await page.title()}")

      except Exception as e:
          print(f"Error: {e}")
      finally:
          # Stop session
          await client.sessions.stop(session.id)


  if __name__ == "__main__":
      asyncio.run(main())
  ```

  ```python Puppeteer theme={null}
  import asyncio
  from pyppeteer import connect
  from hyperbrowser import AsyncHyperbrowser
  from hyperbrowser.models import CreateSessionParams
  from dotenv import load_dotenv
  import os

  load_dotenv()

  client = AsyncHyperbrowser(api_key=os.getenv("HYPERBROWSER_API_KEY"))


  async def main():
      # Create session
      session = await client.sessions.create(
          params=CreateSessionParams(use_ultra_stealth=True)
      )

      try:
          # Connect with Puppeteer
          browser = await connect(
              browserWSEndpoint=session.ws_endpoint, defaultViewport=None
          )
          default_context = browser._defaultContext

          page = await default_context.newPage()
          await page.goto("https://example.com")
          title = await page.title()
          print(f"Page title: {title}")

      except Exception as e:
          print(f"Error: {e}")
      finally:
          # Stop session
          await client.sessions.stop(session.id)


  if __name__ == "__main__":
      asyncio.run(main())
  ```

  ```python Selenium theme={null}
  import os
  from dotenv import load_dotenv

  from selenium import webdriver
  from selenium.webdriver.remote.remote_connection import RemoteConnection
  from selenium.webdriver.chrome.options import Options
  from hyperbrowser import Hyperbrowser
  from hyperbrowser.models import CreateSessionParams

  # Load environment variables from .env file
  load_dotenv()

  client = Hyperbrowser(api_key=os.getenv("HYPERBROWSER_API_KEY"))


  class CustomRC(RemoteConnection):
      _signing_key = None

      def __init__(self, server: str, token: str):
          super().__init__(server)
          self._token = token

      def get_remote_connection_headers(self, parsed_url, keep_alive=False):
          headers = super().get_remote_connection_headers(parsed_url, keep_alive)
          headers.update({"x-hyperbrowser-token": self._token})
          return headers


  def main():
      session = client.sessions.create(params=CreateSessionParams(accept_cookies=True))

      try:
          custom_conn = CustomRC(session.webdriver_endpoint, session.token)
          driver = webdriver.Remote(custom_conn, options=Options())

          # Navigate to a URL
          driver.get("https://www.google.com")
          print("Navigated to Google")

          # Search
          search_box = driver.find_element("name", "q")
          search_box.send_keys("Selenium WebDriver")
          search_box.submit()
          print("Performed search")

          # Screenshot
          driver.save_screenshot("search_results.png")
          print("Screenshot saved")
      except Exception as e:
          print(f"Error: {e}")
      finally:
          client.sessions.stop(session.id)


  if __name__ == "__main__":
      main()
  ```
</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

<CodeGroup>
  ```python Sync theme={null}
  response = client.computer_action.click(
      "session-id",  # or session object
      x=500,
      y=300,
      button="left",  # "left" | "right" | "middle" | "back" | "forward" | "wheel"
      click_count=1,
      return_screenshot=False  # do not return screenshot (default: False)
  )

  print(response.success)
  print(response.screenshot) # base64 if requested

  ```

  ```python Async theme={null}
  response = await client.computer_action.click(
      "session-id",
      x=500,
      y=300,
      button="left",
      return_screenshot=False  # do not return screenshot (default: False)
  )
  ```
</CodeGroup>

### Type Text

<CodeGroup>
  ```python Sync theme={null}
  response = client.computer_action.type_text(
      "session-id",
      text="Hello, World!",
      return_screenshot=False  # do not return screenshot (default: False)
  )
  ```

  ```python Async theme={null}
  response = await client.computer_action.type_text(
      "session-id",
      text="Hello, World!"
  )
  ```
</CodeGroup>

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

<CodeGroup>
  ```python Sync theme={null}
  response = client.computer_action.press_keys(
      "session-id",
      keys=["Control_L", "a"],  # Key combination
      return_screenshot=False  # do not return screenshot (default: False)
  )
  ```

  ```python Async theme={null}
  response = await client.computer_action.press_keys(
      "session-id",
      keys=["Control_L", "a"]
  )
  ```
</CodeGroup>

### Move Mouse

<CodeGroup>
  ```python Sync theme={null}
  response = client.computer_action.move_mouse(
      "session-id",
      x=500,
      y=300,
      return_screenshot=False  # do not return screenshot (default: False)
  )
  ```

  ```python Async theme={null}
  response = await client.computer_action.move_mouse(
      "session-id",
      x=500,
      y=300
  )
  ```
</CodeGroup>

### Drag

<CodeGroup>
  ```python Sync theme={null}
  response = client.computer_action.drag(
      "session-id",
      coordinates=[
          {"x": 100, "y": 100},
          {"x": 200, "y": 200},
          {"x": 300, "y": 300}
      ],
      return_screenshot=False  # do not return screenshot (default: False)
  )
  ```

  ```python Async theme={null}
  response = await client.computer_action.drag(
      "session-id",
      coordinates=[
          {"x": 100, "y": 100},
          {"x": 200, "y": 200}
      ]
  )
  ```
</CodeGroup>

### Scroll

<CodeGroup>
  ```python Sync theme={null}
  response = client.computer_action.scroll(
      "session-id",
      x=500,
      y=300,
      scroll_x=0,
      scroll_y=100,
      return_screenshot=False  # do not return screenshot (default: False)
  )
  ```

  ```python Async theme={null}
  response = await client.computer_action.scroll(
      "session-id",
      x=500,
      y=300,
      scroll_y=100
  )
  ```
</CodeGroup>

### Screenshot

<CodeGroup>
  ```python Sync theme={null}
  response = client.computer_action.screenshot("session-id")
  print(response.screenshot)  # base64
  ```

  ```python Async theme={null}
  response = await client.computer_action.screenshot("session-id")
  ```
</CodeGroup>

## Support

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