AgentBox v2 lets your code create, use and delete sandboxes through one API. The box has outbound internet, so a process inside it can call the GMI model catalog with the same account's model key. To show what that is for, I built Does It Run: a lobby that reads GitHub's trending page, launches one sandbox per repo, and has Gemini 4 Argon turn each README into an install plan that the sandbox executes. The video walks through the dashboard. This post is the part the video skips: the 5 API calls, what the runner does inside the box, every number from the run, the price of a sandbox-minute, and the limits you will hit first.
November 09, 2026

Every repo on GitHub's trending page got its own machine, a model wrote the install plan, and the whole fleet cost about $1.29 before it drained back to 0.
One card per repo. Green means the first example ran. Red means a step failed, and the card quotes the failing line. Amber means the sandbox lacked something the repo needs: an API key, a display, a GPU. The chart in the corner counts live sandboxes; it climbs in one burst when a run starts and falls as each verdict lands, because every verdict is followed by a delete call.

The dashboard after both batches: 47 cards, the live-sandbox chart back at 0
The red card in the video is nightly-labs/openbot. The clone, the bun install and 1,467 packages went fine, then node resources/cli/openbot-cli.mjs --help printed its usage text and exited with code 2, and the card quotes that line.

The red card's drawer: the plan, the steps, the log, and the quoted failing line
Everything in the demo is 5 operations on the AgentBox API. The lobby is about 1,200 lines of stdlib Python on my laptop, a server plus a REST client; the same loop is a few lines in the SDK.
Call | What it does in Does It Run |
|---|---|
Create a template (an Agent in the SDK) | Once, in the console. Points at my runner image in a public registry and picks a data center. In the REST API the size is part of the template; in the SDK it is chosen at launch. |
POST /sandboxes | Once per repo. Carries the template id, a lifetime in seconds, and env_vars with the repo name, a job id, the model id and the model key. |
GET /sandboxes/{id} | Polled until running. Median 1.0 s. |
POST /executions on the sandbox's own host | Starts the runner. The runner answers on port 8080 within 0.3 to 0.7 s. |
DELETE /sandboxes/{id} | 8 s after the verdict. The chart drops by 1 and metering stops. |
There is also POST /sandboxes/{id}/timeout to extend a lifetime. The lobby never needed it, because every sandbox is created with the runner's 15-minute cap plus 5 minutes.
The create response hands back the sandbox id, a sandbox_key and domain that together form the box's hostname, and an access token for the data plane. Any port a process inside listens on gets a public URL with the port in front of the hostname. The runner's status page is https://8080-{sandbox_key}.{domain}/status, and the lobby reads it the way it would read any web service.
The image is built for linux/amd64 and pushed to a public registry once. The template needs an anonymously pullable image, and the lobby passes REPO, JOB_ID, MODEL, GMI_API_KEY and the runner's timeouts to each sandbox as environment variables.
Here isHere is the loop with the Python SDK. Every name below is from the SDK reference: client.agents.get, agent.launch, wait_until_running, execute, delete. My lobby does the same over REST, where it also passes the variables on the exec call.
# pip install gmi-agentbox-sdk
# GMI_AGENTBOX_API_KEY = a compute-scoped key (Console: Compute > Access > API Keys)
import os
from agentbox_sdk import AgentBoxClient
client = AgentBoxClient()
agent = client.agents.get("doesitrun1") # the template I registered in the console
sandbox = agent.launch(
instance_type="gmi.sandbox.large", # 4 vCPU, 8 GiB, 40 GB
env=[
{"name": "REPO", "value": "obra/superpowers"},
{"name": "JOB_ID", "value": "demo-1"},
{"name": "MODEL", "value": "google/gemini-4-argon"},
{"name": "GMI_API_KEY", "value": os.environ["GMI_API_KEY"], "secret": True},
],
metadata={"demo": "does-it-run", "repo": "obra/superpowers"},
timeout_seconds=1200, # my runner caps itself at 900 s
)
sandbox.wait_until_running(timeout=300, poll_interval=2.0)
print(sandbox.status, sandbox.endpoint_url)
# start the runner in the background; execute(wait=False) returns at once
sandbox.execute("setsid nohup python3 /app/runner.py > /tmp/runner.log 2>&1 < /dev/null &", wait=False)
# ... poll the status page (the endpoint host with 8080- in front) until the verdict lands ...
sandbox.delete() # one-way; metering stops hereThe demo uses 2 keys. Creating sandboxes takes a compute-scoped key from the Compute panel of the console. The model key, the one you already use for chat completions, goes inside each sandbox as an environment variable so the runner can call the catalog from there. Both keys belong to the same account, so the model calls and the sandbox minutes land on one bill.
The runner is 1 Python file of 621 lines, baked into a Debian image with git, Python 3.12 and uv, Node 22 with corepack, Go 1.23, a stable Rust toolchain, make and gcc. It does 5 things in order and serves its state on port 8080 the whole time.
Clone. git clone --depth 1 with shallow submodules and a 3-minute timeout. A clone that will not finish is amber.
Inspect. Read the README, a listing of the top 3 directory levels, and the manifests it can find: package.json, pyproject.toml, requirements.txt, setup.py, Cargo.toml, go.mod, a Makefile, a Dockerfile, a compose file and lock files.
Plan. 1 model call, with 1 terse retry if the reply is not JSON. The system prompt tells the model what the box has (the toolchain above, 2 CPUs, no GPU, outbound internet, CI=true) and what it lacks (sudo, apt, docker, a display, every credential). It asks for JSON: a list of install commands, one command to run, whether that command is a one-shot command or a server, a health URL if it is a server, and a list of things the box cannot provide. The order of preference is the README's first self-contained example, then the repo's own test suite if it finishes in minutes, then a demo mode, and only as a last resort --help, which the plan has to label as weak evidence.
Execute. Each command runs with sh -c in a scrubbed environment: no model key, no lobby URL, and the runner marks itself non-dumpable so a repo cannot read the key out of /proc. 4 minutes per install step, 2 minutes for the example (90 s for a server to answer its health URL), 15 minutes in total.
Verdict. A second model call reads the exit codes and the last lines of output and returns green, red or amber with one quoted line. The runner clamps it: a non-zero exit can never be green, and a red without a quote gets the failing line from the log.
The plan is the interesting artifact, so here is one in the shape the prompt asks for:
{
"language": "Python",
"kind": "library",
"summary": "A CLI and library for converting notebooks to scripts",
"install": ["uv venv && uv pip install -e ."],
"run": "uv run pytest -x -q",
"run_mode": "command",
"health": null,
"expect": "pytest reports passed, 0 failed",
"evidence": "test",
"needs": [],
"confidence": 0.8
}The rule that took the longest to get right is in the prompt: nothing persists between commands. Each line runs in its own shell, so source .venv/bin/activate does nothing and the plan has to say uv run or .venv/bin/python explicitly. Without that rule a venv created in one step is invisible to the next, pytest runs on system Python, and the repo goes red for the box's fault. The runner also puts .venv/bin on PATH for every later command.
The video shows 2 batches. The first, 36 repos collected over several runs of the daily and weekly trending pages, was run before recording. The second, 11 repos from this week's page, ran live. The dashboard adds them up.
Loaded batch | Live batch | Total | |
|---|---|---|---|
Repos, one sandbox each | 36 | 11 | 47 |
Green | 30 | 9 | 39 |
Amber | 5 | 2 | 7 |
Red | 1 | 0 | 1 |
Model calls (plan + verdict) | 72 | 22 | 94 |
Model cost at $2 / $10 per M | $0.739 | $0.229 | $0.968 |
Sandbox-minutes | 46 | 10 | 56 |
Create to running was 1.0 s at the median over all 47 boxes. The peak number of sandboxes alive at once was 11, and all 47 were deleted by the end, so the live count returned to 0.

