Python SDK reference
The orkestr Python package is the official client for the sandbox API. Sync only in v1 (an async AsyncSandbox is planned). Requires Python 3.10 or newer.
Install
pip install orkestrAuthentication
Every request is authenticated with a Bearer API token. The SDK looks for it in three places, in order:
- The
api_key=kwarg onSandbox.create()/Sandbox.get()/ etc. - The
client=kwarg (a pre-builtOrkestrClient) - The
ORKESTR_API_KEYenvironment variable
import os
from orkestr import Sandbox
# 1) Implicit: SDK reads ORKESTR_API_KEY from env
sbx = Sandbox.create(template="python-3.12")
# 2) Explicit: per-call api_key
sbx = Sandbox.create(template="python-3.12", api_key="ork_...")
# 3) Reusable client (good for long-running agents)
from orkestr import OrkestrClient
client = OrkestrClient(api_key="ork_...", base_url="https://api.orkestr.eu")
sbx = Sandbox.create(template="python-3.12", client=client)sandboxes:read and sandboxes:write for agent runtimes. A scoped token cannot reach the rest of the orkestr platform if it leaks.Sandbox class
Sandbox is the main public class. Static constructors create / fetch / list / resume sandboxes; instance methods drive exec, files, and lifecycle.
Sandbox.create
from orkestr import Sandbox
sbx = Sandbox.create(
template="python-3.12",
cpu=1.0,
memory_mb=2048,
network="off",
timeout_seconds=600,
env={"OPENAI_API_KEY": "sk-..."},
metadata={"agent_run": "r_42"},
region="fsn1",
)
print(sbx.id, sbx.status)| Parameter | Type | Description |
|---|---|---|
| templaterequired | Template | One of python-3.12, python-3.12-bare, node-22, ubuntu-24.04. |
| cpu | float | vCPU allocation. Default 1.0. Plan-capped (free 0.5, pro up to 2, team up to 4). |
| memory_mb | int | RAM in MB. Default 2048. Plan-capped (free 512, pro up to 4096, team up to 8192). |
| network | NetworkMode | off (default, no egress), restricted (curated allowlist), open (full egress, requires verified payment). |
| timeout_seconds | int | Sandbox auto-terminates after this many seconds. Default 600. Max 3600 on Pro and Team. |
| env | dict[str, str] | Environment variables exposed to processes in the sandbox. In-memory only, never persisted past terminate. |
| metadata | dict[str, str] | Caller-defined tags echoed back on every response. Max 16 keys, 256 chars per value. |
| region | str | None | Region preference. fsn1 (Falkenstein, DE) or hel1 (Helsinki, FI). Omit to let the control plane pick. |
Sandbox.get and Sandbox.list
# Reattach from another process
sbx = Sandbox.get("sbx_01HXYZ...")
# List your sandboxes
for sbx in Sandbox.list(status="running"):
print(sbx.id, sbx.template, sbx.created_at)Sandbox.get reattaches to an existing sandbox by id - useful across worker processes. Sandbox.list returns the caller's sandboxes; filter by status server-side, bounded by limit (default 50, max 200).
sbx.exec
Run a shell command and return the buffered result. Raises ExecTimeout if the command exceeds timeout_seconds; the sandbox itself stays alive so you can run another command.
result = sbx.exec("python /workspace/main.py", timeout_seconds=60)
result.stdout # str
result.stderr # str
result.exit_code # int
result.duration_ms # int| Parameter | Type | Description |
|---|---|---|
| commandrequired | str | Shell command to run inside the sandbox. |
| cwd | str | Working directory. Default /workspace. |
| env | dict[str, str] | Per-call env overrides on top of the sandbox env. |
| timeout_seconds | int | Per-command timeout, default 60. Capped at 3600. Process gets SIGTERM, then SIGKILL after 2s. |
sbx.exec_stream
Stream output as it arrives. Returns an iterator of ExecChunk objects; iteration ends with a final chunk where is_final is true. Iterate to completion - breaking early leaves the in-VM process running until its own timeout fires.
for chunk in sbx.exec_stream("python long_task.py"):
if chunk.stream == "stdout":
print(chunk.data, end="", flush=True)
elif chunk.stream == "stderr":
print(chunk.data, end="", flush=True, file=sys.stderr)
if chunk.is_final:
print(f"exit={chunk.exit_code} duration={chunk.duration_ms}ms")Files namespace
sbx.files is the file operations namespace. Writes go to writable roots (/workspace and /tmp); reads and directory listings work anywhere readable inside the sandbox.
# Text I/O
sbx.files.write("/workspace/script.py", "print('hello')")
content = sbx.files.read("/workspace/output.txt")
# Binary I/O
sbx.files.write_bytes("/workspace/blob.bin", b"\x00\x01\x02")
raw = sbx.files.read_bytes("/workspace/blob.bin")
# Directory listing
for entry in sbx.files.list("/workspace"):
print(entry.name, entry.is_dir, entry.size, entry.mode)
# Remove
sbx.files.delete("/workspace/output.txt")sbx.pause and Sandbox.resume
pause() snapshots the sandbox and stops the compute meter. Returns the sandbox id (same as sbx.id) - persist it wherever your agent keeps state and pass to Sandbox.resume() when you want to come back.
# Pause: returns the sandbox id, stops compute meter
sandbox_id = sbx.pause()
# ... later ...
sbx = Sandbox.resume(sandbox_id)sbx.terminate (and context manager)
terminate() stops the sandbox and frees the resources. Idempotent. The recommended pattern is the context manager, which always terminates on exit:
with Sandbox.create(template="python-3.12") as sbx:
result = sbx.exec("python -c 'print(2+2)'")
print(result.stdout)
# sbx.terminate() is called automatically on exit (including on exceptions)Result types
All result types are frozen dataclasses. No magic accessors - attributes are exactly what's documented here and what the API sends. ExecResult has stdout, stderr, exit_code, duration_ms. ExecChunk has stream, data, exit_code (only on final), duration_ms (only on final), plus is_final for convenience. FileEntry has name, is_dir, size, mode, modified_at.
Errors
Every exception the SDK raises inherits from OrkestrError. Catch the base for anything the SDK throws; catch a specific subclass to handle one failure mode.
from orkestr import (
OrkestrError,
AuthError,
SandboxNotFound,
ExecTimeout,
SnapshotCapReached,
)
try:
sbx = Sandbox.create(template="python-3.12")
result = sbx.exec("sleep 9999", timeout_seconds=5)
except ExecTimeout:
print("Command exceeded timeout - the sandbox is still running.")
except AuthError as e:
print(f"Auth failed ({e.status_code}): {e.detail}")
except OrkestrError as e:
print(f"Something else broke: {e}, request_id={e.request_id}")| Parameter | Type | Description |
|---|---|---|
| AuthError | OrkestrError | API key missing, invalid, expired, or scope insufficient. 401 / 403. |
| RateLimitError | OrkestrError | Plan rate limit hit. retry_after attribute carries the recommended wait. |
| PlanLimitError | OrkestrError | Plan tier limit hit (concurrent sandboxes, monthly CPU-seconds). |
| SnapshotCapReached | PlanLimitError | pause() blocked because the snapshot retention cap is full. |
| SandboxNotFound | OrkestrError | Sandbox id doesn't exist or doesn't belong to the caller. 404. |
| SandboxNotReady | OrkestrError | Operation called on a sandbox in the wrong state (e.g. exec on paused). 409. |
| ExecTimeout | OrkestrError | exec exceeded timeout_seconds. Sandbox stays alive. |
| ExecKilled | OrkestrError | Exec process exited via a signal. signal attribute carries the signal number. |
| NetworkPolicyError | OrkestrError | In-sandbox code tried to reach a host blocked by the network policy. |
Advanced: OrkestrClient
The default client is built lazily from the env var on first use. For long-running agents you may want to construct one explicitly and pass it to every Sandbox.* call - this lets you change the base URL (e.g. for testing against a staging deployment), configure timeouts, or swap the underlying HTTP client. See REST API reference for raw wire format if you want to bypass the SDK entirely.