> ## Documentation Index
> Fetch the complete documentation index at: https://porter-mintlify-2aef9693.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Python Sandbox SDK reference

> Core classes and methods in the porter-sandbox Python Sandbox SDK

Import the top-level client from `porter_sandbox`:

```python theme={null}
from porter_sandbox import Porter, AsyncPorter
```

## `Porter`

`Porter` is the top-level sync client. It exposes resource namespaces for sandboxes, volumes, health, and readiness.

```python theme={null}
from porter_sandbox import Porter

with Porter() as porter:
    sandbox = porter.sandboxes.create(
        image="python:3.12-slim",
        name="my-sandbox",
    )
```

Constructor options:

| Option     | Type            | Description                                                                                                                                                                                                                                                                                                                                                                     |
| ---------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `api_key`  | `str \| None`   | Optional API key. Falls back to the `PORTER_SANDBOX_API_KEY` environment variable. Only required when invoking the Sandbox API from outside the sandbox-enabled cluster. Create one from **Settings > API tokens** in the Porter Dashboard. Creating API tokens requires admin permissions, and the token must have developer permissions to authenticate with the Sandbox API. |
| `base_url` | `str \| None`   | Optional API base URL override. Falls back to the `PORTER_SANDBOX_BASE_URL` environment variable. If neither is set, the SDK builds the external Porter API URL from the API token's project and `PORTER_CLUSTER_ID`, then falls back to the in-cluster Sandbox API URL when running in a Kubernetes cluster, and otherwise raises an error explaining what to set.             |
| `timeout`  | `float \| None` | Request timeout in seconds. Defaults to 30 seconds.                                                                                                                                                                                                                                                                                                                             |

## `porter.sandboxes`

### `create`

```python theme={null}
sandbox = porter.sandboxes.create(
    image="python:3.12-slim",
    name="my-sandbox",
    command=None,
    args=None,
    env=None,
    tags={"workflow": "demo"},
    volume_mounts=None,
)
```

Arguments:

