---
title: "Python SDK · Promigence"
description: "The promigence Python package (0.1.0b7, Python 3.9 or newer): start sandboxes and run commands in them, move files, pause or fork a sandbox and end it, with sync and async clients."
url: "https://www.promigence.ai/docs/sdk-python"
site: "Promigence"
---

# Python SDK

The `promigence` package on PyPI, version 0.1.0b7, for Python 3.9 or newer. It has two parts: a `Sandbox` class for working in one sandbox, and the `Promigence` client for everything else the API does. Each has an async twin.

Terminal

```
pip install promigence
```

It brings one dependency with it, `httpx`. Using TypeScript instead? The TypeScript SDK has the same two parts.

[TypeScript SDK](https://www.promigence.ai/docs/sdk)

## Sign in

The SDK finds your API key the way the CLI does, and the first match wins: the `api_key=` argument, then the `PROMIGENCE_API_KEY` environment variable, then the key `promigence login` saved on this machine.

Terminal

```
export PROMIGENCE_API_KEY=...      # or sign in once with: promigence login
```

Every run has a spend cap in US dollars: `spend_cap=`, else `PROMIGENCE_SPEND_CAP`, else your organization's default.

## Start a sandbox, run a command

`Sandbox.create()` starts a sandbox from the built-in `promigence/base` snapshot. Pass a snapshot ID or alias, or a template's name, to start from another.

One sandbox, one command

Python

```
from promigence import Sandbox

with Sandbox.create(spend_cap=1) as sb:        # starts from promigence/base
    result = sb.commands.run("python3 -c 'print(6 * 7)'")
    print(result.stdout)                        # 42
    print(result.exit_code)                     # 0
# leaving the block ends the sandbox
```

A command that exits non-zero raises `CommandExitError`; pass `throw_on_exit=False` to get its result instead. Each command takes a `timeout_ms`, and every duration in the SDK is in milliseconds.

To start from a container image, name it with a tag or a digest and pass `allow_build=True`. The first use builds a snapshot of the image, billed per second at its size's rate while it builds. Later sandboxes reuse it.

From an image

Python

```
sb = Sandbox.create(image="python:3.12", allow_build=True, spend_cap=1)
```

## Files

Write, read, list, remove

Python

```
sb.files.write("/work/notes.txt", "hello\n")
print(sb.files.read("/work/notes.txt"))          # hello
print([e.name for e in sb.files.list("/work")])
sb.files.remove("/work/notes.txt")
```

`files.read` returns text. For binary files, pass `format="bytes"`.

## Pause and resume

Python

```
sb.pause()                  # not billed by the second while paused
sb.resume()                 # back where it was
sb.set_auto_pause(300)      # or let it pause itself after 5 idle minutes
```

A paused sandbox keeps its files and is not billed by the second. Commands left running in the background end at the pause.

[How long a paused sandbox is kept](https://www.promigence.ai/docs/limits)

## Fork

`Sandbox.fork(snapshot, count=N)` starts N identical sandboxes from one snapshot and returns when all of them are ready. To fork a sandbox as it is now, save it as a snapshot first.

Five copies of a sandbox

Python

```
from promigence import Promigence, Sandbox

with Promigence() as client:
    saved = client.sandboxes.snapshot(sb.sandbox_id, alias="my-env")
    copies = Sandbox.fork(saved["snapshot_id"], count=5, spend_cap=2)
    for copy in copies:
        print(copy.commands.run("ls /work").stdout)
        copy.kill()
```

## End a sandbox

`sb.kill()` ends a sandbox and stops its bill, and returns `False` if it had already ended. Leaving a `with` block calls it for you. `set_timeout(timeout_ms)` changes how long a sandbox may run.

## Errors

Everything the SDK raises on purpose is a `PromigenceError`. It carries a stable `code`, a `message`, a `fix` and a `docs` link to the code's row on the errors page, plus the HTTP `status` and `request_id` when the API answered, and the CLI's `exit_code`.

Its subclasses name the cases you are likely to catch: `AuthError`, `NotFoundError`, `RateLimitError` (with `retry_after_ms`), `SpendCapError`, `PromigenceTimeoutError` and `InvalidArgumentError`. `EnvFailedError` means the environment failed, which is ours and never counted against your code. `CommandExitError` is a command that failed, and `CommandTimeoutError` one that ran past its `timeout_ms`.

Your failure or ours

Python

```
from promigence import CommandExitError, PromigenceError, Sandbox

try:
    with Sandbox.create(spend_cap=1) as sb:
        sb.commands.run("cd /work && pytest -q")
except CommandExitError as e:
    print("the tests failed with exit code", e.exit_code)
except PromigenceError as e:
    print(e.code, e.fix, e.docs)
```

[Every error code, what it means, and whether it was billed](https://www.promigence.ai/docs/errors)

## Async

`AsyncSandbox` and `AsyncPromigence` have the methods of `Sandbox` and `Promigence`. Await them.

Python

```
import asyncio
from promigence import AsyncSandbox

async def main():
    async with await AsyncSandbox.create(spend_cap=1) as sb:
        result = await sb.commands.run("echo hi")
        print(result.stdout)

asyncio.run(main())
```

The SDK calls the same routes as the CLI. The API reference lists every one, with its parameters and answers.

[API reference](https://www.promigence.ai/docs/reference/api)
