Skip to main content

codecapsules-sandbox

Ephemeral isolated Linux environments via REST API. Create a sandbox, run code, delete it — in three lines.

Powered by Firecracker microVMs. Built for AI agents, code execution APIs, and any workload that needs strong isolation without managing infrastructure.

pip install codecapsules-sandbox

Quick Start

from codecapsules_sandbox import Sandbox

# Create → exec → auto-delete
with Sandbox.create(flavor="python-3.12") as sb:
    result = sb.exec("python --version")
    print(result.stdout)    # "Python 3.12.3\n"
    print(result.exit_code) # 0

Set your API key:

export CODECAPSULES_API_KEY=your_api_key

Or pass it directly:

sb = Sandbox.create(api_key="your_api_key")

Install

pip install codecapsules-sandbox
# or
uv add codecapsules-sandbox
# or
poetry add codecapsules-sandbox

Requires Python ≥ 3.9.


Usage

Create a sandbox

from codecapsules_sandbox import Sandbox

# Default: Python 3.12, 512MB RAM, 1 vCPU, 60-minute TTL
sb = Sandbox.create()

# With options
sb = Sandbox.create(
    flavor="node-20",    # python-3.12 | node-20 | browser | full
    memory=2048,         # MB
    cpu=2,               # vCPUs
    ttl=30,              # minutes
    metadata={"project": "my-agent"},
)

print(sb.id)     # "sb_01hx..."
print(sb.status) # "running"

# Always clean up
sb.delete()

Context manager (recommended)

with Sandbox.create(flavor="python-3.12") as sb:
    result = sb.exec("python --version")
    print(result.stdout)
# Sandbox is automatically deleted here — even if an exception was raised

Execute commands

with Sandbox.create() as sb:
    result = sb.exec("python --version")
    print(result.stdout)     # "Python 3.12.3\n"
    print(result.stderr)     # ""
    print(result.exit_code)  # 0
    print(result.duration_ms) # 82

    # With options
    result = sb.exec(
        "python /workspace/train.py",
        timeout=120,                          # seconds (default: 30)
        env={"EPOCHS": "10", "LR": "0.001"}, # environment variables
        stdin="input data",                   # stdin content
    )

    # Run multiple commands
    sb.exec("pip install numpy pandas")
    sb.exec('python -c "import numpy; print(numpy.__version__)"')

Stream long-running commands

with Sandbox.create() as sb:
    for chunk in sb.exec_stream("python train.py"):
        print(chunk, end="", flush=True)

Upload and download files

with Sandbox.create() as sb:
    # Upload
    sb.upload("/workspace/script.py", open("script.py", "rb").read())
    sb.upload("/workspace/config.json", '{"learning_rate": 0.001}')

    # Run
    result = sb.exec("python /workspace/script.py")

    # Download the output
    output = sb.download("/workspace/output.json")
    import json
    data = json.loads(output)

Fetch an existing sandbox

sb = Sandbox.get("sb_01hx...")
print(sb.status)  # 'running' | 'starting' | 'stopping' | 'stopped' | 'error'

Sandbox logs

with Sandbox.create() as sb:
    sb.exec("echo hello")
    entries = sb.logs()
    for e in entries:
        print(f"[{e.ts.isoformat()}] [{e.source}] {e.message}")

    # Since a timestamp
    from datetime import datetime, timedelta
    entries = sb.logs(since=datetime.utcnow() - timedelta(minutes=5), limit=50)

Resource metrics

with Sandbox.create() as sb:
    m = sb.metrics()
    print(f"CPU:  {m.cpu_percent}%")
    print(f"RAM:  {m.memory_used_mb}MB / {m.memory_limit_mb}MB")
    print(f"Disk: {m.disk_used_mb}MB / {m.disk_limit_mb}MB")

Async Usage

import asyncio
from codecapsules_sandbox import AsyncSandbox

