swarms.md

Swarms

Sandboxes

A swarm is many sandboxes running one job in parallel. You send one command and a count (or one input per sandbox); each sandbox boots, runs the command, reports its exit code and output, and is ended.

Swarms are invite-only for now. Press Request access on the Swarms page of the dashboard; we answer by email within 24 hours. Until access is granted, starting a run returns 403 swarms_access_required, and GET /api/swarms/access tells you whether you have it.

At a glance

What it is One shell command on many sandboxes at once, each with its own input, with every result collected.
Use it for Evals, reinforcement learning rollouts, batch jobs where each item needs its own machine.
Use something else when One machine: Sandbox. State that must stay: Agent box.
Start it Invite-only for now: request access on the Swarms page of the dashboard. Then POST /api/swarms, maritime swarms run, maritime.swarms.start in the SDK, start_swarm over MCP, or the Swarms page.
Lifecycle Each sandbox goes queued, booting, running, then succeeded or failed, and is ended after its job. The run completes when every sandbox is done.
Price Each sandbox: $0.0055 per vCPU-hour + $0.00275 per GB-hour while running, per second (small, 2 vCPU and 4 GB, is $0.022 an hour). Swarm sandboxes never sleep, so there is no disk line.
Limits Up to 100 sandboxes per run on Starter and the free trial, 1,000 on Growth, 5,000 on Scale, 10,000 on Dedicated. Running at once follows the awake limit. 10 seconds to 6 hours per sandbox; results up to 64 KB.
Related Sandboxes, REST API: Swarms, SDK.

When to use it

How a run works

  1. You start a run. Every sandbox is queued. As many start at once as the run's concurrency allows, which is never more than your plan's machines awake at once.
  2. Each sandbox is a fresh Linux micro-VM on the sandbox engine (the base template unless you pick another). Once it has booted, your files are written and the command runs with MARITIME_SWARM_INDEX (0, 1, 2, ...), MARITIME_SWARM_COUNT and MARITIME_SWARM_ID set.
  3. When the command exits, the sandbox records its exit code, the last 16 KB of its output and errors, and the result file if you asked for one. Then it is ended, disk included. Exit code 0 is succeeded; anything else, a timeout or a boot failure is failed, with the reason.
  4. The run is completed when every sandbox has ended.

On the dashboard, the Swarms page does the same: write the job, pick how many sandboxes and their size, and press Start. The run's page shows every sandbox as a square coloured by its state.

Start a swarm

start.sh:

curl -X POST https://api.maritime.sh/api/swarms \
  -H "Authorization: Bearer $MARITIME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: evals-2026-10-26" \
  -d '{
    "name": "evals",
    "command": "python eval.py --case $MARITIME_SWARM_INDEX",
    "count": 50,
    "size": { "vcpus": 2, "memoryGb": 4 },
    "timeoutSeconds": 600
  }'

The body:

Send an Idempotency-Key header (or idempotencyKey in the body) to retry safely: the same key and body return the run it started the first time, with status 200. The same key with a different body returns 409 idempotency_conflict.

One input per sandbox

Give inputs instead of count to vary each sandbox. Each input may add arguments to the command (shell-quoted), set variables, and write files before the command runs. The count is the number of inputs.

body.json:

{
  "command": "python rollout.py",
  "inputs": [
    { "args": ["--seed", "1"] },
    { "args": ["--seed", "2"], "env": { "TEMPERATURE": "0.7" } },
    { "files": { "/work/task.json": "{\"id\": 3}" } }
  ],
  "resultPath": "/work/result.json"
}

Follow a run

GET /api/swarms/{id} returns the run and every sandbox: its status (queued, booting, running, succeeded, failed or cancelled), exit code, output tails, result, error and sandboxId. The sandboxId (sbx-...) is kept after the job ends and stays readable with GET /v1/sandboxes/{id} for 7 days. GET /api/swarms lists your runs, newest first.

run.json:

{
  "id": "4f1c...",
  "name": "evals",
  "status": "running",
  "count": 50,
  "size": { "name": "small", "vcpus": 2, "memoryGb": 4 },
  "concurrency": 5,
  "counts": { "queued": 30, "booting": 2, "running": 3, "succeeded": 14, "failed": 1, "cancelled": 0 },
  "done": 15,
  "waitingReason": null,
  "error": null,
  "durationSeconds": 212.4,
  "estimatedCostCents": 1.52
}

Download results

results.sh:

curl https://api.maritime.sh/api/swarms/$SWARM_ID/results \
  -H "Authorization: Bearer $MARITIME_API_KEY" -o results.jsonl

# One sandbox per line, in input order:
# {"index": 0, "status": "succeeded", "sandboxId": "sbx-k3m7q2ab", "exitCode": 0, "stdoutTail": "...", "result": "...", ...}

Cancel a run

POST /api/swarms/{id}/cancel. Queued sandboxes never start; running commands are stopped and their sandboxes ended within seconds. Sandboxes that already finished keep their results.

From the SDK

Both SDKs have swarms.start, list, get, cancel, results and wait, which polls every 3 seconds until the run ends. start sends an Idempotency-Key for you, so a retried start returns the same run.

evals.ts:

import { Maritime } from 'maritime-sdk'

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

const run = await maritime.swarms.start({
  command: 'python eval.py --case $MARITIME_SWARM_INDEX',
  count: 50,
  timeoutSeconds: 600,
})

const done = await maritime.swarms.wait(run.id)
console.log(`${done.counts.succeeded} succeeded, ${done.counts.failed} failed`)
for (const sandbox of done.sandboxes) console.log(sandbox.index, sandbox.sandboxId, sandbox.exitCode)

evals.py:

import os

from maritime import Maritime

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

run = client.swarms.start("python eval.py --case $MARITIME_SWARM_INDEX", count=50, timeout_seconds=600)
done = client.swarms.wait(run["id"])
for sandbox in done["sandboxes"]:
    print(sandbox["index"], sandbox["sandboxId"], sandbox["exitCode"])

From the CLI and MCP

terminal:

maritime swarms run --count 50 --command 'python eval.py --case $MARITIME_SWARM_INDEX' --size 2x4 --timeout 10m --wait
maritime swarms list
maritime swarms get <swarm_id>
maritime swarms results <swarm_id> -o results.jsonl
maritime swarms cancel <swarm_id>

--wait follows the run and exits 0 only when every sandbox succeeded. With an --inputs file, each line is one sandbox's args, env and files. The Maritime MCP server has the same calls as tools: start_swarm, list_swarms, get_swarm, cancel_swarm and get_swarm_results. The CLI's own MCP server (maritime mcp) names every tool after its command, so there they are swarms_run, swarms_list, swarms_get, swarms_cancel and swarms_results.

Limits and billing