> ## 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. In Hyperbrowser 1.0, request parameters are
described with `TypedDict`, so editors autocomplete keys directly inside plain
dictionary literals.

Import a request type from `hyperbrowser.types` when you want to annotate a
variable:

<CodeGroup>
  ```python Python 1.0+ theme={null}
  from hyperbrowser.types import CreateSessionParams

  params: CreateSessionParams = {"use_stealth": True}
  session = client.sessions.create(params)
  ```

  ```python Python (legacy) theme={null}
  from hyperbrowser.models import CreateSessionParams

  params: CreateSessionParams = CreateSessionParams(use_stealth=True)
  session = client.sessions.create(params)
  ```
</CodeGroup>

* **Request parameters**: Most methods accept a single typed dictionary. Pass only the fields you need.
* **Field names and aliases**: Use Pythonic snake-case keys; the SDK serializes them to API field names automatically (for example, `use_ultra_stealth` becomes `useUltraStealth`).
* **JSON Schema and open mappings**: User-owned dictionaries, including JSON Schema objects, agent action payloads, environment variables, and storage state values, are preserved rather than recursively renaming their keys. Use the SDK's snake-case key only for the field that contains the object, such as `"output_model_schema"` or `"schema"`.
* **Pydantic-generated schemas**: Schema fields also accept a Pydantic model class where supported; the SDK generates and dereferences its JSON Schema without mutating your model or input dictionary.
* **Responses**: Methods return typed Pydantic models.
* **Backwards compatibility**: Existing Pydantic request classes imported from `hyperbrowser.models` remain supported in 1.0, including their constructors and validation behavior.

<Info>
  Upgrading an existing application? See [Migrating to Python SDK
  1.0](/docs/sdks/python-1-0-migration) for side-by-side examples and a compatibility
  checklist.
</Info>

### Example: Create a session

<CodeGroup>
  ```python Python 1.0+ theme={null}
  session = client.sessions.create(
      {
          "accept_cookies": True,
          "screen": {"width": 1920, "height": 1080},
      }
  )
  print("session created", session.id)
  ```

  ```python Python (legacy) 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)
  ```
</CodeGroup>

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

<CodeGroup>
  ```python Python 1.0+ theme={null}
  response = 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
      }
  )

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

  ```python Python (legacy) 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])
  ```
</CodeGroup>

#### List Images

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

#### List Snapshots

<CodeGroup>
  ```python Python 1.0+ theme={null}
  snapshots = client.sandboxes.list_snapshots(
      {
          "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])
  ```

  ```python Python (legacy) 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])
  ```
</CodeGroup>

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

<CodeGroup>
  ```python Python 1.0+ theme={null}
  sandbox = client.sandboxes.create(
      {
          "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": [{"port": 3000, "auth": True}],  # optional
      }
  )
  ```

  ```python Python (legacy) 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
      )
  )
  ```
</CodeGroup>

#### Start From A Snapshot

Start a new sandbox from a memory checkpoint.

<CodeGroup>
  ```python Python 1.0+ theme={null}
  sandbox = client.sandboxes.create(
      {
          "snapshot_name": "node-after-setup",  # required: snapshot name
          "snapshot_id": "snapshot-id",  # optional: pin a specific snapshot version
      }
  )
  ```

  ```python Python (legacy) 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
      )
  )
  ```
</CodeGroup>

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

<CodeGroup>
  ```python Python 1.0+ theme={null}
  exposure = sandbox.expose(
      {
          "port": 3000,  # required: port inside the sandbox
          "auth": True,  # optional: require the sandbox bearer token
      }
  )

  print(exposure.url)
  ```

  ```python Python (legacy) 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)
  ```
</CodeGroup>

#### 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(...)`.

<CodeGroup>
  ```python Python 1.0+ theme={null}
  terminal = 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
          "timeout_ms": 60000,  # optional: PTY timeout in milliseconds
      }
  )

  print(terminal.id)
  ```

  ```python Python (legacy) 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)
  ```
</CodeGroup>

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

