| name | upstash-box-py |
| description | Work with the upstash-box Python SDK for sandboxed cloud containers with AI agents, shell, filesystem, and git. Use when building with Upstash Box in Python, creating sandboxed environments, running AI agents in containers, or orchestrating parallel boxes. |
upstash-box Python SDK
Sandboxed cloud containers with built-in AI agents, shell, filesystem, and git.
Install & Setup
pip install upstash-box
Set UPSTASH_BOX_API_KEY env var or pass api_key to constructors.
The SDK ships both a synchronous Box (used in the examples below) and an
asynchronous AsyncBox (box = await AsyncBox.create(...), await box.agent.run(...)).
The async surface is identical with await and async for.
Box Lifecycle
import os
from upstash_box import Box, Agent, ClaudeCode, BoxApiKey
box = Box.create(
runtime="node",
agent={
"harness": Agent.CLAUDE_CODE,
"model": ClaudeCode.SONNET_4_5,
"api_key": BoxApiKey.UPSTASH_KEY,
},
git={
"token": os.environ["GITHUB_TOKEN"],
"user_name": "Bot",
"user_email": "bot@example.com",
},
env={"DATABASE_URL": "..."},
skills=["upstash/qstash-js"],
)
same = Box.get(box.id)
all_boxes = Box.list()
box.pause()
box.resume()
box.delete()
status = box.get_status()["status"]
Agent Runs
from pydantic import BaseModel
class Finding(BaseModel):
severity: str
file: str
issue: str
class Review(BaseModel):
verdict: str
findings: list[Finding]
run = box.agent.run(
prompt="Review the code for security issues",
response_schema=Review,
timeout=120_000,
max_retries=2,
on_tool_use=lambda tool: print(tool["name"], tool["input"]),
)
run.status
run.result
run.cost
stream = box.agent.stream(prompt="Build a REST API")
for chunk in stream:
print(chunk)
box.agent.run(
prompt="Run tests",
webhook={"url": "https://example.com/hook", "headers": {"Authorization": "Bearer ..."}},
)
Run Fields
Every run (agent, command, or code) returns a Run:
run = box.exec.command("npm test")
run.id
run.status
run.result
run.exit_code
run.cost
run.cancel()
logs = run.logs()
Shell Execution
run = box.exec.command("echo hello && ls -la")
run2 = box.exec.code(code="print(1 + 1)", lang="python", timeout=10_000)
stream = box.exec.stream("npm run build")
for chunk in stream:
...
Filesystem
box.files.write(path="/workspace/home/app.py", content="print('hi')")
content = box.files.read("/workspace/home/app.py")
entries = box.files.list("/workspace/home")
box.files.write(path="/workspace/home/image.png", content=base64_string, encoding="base64")
b64 = box.files.read("/workspace/home/image.png", encoding="base64")
box.files.upload([{"path": "./local/file.txt", "destination": "/workspace/home/file.txt"}])
box.files.download(folder="./output")
cd / Working Directory
The SDK tracks cwd client-side. All operations (exec, files, git, agent) run relative to it.
box.cwd
box.cd("my-repo")
box.cd("/workspace/home/other")
Git
box.git.clone(repo="github.com/org/repo", branch="main")
box.cd("repo")
status = box.git.status()
diff = box.git.diff()
result = box.git.commit(message="fix: resolve bug")
box.git.push(branch="feature/fix")
box.git.checkout(branch="release/v2")
pr = box.git.create_pr(title="Fix bug", body="...", base="main")
output = box.git.exec(args=["log", "--oneline", "-5"])
Snapshots
snap = box.snapshot(name="after-setup")
restored = Box.from_snapshot(snap.id)
snaps = box.list_snapshots()
box.delete_snapshot(snap.id)
EphemeralBox
Lightweight, short-lived boxes (max 3 days). No agent or git. Supports exec, files,
schedule, cd, network policy, and snapshots only.
from upstash_box import EphemeralBox
ebox = EphemeralBox.create(
runtime="python",
ttl=3600,
env={"API_KEY": "..."},
)
ebox.expires_at
ebox.exec.command("python -c 'print(1+1)'")
ebox.exec.code(code="print('hi')", lang="python")
ebox.files.write(path="/workspace/home/data.json", content="{}")
ebox.cd("subdir")
ebox.delete()
ebox2 = EphemeralBox.from_snapshot(snap.id, ttl=7200)
Public URLs
Expose box ports as public URLs with optional auth.
public_url = box.get_public_url(3000)
authed = box.get_public_url(3000, bearer_token=True)
basic = box.get_public_url(3000, basic_auth=True)
result = box.list_public_urls()
box.delete_public_url(3000)
MCP Servers
Attach MCP servers to the box agent.
box = Box.create(
agent={"harness": Agent.CLAUDE_CODE, "model": ClaudeCode.SONNET_4_5},
mcp_servers=[
{"name": "fs", "package": "@modelcontextprotocol/server-filesystem"},
{"name": "custom", "url": "https://mcp.example.com/sse", "headers": {"Authorization": "..."}},
],
)
Async client
The async client mirrors the sync API exactly — await the calls and use async for to stream.
import asyncio
from upstash_box import AsyncBox, Agent
async def main():
box = await AsyncBox.create(runtime="node", agent={"harness": Agent.CLAUDE_CODE})
run = await box.agent.run(prompt="Set up a Next.js project")
print(run.result)
stream = await box.agent.stream(prompt="Build a REST API")
async for chunk in stream:
print(chunk)
await box.delete()
asyncio.run(main())
asyncio.gather over many AsyncBox.create(...) / box.agent.run(...) calls runs boxes in parallel.
Gotchas
- Public API option keys are snake_case in Python:
api_key, user_name, network_policy, response_schema, max_retries, on_tool_use, and agent options like max_turns, max_budget_usd.
- Agent config takes
harness (not the deprecated provider/runner) — harness is required.
response_schema accepts a Pydantic BaseModel subclass (returns a typed instance) or a raw JSON-schema dict (returns a dict).
- Default working directory is
/workspace/home, not /home or /.
box.cd() is client-side tracking — it validates the path exists but doesn't change the box's shell cwd. All SDK methods use it automatically.
EphemeralBox does NOT support agent or git — use full Box for those.
run.exit_code is None for agent runs, only available for exec commands.
box.delete() is irreversible — snapshot first if you need the state.
- Git operations require
git.token in the box config for private repos and PRs.
Box.from_snapshot() creates a new box — it does not modify the original.
- Close the transport when done:
box.delete() closes it, or use with box: / box.close() (async with / await box.aclose() for AsyncBox).