async def main():
    # Async context manager
    async with AsyncSandbox.create(flavor="python-3.12") as sb:
        result = await sb.exec("python --version")
        print(result.stdout)

    # Upload and exec
    async with AsyncSandbox.create() as sb:
        await sb.upload("/workspace/script.py", open("script.py", "rb").read())
        result = await sb.exec("python /workspace/script.py")
        output = await sb.download("/workspace/output.json")

    # Stream output
    async with AsyncSandbox.create() as sb:
        async for chunk in sb.exec_stream("python train.py"):
            print(chunk, end="", flush=True)

asyncio.run(main())

Environments (Flavors)

Flavor Pre-installed
python-3.12 Python 3.12, pip, numpy, pandas, requests, git
node-20 Node.js 20, npm, yarn, git
browser Chromium, Playwright, xvfb, Python 3.12
full All of the above + jq, ffmpeg, ImageMagick

AI Agent Integration

Anthropic Claude

import anthropic
from codecapsules_sandbox import Sandbox

client = anthropic.Anthropic()

tools = [{
    "name": "run_python",
    "description": (
        "Execute Python code in an isolated sandbox and return the output. "
        "Use this for calculations, data processing, testing code, or any task "
        "requiring code execution."
    ),
    "input_schema": {
        "type": "object",
        "properties": {
            "code": {"type": "string", "description": "Python code to execute"},
        },
        "required": ["code"],
    },
}]


def run_python(code: str) -> str:
    with Sandbox.create() as sb:
        r = sb.exec(f"python -c {repr(code)}")
        return r.stdout + (f"\nSTDERR: {r.stderr}" if r.stderr else "")


def process_tool_call(tool_name: str, tool_input: dict) -> str:
    if tool_name == "run_python":
        return run_python(tool_input["code"])
    raise ValueError(f"Unknown tool: {tool_name}")

OpenAI

import openai
from codecapsules_sandbox import Sandbox

client = openai.OpenAI()

tools = [{
    "type": "function",
    "function": {
        "name": "execute_python",
        "description": "Run Python code in an isolated sandbox. Returns stdout and stderr.",
        "parameters": {
            "type": "object",
            "properties": {
                "code": {"type": "string"},
            },
            "required": ["code"],
        },
    },
}]


def execute_python(code: str) -> str:
    with Sandbox.create() as sb:
        r = sb.exec(f"python -c {repr(code)}")
        return f"stdout: {r.stdout}\nstderr: {r.stderr}\nexit_code: {r.exit_code}"

LangChain

from langchain.tools import tool
from codecapsules_sandbox import Sandbox


@tool
def execute_python(code: str) -> str:
    """Execute Python code in an isolated sandbox. Returns stdout, stderr, and exit code."""
    with Sandbox.create() as sb:
        result = sb.exec(f"python -c {repr(code)}")
        return f"stdout: {result.stdout}\nstderr: {result.stderr}\nexit_code: {result.exit_code}"

smolagents (HuggingFace)

from smolagents import tool
from codecapsules_sandbox import Sandbox


@tool
def python_interpreter(code: str) -> str:
    """Execute Python code in a secure isolated environment."""
    with Sandbox.create() as sb:
        result = sb.exec(f"python -c {repr(code)}")
        if result.exit_code != 0:
            return f"Error (exit {result.exit_code}):\n{result.stderr}"
        return result.stdout

Error Handling

from codecapsules_sandbox import (
    Sandbox,
    SandboxAuthError,
    SandboxNotFoundError,
    SandboxExecTimeoutError,
    SandboxQuotaError,
    SandboxRateLimitError,
    SandboxAPIError,
)

try:
    with Sandbox.create() as sb:
        result = sb.exec("python script.py", timeout=10)
except SandboxAuthError:
    print("Invalid API key — set CODECAPSULES_API_KEY")
except SandboxExecTimeoutError as e:
    print(f"Command timed out after {e.timeout_seconds}s")
except SandboxNotFoundError as e:
    print(f"Sandbox {e.sandbox_id} was deleted or expired")
except SandboxQuotaError:
    print("Too many concurrent sandboxes — delete one first")
except SandboxRateLimitError as e:
    print(f"Rate limited — retry after {e.retry_after_ms}ms")
