Sandboxes
Create a sandbox
One request boots a Firecracker microVM from a prebuilt snapshot and returns when the agent inside it is ready to run code.
POST /session
Behind the call, we pick a machine with room for the size you asked for, reserve its CPU, memory, disk and a network slot, and restore the VM from a snapshot of that size — inside its own cgroup and network namespace, with its own copy-on-write disk.
curl -sS -X POST "$SPAWN_API_URL/session" \
-H "Authorization: Bearer $SPAWN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "dependency-check",
"os": "alpine",
"template": "base",
"size": "nano",
"metadata": {
"project": "docs"
},
"env": {
"APP_ENV": "dev"
},
"network": {
"internet": true
},
"idle_timeout_s": 300,
"max_lifetime_s": 3600
}' {
"status": "success",
"request_id": "4e245aae-1694-4dd5-b944-7180bf480fe8",
"session": {
"session_id": "3ecb0221-faf1-4976-a8ae-6fd6262de59a",
"state": "active",
"os": "alpine",
"template": "base",
"billing_model": "payg",
"created_at": "2026-08-17T11:00:00Z",
"last_used": "2026-08-17T11:00:00Z",
"expires_at": "2026-08-17T11:05:00Z"
},
"limits": {
"max_sessions": 100,
"active_sessions": 0,
"max_execution_ms": 30000,
"idle_timeout_ms": 300000
},
"tenant": {
"tenant_id": "<user-id>",
"billing_model": "payg"
}
}Request body
Every field is optional; an empty body creates a nano sandbox with defaults.
namestring- A label shown in the dashboard. Defaults to "sandbox".
osstring- The OS image. Defaults to "alpine" — see OS images and templates below.
templatestring- The software on top of the OS: "base" (default) or "agents" (Claude Code and Codex).
sizestring- nano, small, medium or large — see Sizes below. Each template runs only at the sizes it lists. Defaults to nano.
resourcesobject- An exact shape instead of a size name: { vcpus, memory_mb, disk_gb }. It must match one of the sizes exactly, and takes precedence over size.
imagestring- Legacy: an image ID (alpine, agents), used only when os and template are unset. A value that disagrees with them is rejected.
envobject of strings- Environment variables set for code and commands you run in the sandbox.
metadataobject- Your own labels, stored with the sandbox and returned when you list it.
network.internetboolean- Whether the sandbox can reach the internet. Defaults to true; set false for untrusted code that shouldn’t make outbound calls.
idle_timeout_sinteger- Seconds without activity before the sandbox pauses itself. Defaults to 300 (5 minutes). Capped by the maximum lifetime.
max_lifetime_sinteger- Seconds after creation when the sandbox is destroyed, whatever it’s doing. Capped by your account’s policy.
OS images and templates
A sandbox is an OS image, a template of software on top of it, and a size. Each template runs at the sizes listed for it.
| OS | Template | What’s inside | Sizes |
|---|---|---|---|
alpine | base | Alpine Linux 3.22, Python 3.12, Node 22, git | nano, small, medium |
alpine | agents | Base plus Claude Code and Codex | large |
Ubuntu and Debian images are coming. The current list is always available from the catalog:
GET /v1/catalog
curl -sS "$SPAWN_API_URL/v1/catalog" \
-H "Authorization: Bearer $SPAWN_API_KEY" {
"os": [{
"id": "alpine",
"name": "Alpine Linux 3.22",
"templates": [
{ "id": "base", "name": "Base", "description": "Python 3.12, Node 22, git",
"sizes": ["nano", "small", "medium"], "image": "alpine" },
{ "id": "agents", "name": "Agents", "description": "Base plus Claude Code and Codex",
"sizes": ["large"], "image": "agents" }
]
}],
"sizes": [
{ "name": "nano", "vcpus": 1, "memory_mb": 256, "disk_gb": 1 },
{ "name": "small", "vcpus": 2, "memory_mb": 512, "disk_gb": 10 },
{ "name": "medium", "vcpus": 2, "memory_mb": 1024, "disk_gb": 20 },
{ "name": "large", "vcpus": 2, "memory_mb": 4096, "disk_gb": 20 }
]
}Sizes
Only these shapes are accepted; anything else is rejected rather than rounded.
| Size | vCPU | Memory | Disk |
|---|---|---|---|
nano | 1 | 256 MiB | 1 GiB |
small | 2 | 512 MiB | 10 GiB |
medium | 2 | 1024 MiB | 20 GiB |
large | 2 | 4096 MiB | 20 GiB |
Response
session.session_idstring- The sandbox ID. Every other call takes it.
session.statestring- active once the call returns.
session.osstring- The OS image the sandbox runs.
session.templatestring- The template the sandbox was created from.
session.expires_attimestamp- When the sandbox will pause if it stays idle. Each run or command pushes it back.
limits.max_execution_msinteger- The longest a single run or command may take.
limits.idle_timeout_msinteger- The idle timeout that was applied.
limits.max_sessionsinteger- Your account’s sandbox limit.
Errors
| Status | Code | Meaning |
|---|---|---|
400 | invalid_resources | The size or resource shape isn’t one of the supported ones. |
400 | invalid_template | The OS or template doesn’t exist. The message lists the available ones. |
400 | size_not_available | The template doesn’t run at that size. The message lists the sizes it does. |
402 | — | The account has no balance left. |
403 | sessions_not_allowed | Your account’s policy doesn’t allow sandboxes. |
503 | no_capacity | No machine has room for this size right now. Retry with backoff. |
503 | scheduler_busy | Placement stayed contended after retries. Retry with backoff. |
500 | provision_failed | The VM failed to boot. |
List your sandboxes
GET /session
Returns every sandbox on your account that hasn’t been destroyed.
curl -sS "$SPAWN_API_URL/session" \
-H "Authorization: Bearer $SPAWN_API_KEY" {
"sandboxes": [
{
"id": "3ecb0221-faf1-4976-a8ae-6fd6262de59a",
"name": "dependency-check",
"state": "active",
"billing_model": "payg",
"vcpus": 1,
"memory_mb": 256,
"disk_gb": 1,
"idle_timeout_ms": 300000,
"metadata": {"project": "docs"},
"created_at": "2026-08-17T11:00:00Z"
}
]
}Get one sandbox
GET /session/{sandbox_id}
Returns its state and limits, plus run statistics. An ID that doesn’t exist and one that belongs
to another account both return 404.
curl -sS "$SPAWN_API_URL/session/$SANDBOX_ID" \
-H "Authorization: Bearer $SPAWN_API_KEY" {
"status": "success",
"session": {
"session_id": "3ecb0221-faf1-4976-a8ae-6fd6262de59a",
"state": "active",
"last_used": "2026-08-17T11:01:00Z",
"expires_at": "2026-08-17T11:03:00Z",
...
},
"stats": {
"run_count": 2,
"total_execution_ms": 138.4,
"last_exit_code": 0
},
...
}