Run arbitrary commands over HTTP, in a machine you can throw away
Three endpoints: create an isolated virtual machine, execute shell commands in it, destroy it. Each execution returns stdout, stderr, exit code and wall-clock duration as JSON. No agent to install inside the sandbox, no image to build, no cluster to operate.
The contract
Authentication is a bearer token: Authorization: Bearer aas_sk_..., created from the
dashboard and scoped to your account. Everything is JSON over HTTPS, and the whole surface fits on
one screen:
POST /v1/sandboxes— create a machine. Body takessizeandtimeout_minutes. Returns when the sandbox is ready to accept commands.POST /v1/sandboxes/{id}/exec— run one shell command. Body takescommand,timeout_ms,cwdandenv.POST /v1/sandboxes/{id}/extend— push the expiry out when a job is taking longer than planned.DELETE /v1/sandboxes/{id}— destroy the machine and stop billing.GET /v1/usage— current balance and consumption.
Request and response
The execution response is intentionally boring: the four things a caller actually branches on, plus a flag telling you whether you are looking at all of the output.
POST /v1/sandboxes/sbx_8f2c19a4/exec HTTP/1.1
Host: sandbox-as-a-service.com
Authorization: Bearer aas_sk_3f9c2a7b1e4d8065af23c1
Content-Type: application/json
{
"command": "python3 analyse.py --input data.csv",
"timeout_ms": 120000,
"cwd": "/home/sandbox/project",
"env": {
"PYTHONUNBUFFERED": "1",
"DATASET_URL": "https://storage.example.com/signed/abc123"
}
}HTTP/1.1 200 OK
Content-Type: application/json
{
"stdout": "rows=10423 mean=41.9 p95=88.2\n",
"stderr": "",
"exit_code": 0,
"duration_ms": 3184,
"truncated": false
}
// A command that failed. Note the 200: the HTTP status describes the
// API call, exit_code describes the command. Branch on exit_code.
{
"stdout": "",
"stderr": "Traceback (most recent call last):\n File \"analyse.py\", line 3\n import pandas as pd\nModuleNotFoundError: No module named 'pandas'\n",
"exit_code": 1,
"duration_ms": 212,
"truncated": false
}POST /v1/sandboxes HTTP/1.1
Host: sandbox-as-a-service.com
Authorization: Bearer aas_sk_3f9c2a7b1e4d8065af23c1
Content-Type: application/json
Idempotency-Key: run-2026-08-22-4711
{
"size": "small",
"timeout_minutes": 15
}
// -> 201 Created
{
"id": "sbx_8f2c19a4",
"status": "running",
"size": "small",
"expires_at": "2026-08-22T14:31:00Z"
}
Reading the result correctly
The most common integration bug is conflating two different kinds of failure. An HTTP 4xx or 5xx means
the API call failed — bad token, unknown sandbox id, insufficient credit. A 200 with a non-zero
exit_code means the API call succeeded and the command inside the sandbox failed, which is
a completely normal outcome when you are running code you did not write. Retrying on the first is
sensible; retrying on the second usually just burns credit reproducing the same traceback.
duration_ms measures the command, not the request, so it excludes network time and is the
number to use if you are enforcing your own budget across a multi-step job. truncated is
the flag people forget: if you are feeding output back into a model, a silently cut traceback produces
confident nonsense, while a visible truncation marker lets you decide to fetch the tail instead.
Long-running work
Because exec is synchronous, anything longer than your HTTP client's patience needs the
background pattern: launch with nohup ... > build.log 2>&1 &, return
immediately, then poll with tail -n 40 build.log until a sentinel appears. Call
extend if the job outgrows the sandbox timeout — up to the 24 hours maximum.
Concurrency and limits
An account can hold 20 sandboxes at once by default. That is a deliberate guard rail rather than a capacity statement: it turns a leak in your orchestrator into an immediate, obvious error instead of an invoice. Current limits, including body size caps and how startup latency is measured on the warm and cold paths, are published in limits.
Pricing for API workloads
Billing follows sandbox runtime, not the number of API calls. A thousand exec calls against one sandbox cost exactly what that sandbox's minutes cost.
| Size | vCPU | Memory | Disk | Price |
|---|---|---|---|---|
small | 2 | 4 GB | 40 GB | $0.09/hour |
medium | 4 | 8 GB | 80 GB | $0.28/hour |
large | 8 | 16 GB | 160 GB | $0.55/hour |
Prepaid credit, charged for the time it runs. A five-minute job on the small size costs about $0.007. Full breakdown on the pricing page.
Questions
Is there a single endpoint that just runs code?
No, and that is deliberate. A one-shot "run this snippet" endpoint has to boot something, run one command and throw it away, which makes multi-step work (install, then run, then inspect) impossible or absurdly slow. Creating the sandbox once and issuing many exec calls against it costs you one extra HTTP round trip at the start and gives you a real working directory for everything after.
What is the difference between timeout_ms and timeout_minutes?
timeout_minutes is set at creation and bounds the life of the whole sandbox; when it expires the machine is destroyed and billing stops. timeout_ms is per exec call and bounds one command; when it expires the command is killed but the sandbox survives and you can issue another command. They are independent — a command timeout does not end the session, and the session timeout does not care what a command is doing.
How do I make retries safe?
Send an Idempotency-Key header on POST /v1/sandboxes. A retry with the same key returns the sandbox that was already created rather than creating a second one, which matters because create is the expensive call and network timeouts are exactly when you cannot tell whether it succeeded. exec is not idempotent by nature — the command decides that — so make the commands themselves re-runnable if you intend to retry them.
Are responses streamed?
No. exec is a synchronous request that returns once the command finishes or hits its timeout_ms, with the full stdout and stderr in the response body. For a long build, either raise timeout_ms and wait, or start the process in the background with nohup and poll a log file with follow-up exec calls.
What happens to output that is too large?
It is cut and the response sets truncated: true so you can tell the difference between "the command printed nothing" and "we dropped the rest". If you need the whole thing, redirect it to a file inside the sandbox and pull out only the part you care about with a second command.
Which languages can I run?
Any that you can install. The endpoint runs a shell command, not a language-specific payload, so the question is really what is on the machine. Python 3, Node.js 22, git, curl and build-essential ship pre-installed; Go, Rust, Java or anything else is an apt-get or a downloaded toolchain away.
Make your first exec call
Create an account, generate a key, and run a command in a machine that did not exist a minute ago.