Python SDK
A dependency-free Python client for the whole API — sandboxes, execution, files, preview URLs and account. It installs straight from this site, so there is no registry account to create and nothing to keep in sync but the URL.
Install
The client uses only the Python standard library, so installing it pulls in no dependencies. It supports Python 3.8 and newer.
pip install https://sandbox-as-a-service.com/sdk/sandbox-as-a-service-python.tar.gz
That URL always serves the current release, so the same command upgrades an existing install. The
versioned archive is published alongside it as
sandbox_as_a_service-0.1.0.tar.gz if you pin by name. Prefer a virtual environment or a
container if the interpreter is externally managed (see
running commands for why a bare pip install is refused on
some systems).
Quickstart
Set AAS_API_KEY from Dashboard → API keys and the
client picks it up. Creating a sandbox blocks until the machine is ready, and using it as a context
manager destroys it on the way out — billing stops there.
from sandbox_as_a_service import Client
client = Client() # reads AAS_API_KEY from the environment
with client.create_sandbox(size="small", timeout_minutes=15) as sandbox:
result = sandbox.exec('python3 -c "print(6 * 7)"')
print(result.stdout, end="") # 42
sandbox.write_file("/workspace/app.py", "print('hello from the sandbox')")
print(sandbox.read_file("/workspace/app.py").content)
for entry in sandbox.list_files("/workspace").entries:
print(entry["name"], entry["type"], entry["size_bytes"])
# the sandbox is destroyed here
result.ok is True when the exit code is zero, and
result.check() raises ExecutionFailed when it is not — useful when a failed
command should stop the script:
result = sandbox.exec("python3 -m pytest -q", timeout_ms=300000, cwd="/workspace")
result.check() # raises ExecutionFailed on a non-zero exit code
A sandbox can also be created, kept and destroyed explicitly, which is what you want when one machine serves several steps:
sandbox = client.create_sandbox(size="small", name="build-runner", timeout_minutes=15)
try:
sandbox.exec("git clone https://example.com/repo.git /workspace/repo")
sandbox.extend(additional_minutes=30)
finally:
sandbox.destroy()
Pass idempotency_key when a request might be retried — a dropped response then
returns the existing sandbox instead of creating a second one:
client.create_sandbox(idempotency_key="job-42-attempt-1"). If it is omitted, the client
generates a random one per call.
What the client covers
| Call | What it does |
|---|---|
client.create_sandbox(size, name, timeout_minutes) | Creates a sandbox and returns it ready to use. Idempotency key optional. |
client.get_sandbox(id) | Fetches one sandbox by id. |
client.list_sandboxes(limit, starting_after, include_deleted) | One page of sandboxes, newest first. |
client.iter_sandboxes() | Iterates every sandbox across pages. |
client.get_account() | Balance, limits and current pricing. |
client.get_usage(days=30) | Usage and spend over a window. |
sandbox.refresh() | Re-reads status, expiry and resources. |
sandbox.extend(additional_minutes) | Pushes the expiry back, within the maximum lifetime. |
sandbox.exec(command, timeout_ms, cwd, env) | Runs a command and returns stdout, stderr, exit code and duration. |
sandbox.get_execution(id) | Fetches an execution by id. |
sandbox.write_file(path, content, encoding) | Writes text or base64 content into the sandbox. |
sandbox.read_file(path, encoding) | Reads a file back, as text or base64. |
sandbox.list_files(path, recursive) | Lists a directory with names, types and sizes. |
sandbox.delete_file(path, recursive) | Deletes a file or directory. |
sandbox.expose_port(port) / list_ports() / close_port(port) | Creates and manages preview URLs. |
sandbox.destroy() | Destroys the sandbox. Billing stops immediately. |
Errors
Non-2xx responses raise a specific exception, so a caller can react to the reason rather than
parse a status code. Every SandboxApiError carries status,
type, request_id and the decoded body; quote the request id in a support
request.
from sandbox_as_a_service import Client, RateLimitError, NotFoundError
client = Client()
try:
sandbox = client.get_sandbox("sbx_does_not_exist")
except NotFoundError:
print("gone")
except RateLimitError as err:
print("slow down, retry after", err.retry_after, "seconds")
| Exception | Raised when |
|---|---|
AuthenticationError | The key is missing, malformed or revoked (401). |
PermissionError_ | The key is valid but not allowed to do this (403). |
NotFoundError | No such sandbox, file or execution (404). |
InvalidRequestError | The request body or parameters were rejected (400/422). |
ConflictError | The sandbox is in a state that forbids the operation (409). |
PaymentRequiredError | The account has no credit left (402). |
RateLimitError | A rate limit was hit (429); retry_after is set when the header is present. |
ServiceUnavailableError | A transient server or upstream failure (5xx). |
SandboxConnectionError | The request never reached the API — DNS, TLS or timeout. |
Notes
Client()readsAAS_API_KEY; passapi_key=orbase_url=to override. A bare host is normalized to its/v1API root.- The default client timeout is 600 seconds, generous because create blocks until the machine
is ready.
execraises its own timeout above the command'stimeout_msso a long command is not cut off by the client first. - Release with the context managers or by calling
destroy()— an abandoned sandbox keeps billing until it expires. - The same operations are available over REST and as MCP tools; the SDK is a thin, typed wrapper over them, and the OpenAPI document can generate a client in another language.
Try it with your own key
Sign in, create a key, then pip install the client and run your first sandbox.