Ephemeral sandboxes

Ephemeral Sandboxes

An isolated machine with a lifetime. Run your code on it, and when the lifetime ends 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 with a lifetime from 5 minutes to 24 hours, ended with its disk when the lifetime ends. 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: Persistent Sandbox. Many at once: Swarm. A screen: Linux desktop.
Start it
New sandbox (?type=ephemeral), POST /api/sandboxes/ephemeral, maritime sandboxes ephemeral create, or ephemeral.createEphemeral in the SDK.
Lifecycle
Starts, runs your commands through the Sandboxes API, and the sandbox engine ends it with its disk at expiresAt. POST /api/sandboxes/ephemeral/{id} extends it.
Price
$0.0045 per vCPU-hour + $0.00225 per GB-hour while running, per second (small, 2 vCPU and 4 GB, is $0.018 an hour). It never sleeps, so there is no disk line.
Limits
Lifetime 5 to 1,440 minutes. Sizes small (default), medium, large and xlarge, up to 16 vCPU and 32 GB. Counts toward awake at once while running.

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 a Persistent Sandbox instead.

From the dashboard

Open New sandbox (/dashboard/new-sandbox?type=ephemeral), name it, pick a size and click Start. It gets the default lifetime, 60 minutes; for another lifetime, use the API below.

From the API

One call starts a sandbox with a lifetime: POST /api/sandboxes/ephemeral. When the lifetime ends, the sandbox engine ends it, disk included. The key needs the sandboxes scope (or manage).

# start it: ended, disk and all, 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", "lifetimeMinutes": 60, "size": "small"}'
# 201: the Sandbox object plus its expiry
# {"id": "sbx-k3m7q2ab", "name": "job-42", "state": "running", "size": "small",
#  "dataPlaneUrl": "https://api-sbx-k3m7q2ab.sbx.maritime.sh/v1", "secret": "...",
#  ..., "expiresAt": "2026-10-26T18:00:00+00:00"}

Every body field is optional. lifetimeMinutes is 5 to 1,440, default 60. 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), screen, network, endUser, forEndUser, env, metadata and failFast. The idle timer fields idleTimeoutSeconds 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.

The answer is the Sandbox object, the same as GET /v1/sandboxes/{id}, plus expiresAt. 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 a sandbox, call POST /api/sandboxes/ephemeral/{id} with {"lifetimeMinutes": 60}. The lifetime restarts from that call. To end it now, call DELETE /api/sandboxes/ephemeral/{id}; calling it again is safe.

# 30 more minutes from now
curl -X POST https://api.maritime.sh/api/sandboxes/ephemeral/sbx-k3m7q2ab \
  -H "Authorization: Bearer $MARITIME_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"lifetimeMinutes": 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 ephemeral 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 that never ends by itself.

curl -H "Authorization: Bearer $MARITIME_API_KEY" \
  https://api.maritime.sh/api/sandboxes/ephemeral
# {"sandboxes": [{"id": "sbx-k3m7q2ab", ..., "expiresAt": "..."}], "nextCursor": null}

Commands and files

An ephemeral sandbox 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.

job.ts
import { Maritime } from 'maritime-sdk'

const maritime = new Maritime({ apiKey: process.env.MARITIME_API_KEY })

const box = await maritime.ephemeral.createEphemeral({ lifetimeMinutes: 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 the lifetime end
job.py
import os

from maritime import Maritime

client = Maritime(api_key=os.environ["MARITIME_API_KEY"])

box = client.ephemeral.create_ephemeral(lifetime_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 the lifetime end
terminal
maritime sandboxes ephemeral 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 ephemeral kill sbx-k3m7q2ab

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 sbx.maritime.sh and E2B_API_KEY to a Maritime key with the sandboxes scope. The SDK then calls https://api.sbx.maritime.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=sbx.maritime.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)'

Lifetime

  • lifetimeMinutes is 5 to 1,440 (24 hours), default 60. Outside that range the call returns 400 invalid_request.
  • The lifetime counts from the call. Extending it restarts it from now.
  • The sandbox engine ends the sandbox, disk included, when the lifetime is over. No separate cleanup job is involved.
  • You can end it earlier with DELETE /api/sandboxes/ephemeral/{id} or maritime sandboxes ephemeral kill <id>.
  • A wrong id, or another account's sandbox, returns 404 sandbox_not_found.

Sizes, limits and billing

SizevCPUMemoryDiskPer hour
small (default)24 GB12 GB$0.018
medium48 GB50 GB$0.036
large816 GB125 GB$0.072
xlarge1632 GB250 GB$0.144
  • It is billed per second while running, with no minimum: $0.0045 per vCPU-hour plus $0.00225 per GB-hour of memory, before your plan's discount. 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, whose checkoutUrl is the billing page. At the awake limit it returns 429 concurrency_limit.

Every number is in Limits & errors.