| Argument        | Type                                  | Description                                                                                                                                                                                                                                                                                               |
| --------------- | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `image`         | `str`                                 | Container image to run.                                                                                                                                                                                                                                                                                   |
| `name`          | `str \| None`                         | Optional cluster-unique sandbox name. Use lowercase letters, numbers, and hyphens; start and end with a letter or number. Names currently cannot be reused, even after termination.                                                                                                                       |
| `command`       | `list[str] \| None`                   | Optional entrypoint override.                                                                                                                                                                                                                                                                             |
| `args`          | `list[str] \| None`                   | Optional arguments passed to the command.                                                                                                                                                                                                                                                                 |
| `env`           | `dict[str, str] \| None`              | Environment variables to set in the sandbox.                                                                                                                                                                                                                                                              |
| `env_groups`    | `list[str] \| None`                   | Names of [environment groups](/applications/configure/environment-groups) on the cluster whose variables are injected into the sandbox, resolved to their latest version at create time. On a key conflict a later group wins over an earlier one, and an explicit `env` entry wins over any group value. |
| `tags`          | `dict[str, str] \| None`              | Key-value labels for finding related sandboxes.                                                                                                                                                                                                                                                           |
| `volume_mounts` | `dict[str, str] \| None`              | Volume IDs keyed by absolute mount path inside the sandbox.                                                                                                                                                                                                                                               |
| `networking`    | `list[SandboxNetworkingSpec] \| None` | Port to expose and, optionally, the domain and visibility to serve it on. Omit to expose nothing. See [Sandbox Networking](/sandboxes/networking).                                                                                                                                                        |
| `ttl_seconds`   | `int \| None`                         | Maximum lifetime in seconds, counted from creation. The sandbox is terminated once it elapses. Omit for no limit. See [sandbox lifetime](/sandboxes/overview#sandbox-lifetime).                                                                                                                           |

Returns a `Sandbox` handle.

### `get`

```python theme={null}
sandbox = porter.sandboxes.get("my-sandbox")
```

Looks up a sandbox by name and returns a `Sandbox` handle. Use names as the canonical reference for sandboxes you need to operate on later.

### `list`

```python theme={null}
sandboxes = porter.sandboxes.list(tags={"workflow": "demo"}, page=1)
```

Lists sandboxes, optionally filtered by tags and page.

## `Sandbox`

`Sandbox` is the handle returned by `porter.sandboxes`.

Properties:

| Property | Type                          | Description                 |
| -------- | ----------------------------- | --------------------------- |
| `id`     | `str`                         | Sandbox identifier.         |
| `phase`  | `StatusResponsePhase \| None` | Last-known lifecycle phase. |
| `tags`   | `dict[str, str] \| None`      | Last-known tags.            |

Methods:

```python theme={null}
status = sandbox.refresh()
result = sandbox.exec(["python", "-c", "print('hi')"])
logs = sandbox.logs(limit=100)
sandbox.terminate()
```

| Method                         | Description                                                              |
| ------------------------------ | ------------------------------------------------------------------------ |
| `refresh()`                    | Refetches status and updates `phase` and `tags`.                         |
| `exec(command, timeout=None)`  | Runs a command in the sandbox and returns stdout, stderr, and exit code. |
| `logs(since=None, limit=None)` | Fetches structured log lines.                                            |
| `terminate()`                  | Terminates the sandbox.                                                  |

### `exec` timeouts

`exec` is long-running: the client sends no timeout by default, so the call waits for the command to exit. Pass `timeout` (seconds) to cap how long the client waits. When the timeout elapses, the SDK raises `SandboxTimeoutError` and does not retry, since the command may still be running server-side.

```python theme={null}
from porter_sandbox import SandboxTimeoutError

try:
    result = sandbox.exec(["sleep", "120"], timeout=30.0)
except SandboxTimeoutError:
    print("exec exceeded 30s; the command may still be running in the sandbox")
```

The client-wide `timeout` on `Porter` still applies to every other request (30 seconds by default).

## Volumes

Volumes are persistent storage that can be mounted into sandboxes.

```python theme={null}
volume = porter.volumes.create(name="my-data")

sandbox = porter.sandboxes.create(
    image="python:3.12-slim",
    volume_mounts={"/mnt/data": volume.id},
)
```

Sandbox and volume names may contain lowercase letters, numbers, and hyphens, and must start and end with a letter or number. Sandbox names currently cannot be reused, even after termination. Volume names must be unique for the lifetime of the volume and can be reused after deletion.

Volume methods:

| Method                             | Description                                                      |
| ---------------------------------- | ---------------------------------------------------------------- |
| `porter.volumes.list()`            | Lists volumes.                                                   |
| `porter.volumes.create(name=None)` | Creates a volume. Pass a name when you need stable lookup later. |
| `porter.volumes.get(name)`         | Fetches a volume by name.                                        |
| `porter.volumes.delete(name)`      | Deletes a volume by name.                                        |

## Async client

`AsyncPorter` mirrors `Porter` with awaitable methods:

```python theme={null}
from porter_sandbox import AsyncPorter

async with AsyncPorter() as porter:
    sandbox = await porter.sandboxes.create(
        image="python:3.12-slim",
        name="async-sandbox",
    )
    result = await sandbox.exec(["python", "-c", "print('hi')"])
    await sandbox.terminate()
```

## Low-level client

The top-level client exposes generated low-level clients through `.raw`:

```python theme={null}
raw_client = porter.raw
raw_sandboxes = porter.sandboxes.raw
```

Use these only when you need an endpoint that is not wrapped by the ergonomic SDK surface yet.

## Next steps

* [Python Sandbox SDK volumes](/sandbox/sdk/python/volumes)
* [Python Sandbox SDK errors](/sandbox/sdk/python/errors)
