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.

Metadata

Release files for codecapsules-sandbox 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for codecapsules-sandbox 0.1.0
File Size Uploaded Provenance Actions
codecapsules_sandbox-0.1.0.tar.gz 18.3 kB Without provenance Details for codecapsules_sandbox-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for codecapsules-sandbox 0.1.0
File Interpreter ABI Platform Actions
codecapsules_sandbox-0.1.0-py3-none-any.whl Python 3 none any Details for codecapsules_sandbox-0.1.0-py3-none-any.whl

Total release size: 36.4 kB

Release files / codecapsules_sandbox-0.1.0.tar.gz

Download URL codecapsules_sandbox-0.1.0.tar.gz
Size 18.3 kB
Tags Source
SHA-256 checksum
How to use checksums
c625ab6667d1d7c2a7f5b49d0af508722586ac6aae0f480ea31bbd5db58e12e3
BLAKE2b-256 checksum
How to use checksums
3db7a037f3ceee3981b658bcda822df955493848baa65e25d08784c3c8aac74b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.6

Release files / codecapsules_sandbox-0.1.0-py3-none-any.whl

Download URL codecapsules_sandbox-0.1.0-py3-none-any.whl
Size 18.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1dc76995efa5143cf1a00685dad446797429296b646e216255fc463b282a5a65
BLAKE2b-256 checksum
How to use checksums
6a47aede334ced0f493a9aa1b4410596ef208ea27badce0c1e7afcdd55c837fc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.6

Security for codecapsules-sandbox 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

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