47 trending repos, 47 sandboxes: 39 ran, 7 were blocked by the box, 1 failed
The 7 ambers are cases the box could not host: an Electron app that needs a display, repos whose first example wants an API key, a project whose README only works with docker. Amber says nothing about the repo. The single red is a line a maintainer can check in a minute: --help exits 2 on a fresh clone.
The cost tile on the dashboard counts model tokens only. Sandbox time is the other quarter.

About $1.29 for 47 repos: $0.97 of model calls, $0.32 of sandbox time
The 94 model calls, a plan and a verdict per repo, came to $0.968, about 2 cents per repo at the $2 in and $10 out per million the lobby prices with. The 56 sandbox-minutes come to about $0.32 at $0.34 per hour, billed per minute; the doesitrun1 template is the large preset. Together that is roughly $1.29 for 47 repos, under 3 cents each, 75% of it model tokens. A smaller preset would have cut the sandbox share of the bill: the medium box is $0.17 per hour and the small one $0.0861.

AgentBox presets: the large box costs about half a cent a minute
Per the product page, template builds are free, and during early access paused sandboxes and snapshots are $0. Its own example is one medium box for a 730-hour month, $124.10.

Same repos: 56 min in series, at most 15 min per burst
In series, 56 sandbox-minutes is 56 minutes of waiting on one machine, and that machine would carry 47 strangers' install scripts competing for ports, package versions and disk. In a burst, each run finishes with its slowest repo, and the lobby caps a run at 25 sandboxes at once. The runner caps a repo at 15 minutes; the average sandbox lived 71 s (56 minutes over 47 boxes) and most verdicts landed inside 2 minutes. The fleet size came from the page: the lobby takes every row the trending page returns, up to the count in the corner of the dashboard, 25 by default.