except SandboxAPIError as e:
    print(f"API error {e.status}: {e}")

All exceptions inherit from SandboxError.


Configuration

Environment variable Description Default
CODECAPSULES_API_KEY API key — (required)
CODECAPSULES_SANDBOX_URL Override API base URL https://sandbox.codecapsules.io/v1

Alternatively, pass keyword arguments to Sandbox.create():

sb = Sandbox.create(
    api_key="your_api_key",
    base_url="https://...",
    timeout=60.0,       # seconds (default: 30)
    max_retries=3,      # on transient errors (default: 2)
)

API Reference

Sandbox.create(...) -> Sandbox

Creates a new sandbox and returns when it is running.

Parameter Type Default Description
flavor str 'python-3.12' Environment preset
memory int 512 Memory in MB (max 8192)
cpu int 1 vCPU count (max 4)
ttl int 60 Lifetime in minutes (max 480)
metadata dict Arbitrary key-value metadata

Sandbox.get(sandbox_id) -> Sandbox

Fetches an existing sandbox by ID.

sb.exec(command, *, timeout, env, stdin) -> ExecResult

Runs a shell command. Returns ExecResult with stdout, stderr, exit_code, and timing.

sb.exec_stream(command, *, timeout, env) -> Iterator[str]

Runs a command and yields output chunks. Use in a for loop.

sb.upload(sandbox_path, content) -> FileInfo

Uploads bytes or str content to a path inside the sandbox.

sb.download(sandbox_path) -> bytes

Downloads a file from the sandbox.

sb.logs(*, since, limit) -> list[LogEntry]

Returns system and exec log entries.

sb.metrics() -> SandboxMetrics

Returns live CPU, memory, and disk usage.

sb.refresh() -> Sandbox

Re-fetches sandbox info from the API (updates cached status).

sb.wait_until_running(*, timeout, interval) -> Sandbox

Polls until status is running. Raises SandboxNotReadyError on timeout.

sb.stop() -> None

Gracefully stops the sandbox.

sb.delete() -> None

Deletes the sandbox and releases all resources.


Types

from codecapsules_sandbox import (
    SandboxInfo,     # dataclass: id, status, flavor, cpu, memory, created_at, expires_at
    ExecResult,      # dataclass: exec_id, command, stdout, stderr, exit_code, duration_ms
    FileInfo,        # dataclass: path, size_bytes, created_at
    LogEntry,        # dataclass: ts, source, message, exec_id
    SandboxMetrics,  # dataclass: id, status, cpu_percent, memory_used_mb, ...
    SandboxStatus,   # Literal["starting", "running", "stopping", "stopped", "error"]
    SandboxFlavor,   # Literal["python-3.12", "node-20", "browser", "full"]
)

All types are fully typed — compatible with mypy, pyright, and Pylance.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

codecapsules_sandbox-0.1.0.tar.gz (18.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

codecapsules_sandbox-0.1.0-py3-none-any.whl (18.2 kB view details)

Uploaded Python 3

File details

Details for the file codecapsules_sandbox-0.1.0.tar.gz.

File metadata

  • Download URL: codecapsules_sandbox-0.1.0.tar.gz
  • Upload date:
  • Size: 18.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.6

File hashes

Hashes for codecapsules_sandbox-0.1.0.tar.gz
Algorithm Hash digest
SHA256 c625ab6667d1d7c2a7f5b49d0af508722586ac6aae0f480ea31bbd5db58e12e3
MD5 cc19aeb829bbacc46f49d7af779fbaed
BLAKE2b-256 3db7a037f3ceee3981b658bcda822df955493848baa65e25d08784c3c8aac74b

See more details on using hashes here.

File details

Details for the file codecapsules_sandbox-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for codecapsules_sandbox-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1dc76995efa5143cf1a00685dad446797429296b646e216255fc463b282a5a65
MD5 c0eeb6c05f2d0d20e081c8f08a3c2004
BLAKE2b-256 6a47aede334ced0f493a9aa1b4410596ef208ea27badce0c1e7afcdd55c837fc

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page