<CodeGroup>
  ```python Python 1.0+ theme={null}
  snapshot = sandbox.create_memory_snapshot(
      {
          "snapshot_name": "node-after-setup",  # optional: custom snapshot name
      }
  )

  print(snapshot.snapshot_id)
  ```

  ```python Python (legacy) 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)
  ```
</CodeGroup>

Sandbox guides:

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

## Session Runtime Updates

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

### CAPTCHA Solving

<CodeGroup>
  ```python Python 1.0+ theme={null}
  client.sessions.start_captcha_solving(
      "session-id",
      {"solver_type": "visual"},
  )

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

  ```python Python (legacy) 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")
  ```
</CodeGroup>

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

### Manual CAPTCHA Evaluation

<CodeGroup>
  ```python Python 1.0+ theme={null}
  result = client.sessions.evaluate_captcha(
      "session-id",
      {
          "captcha_type": "recaptcha",
          "iterations": 2,
      },
  )
  ```

  ```python Python (legacy) theme={null}
  from hyperbrowser.models import CaptchaEvaluationParams

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

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

## Integration Examples

<CodeGroup>
  ```python Playwright (Sync, Python 1.0+) theme={null}
  from playwright.sync_api import sync_playwright
  from hyperbrowser import Hyperbrowser
  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({"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 (Sync, legacy) theme={null}
  from playwright.sync_api import sync_playwright
  from hyperbrowser import Hyperbrowser
  from dotenv import load_dotenv
  import os
  from hyperbrowser.models import CreateSessionParams

  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, Python 1.0+) theme={null}
  import asyncio
  from playwright.async_api import async_playwright
  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():
      # Create session
      session = await client.sessions.create({"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 Playwright (Async, legacy) theme={null}
  import asyncio
  from playwright.async_api import async_playwright
  from hyperbrowser import AsyncHyperbrowser
  from dotenv import load_dotenv
  import os
  from hyperbrowser.models import CreateSessionParams

  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 Selenium (Python 1.0+) theme={null}
  import os
  from dotenv import load_dotenv

  from selenium import webdriver
  from selenium.webdriver.remote.client_config import ClientConfig
  from selenium.webdriver.remote.remote_connection import RemoteConnection
  from selenium.webdriver.chrome.options import Options
  from hyperbrowser import Hyperbrowser

  # 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__(
              client_config=ClientConfig(
                  remote_server_addr=server,
                  extra_headers={"x-hyperbrowser-token": token},
              )
          )


  def main():
      session = client.sessions.create({"accept_cookies": True})
      driver = None

      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:
          if driver is not None:
              driver.quit()
          client.sessions.stop(session.id)


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

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

  from selenium import webdriver
  from selenium.webdriver.remote.client_config import ClientConfig
  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__(
              client_config=ClientConfig(
                  remote_server_addr=server,
                  extra_headers={"x-hyperbrowser-token": token},
              )
          )


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

      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:
          if driver is not None:
              driver.quit()
          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"
      num_clicks=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 Python 1.0+ (sync) theme={null}
  response = client.computer_action.drag(
      "session-id",
      path=[
          {"x": 100, "y": 100},
          {"x": 200, "y": 200},
          {"x": 300, "y": 300},
      ],
      return_screenshot=False,  # do not return screenshot (default: False)
  )
  ```

  ```python Python (legacy, sync) theme={null}
  from hyperbrowser.models import Coordinate

  response = client.computer_action.drag(
      "session-id",
      path=[
          Coordinate(x=100, y=100),
          Coordinate(x=200, y=200),
          Coordinate(x=300, y=300),
      ],
      return_screenshot=False,  # do not return screenshot (default: False)
  )
  ```

  ```python Python 1.0+ (async) theme={null}
  response = await client.computer_action.drag(
      "session-id",
      path=[
          {"x": 100, "y": 100},
          {"x": 200, "y": 200},
      ],
  )
  ```

  ```python Python (legacy, async) theme={null}
  from hyperbrowser.models import Coordinate

  response = await client.computer_action.drag(
      "session-id",
      path=[
          Coordinate(x=100, y=100),
          Coordinate(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_x=0,
      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)