A sandbox lives about as long as the work: 1.0 s to running, then the repo's own time
One sandbox's life, for a typical green repo: 1.0 s from the create call to running, 0.5 s until the runner answers, then whatever the repo needs to clone, install and run, then 8 s of grace so the card's flip is visible before the delete. The lobby pulls the full log before that. The box exists for about as long as the work does.
Things from the docs and the run to check before your first burst.
A sandbox created without timeout_seconds lives 24 hours, so set one. Mine get 1200 s, and the lobby deletes them after the verdict, which keeps cleanup out of the runner.
Synchronous execute waits 25 s at most. Start anything longer in the background with wait=False and poll it, or have it serve its own status, which is why the runner is a web server.
stdout and stderr come back capped at 1 MiB each, so write big logs to a file and download them.
A paused sandbox keeps its disk for up to 7 days, minus /tmp and /dev/shm. Resuming is a cold start: memory and processes are cleared and the start command stays idle. Write anything the next session needs to disk before the pause.
Deleting a sandbox is permanent. Delete the sandboxes before their Agent, since deleting an Agent returns 409 while any of its sandboxes exist.
The sandbox API takes the compute key only, and the model key goes inside the box.
A note on the numbers. Every measured number in this post comes from the GMI Cloud MaaS inference engine and AgentBox on a Tier 5 account, so your spawn times will vary with your account tier and the load on the day.
Each job gets its own sandbox, which comes down to 1 create call and 1 delete call. Your code decides the fleet size at runtime from the work in front of it, a trending page or a queue. At 1.0 s to running, boxes can spin up on demand for a job like this.
Everything a job needs ships in a container image you push once, and everything that changes per job rides in as environment variables at create time. The runner skipped the toolchain install, and the keys stayed out of the repo.
47 repos cost about $1.29, and 75% of that went to model tokens, so the bill follows the work.
The REST shape, with the compute key from the Compute panel. Create, check, delete:
export GMI_AGENTBOX_API_KEY="your-compute-key"
BASE=https://console.gmicloud.ai/api/v2
# create: template id, lifetime in seconds, per-sandbox env; the response carries id, sandbox_key, domain and the data-plane token
curl -s -X POST $BASE/sandboxes -H "Authorization: Bearer $GMI_AGENTBOX_API_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"template_id": "<your-template-id>", "timeout": 1200,
"env_vars": {"REPO": "obra/superpowers"}, "metadata": {"demo": "does-it-run"}}'
# poll until state is running
curl -s $BASE/sandboxes/<id> -H "Authorization: Bearer $GMI_AGENTBOX_API_KEY"
# delete when done; metering stops here
curl -s -X DELETE $BASE/sandboxes/<id> -H "Authorization: Bearer $GMI_AGENTBOX_API_KEY"Does It Run runs in mock mode on a laptop with no keys, in Docker with a model key, and on AgentBox with both.
AgentBox started as a marketplace: register an agent image, GMI runs an instance per user. That is still there. v2 hands the machinery to your code, and the thing I wanted to show is how little code that takes. The runner that lives inside each box is 621 lines, and the lobby that drove 47 of them is about 1,200, mock and Docker fleets included. If your agent runs code it did not write, or keeps per-user state as a live process with files on disk, this is the shape: one box per job, created when the work arrives and deleted when it ends.
Ready to build? Get Started
Grace Deng
DevRel @ GMI Cloud
GMI Cloud helps you architect, deploy, optimize, and scale your AI strategies
