Sandboxes
Sandboxes
An isolated machine that ends when you stop using it. Run your code on it. When it has been idle for a few minutes, or when its time limit passes, Maritime ends the machine and its disk. Each one is its own micro-VM on the sandbox engine, with its own kernel.
At a glance
- What it is
- An isolated Linux micro-VM on the sandbox engine, ended with its disk when it has been idle for a few minutes or at its time limit. Its id is
sbx-.... - Use it for
- Code a model wrote, test runs, analysis of a file that must not stay on disk.
- Use something else when
- Files must outlive the run: Agent box. Many at once: Swarm. A screen: Linux desktop.
- Start it
- New sandbox (
?type=ephemeral),POST /api/sandboxes/ephemeral,maritime sandboxes create, orephemeral.createEphemeralin the SDK. - Lifecycle
- Starts, runs your commands through the Sandboxes API, and the sandbox engine ends it with its disk after
idleMinuteswithout activity (default 5), or atexpiresAt, the time limit.POST /api/sandboxes/ephemeral/{id}restarts the time limit. - Price
- $0.0045 per vCPU-hour + $0.00225 per GB-hour while running, per second, at any size (small, 2 vCPU and 4 GB, is $0.018 an hour). Disk is included. With
onIdle: "pause", a paused sandbox costs $0.08 per GB-month of disk. - Limits
- Idle time 1 to 60 minutes, time limit 5 to 1,440 minutes. Presets small (default), medium, large and xlarge, or exact
vcpu1 to 16,memoryMib1,024 to 65,536 anddiskGib5 to 500. Counts toward awake at once while running. - Related
- Agent boxes, Swarms, REST API.
When to use it
- Running code a model wrote, where nothing should outlive the run.
- Tests for a coding agent, one clean machine per run.
- Analysis of an uploaded file that must not stay on disk.
If the machine should keep its files between runs, use an Agent box instead.
From the dashboard
Open New sandbox (/dashboard/new-sandbox?type=ephemeral), name it, and pick a preset size or set your own vCPU, memory and disk. Under How it ends, pick how long it may stay idle (1, 5, 15 or 60 minutes, default 5) and its time limit (1, 4 or 24 hours, default 24). Then click Create sandbox. For other values, use the API below.
From the API
One call starts a sandbox: POST /api/sandboxes/ephemeral. When it has been idle for idleMinutes, or at its time limit, the sandbox engine ends it, disk included. The key needs the sandboxes scope (or manage).
# start it: it ends, disk and all, after 5 idle minutes or 60 minutes from now
curl -X POST https://api.maritime.sh/api/sandboxes/ephemeral \
-H "Authorization: Bearer $MARITIME_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "job-42", "idleMinutes": 5, "timeLimitMinutes": 60, "size": "small"}'
# 201: the Sandbox object plus when it ends
# {"id": "sbx-k3m7q2ab", "name": "job-42", "state": "running", "size": "small",
# "resources": {"vcpu": 2, "memoryMib": 4096, "diskGib": 12},
# "dataPlaneUrl": "https://api-sbx-k3m7q2ab.sb.mtm.sh/v1", "secret": "...",
# ..., "expiresAt": "2026-10-26T18:00:00+00:00", "endsWhenIdleSeconds": 300}Every body field is optional. Three fields set how the sandbox ends:
idleMinutes: how long it may stay idle before it ends. 1 to 60, default 5.timeLimitMinutes: when it ends whatever its activity. 5 to 1,440, default 1,440 (24 hours).onIdle:kill, the default, orpause, which pauses an idle sandbox instead of ending it.
The other fields are those of POST /v1/sandboxes: name, from (a stock template such as base or python, a saved snapshot, or an OCI image; default base), size (default small), vcpu, memoryMib, diskGib, screen, network, endUser, forEndUser, env, metadata and failFast. The /v1 timer fields idleTimeoutSeconds, timeLimitSeconds and keepAwake are refused. A create with the name of a live sandbox returns that sandbox with status 200. Send an Idempotency-Key header to retry safely.
lifetimeMinutes is a deprecated alias of timeLimitMinutes, kept for earlier clients. A body with both returns 400 invalid_request.
The answer is the Sandbox object, the same as GET /v1/sandboxes/{id}, plus expiresAt, the time limit's deadline, and endsWhenIdleSeconds, the idle time that ends it. Its id (sbx-...) works with every /v1/sandboxes call and with the data plane. Refusals are the Sandboxes Error object (code, message, hint, docUrl, requestId).
To extend the time limit, call POST /api/sandboxes/ephemeral/{id} with {"timeLimitMinutes": 60} (5 to 1,440, default 1,440). The time limit restarts from that call. The idle timer does not change. A sandbox without a time limit that ends it returns 400 invalid_request. To end it now, call DELETE /api/sandboxes/ephemeral/{id}; calling it again is safe.
# restart the time limit: it now ends 30 minutes from now at the latest
curl -X POST https://api.maritime.sh/api/sandboxes/ephemeral/sbx-k3m7q2ab \
-H "Authorization: Bearer $MARITIME_API_KEY" \
-H "Content-Type: application/json" \
-d '{"timeLimitMinutes": 30}'
# end it now
curl -X DELETE https://api.maritime.sh/api/sandboxes/ephemeral/sbx-k3m7q2ab \
-H "Authorization: Bearer $MARITIME_API_KEY"List your live sandboxes, newest first. The list also has sandboxes you made with /v1/sandboxes or the E2B-compatible API, but not swarm sandboxes. expiresAt is null for a sandbox without a time limit, and while a sandbox is not running. endsWhenIdleSeconds is null when going idle pauses the sandbox instead of ending it.
curl -H "Authorization: Bearer $MARITIME_API_KEY" \
https://api.maritime.sh/api/sandboxes/ephemeral
# {"sandboxes": [{"id": "sbx-k3m7q2ab", ..., "expiresAt": "...", "endsWhenIdleSeconds": 300}], "nextCursor": null}Commands and files
A sandbox you start with these calls is a sandbox of the Sandboxes API, so commands and files run through it: the SDK's sandboxes client, maritime sandbox in the CLI, or the data plane at the sandbox's dataPlaneUrl.
import { Maritime } from 'maritime-sdk'
const maritime = new Maritime({ apiKey: process.env.MARITIME_API_KEY })
const box = await maritime.ephemeral.createEphemeral({ idleMinutes: 5, timeLimitMinutes: 30 })
const sbx = await maritime.sandboxes.connect(box)
const { stdout } = await sbx.run('uname -a')
console.log(stdout, 'ends at', box.expiresAt)
await maritime.ephemeral.killEphemeral(box.id) // or let it end when idleimport os
from maritime import Maritime
client = Maritime(api_key=os.environ["MARITIME_API_KEY"])
box = client.ephemeral.create_ephemeral(idle_minutes=5, time_limit_minutes=30)
sbx = client.sandboxes.connect(box["id"])
print(sbx.run("uname -a").stdout, "ends at", box["expiresAt"])
client.ephemeral.kill_ephemeral(box["id"]) # or let it end when idlemaritime sandboxes create --name job-42 --lifetime 30m
maritime sandbox exec sbx-k3m7q2ab -- uname -a
maritime sandbox cp ./data.csv sbx-k3m7q2ab:/tmp/data.csv
maritime sandboxes kill sbx-k3m7q2abIn the CLI, --lifetime sets the time limit. The idle time is the default, 2 minutes.
Over plain HTTP, send the command to {dataPlaneUrl}/commands with the sandbox's secret in X-Maritime-Token. The answer is the Command object, with exitCode, stdout and stderr, when the process exits.
curl -X POST "$DATA_PLANE_URL/commands" \
-H "X-Maritime-Token: $SANDBOX_SECRET" \
-H "Content-Type: application/json" \
-d '{"cmd": "uname -a"}'E2B-compatible API
Code written for the stock E2B SDKs (e2b and @e2b/code-interpreter) works against Maritime. Set E2B_DOMAIN to sb.mtm.sh and E2B_API_KEY to a Maritime key with the sandboxes scope. The SDK then calls https://api.sb.mtm.sh, which serves Maritime's /e2b routes; set E2B_API_URL only to point it somewhere else. A sandbox made this way is a Maritime sandbox, billed at the same rates.
export E2B_DOMAIN=sb.mtm.sh
export E2B_API_KEY=$MARITIME_API_KEY
# your existing E2B code runs unchanged, for example:
python -c 'from e2b import Sandbox; print(Sandbox.create().commands.run("uname -a").stdout)'How it ends
- The sandbox engine ends the sandbox, disk included, after
idleMinuteswithout activity or at the time limit, whichever comes first. No separate cleanup job is involved. - Activity is a data plane call, a foreground command whose caller still waits, an open terminal, or a request or connection through a port URL. A command started with
background: truedoes not count. If the sandbox only runs a background job, it ends afteridleMinutes. To keep it running, wait on the command in the foreground, or raiseidleMinutes. idleMinutesis 1 to 60, default 5.timeLimitMinutesis 5 to 1,440 (24 hours), default 1,440. A value outside its range returns 400invalid_request.- The time limit counts running time only, from the create or the last resume. Extending it restarts it from now.
- A sandbox the idle timer ended has
endedReason: "idle". One the time limit ended hasendedReason: "timeout". GET /v1/sandboxes/{id}/usagereturnsidleSeconds, the seconds since the last activity, with the CPU, memory and disk in use. See the API reference.- You can end it earlier with
DELETE /api/sandboxes/ephemeral/{id}ormaritime sandboxes kill <id>. - A wrong id, or another account's sandbox, returns 404
sandbox_not_found.
Sizes, limits and billing
| Size | vCPU | Memory | Disk | Per hour |
|---|---|---|---|---|
| small (default) | 2 | 4 GB | 12 GB | $0.018 |
| medium | 4 | 8 GB | 50 GB | $0.036 |
| large | 8 | 16 GB | 125 GB | $0.072 |
| xlarge | 16 | 32 GB | 250 GB | $0.144 |
The sizes are presets. To set exact resources, send vcpu (1 to 16), memoryMib (1,024 to 65,536, in whole GiB) and diskGib (5 to 500), alone or on top of a size. Each exact field overrides the preset's value. The Sandbox object shows them in resources, and its size is custom when they match no preset. A sandbox with 1 vCPU and 2 GiB of memory costs $0.009 per hour.
- It is billed per second while running, with no minimum: $0.0045 per vCPU-hour plus $0.00225 per GB-hour of memory. Disk is included. It draws from your plan's usage credit and counts toward the free trial's $20. See Billing & plans.
- While it runs, it counts toward your plan's machines awake at once.
- Without a plan or the free trial, a create returns 402
plan_required, whosecheckoutUrlis the billing page. At the awake limit it returns 429concurrency_limit.
Every number is in Limits & errors.