On Runtime, every list in every product returns the same page object. One
loop walks every page. A sandbox takes up to 32 key: value labels. So a CI
run, a customer or an agent task can find and clean up exactly its own
sandboxes. A paid account runs 100 sandboxes at the same time.
Paused sandboxes do not count toward that limit
(pricing, checked 25 October 2026).
Label at create, filter when you list
TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();const sbx = await runtime.sandboxes.create({ labels: { team: "search", job: "42", ci: "true" },});const page = await runtime.sandboxes.list({ labels: { team: "search" }, state: ["running"] });for await (const found of page) console.log(found.id, found.info.name, found.state);await sbx.stop();Pythonfrom withruntime import Runtimeruntime = Runtime()sbx = runtime.sandboxes.create(labels={"team": "search", "job": "42", "ci": "true"})for found in runtime.sandboxes.list(labels={"team": "search"}, state=["running"]): print(found.id, found.info["name"], found.state)sbx.stop()Terminalid=$(runtime sandboxes create --name demo --label team=search --label job=42)runtime sandboxes lsruntime sandboxes get "${id}"runtime sandboxes stop "${id}"--label k=v repeats on runtime sandboxes create. runtime ls lists
everything the account runs, across every product, not only sandboxes.
Clean up after a CI run
Label each sandbox with the run's id. Stop whatever is left when the run ends, even after a failure:
TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();const run = process.env.GITHUB_RUN_ID ?? "local";await runtime.sandboxes.create({ labels: { ci: run } });for await (const leftover of await runtime.sandboxes.list({ labels: { ci: run } })) await leftover.stop();Pythonimport osfrom withruntime import Runtimeruntime = Runtime()run = os.environ.get("GITHUB_RUN_ID", "local")runtime.sandboxes.create(labels={"ci": run})for leftover in runtime.sandboxes.list(labels={"ci": run}): leftover.stop()Change labels later
TypeScriptimport { Sandbox } from "withruntime";const sbx = await Sandbox.create({ labels: { team: "search" } });await sbx.update({ labels: { team: "search", owner: "ada" } });Terminalruntime sandboxes update "${id}" --name dev-2 --label team=searchFilters and paging
| Filter or field | What it does |
|---|---|
labels |
Every label given must match. Over HTTP, repeat label=key:value |
state |
Such as ["running"] or ["paused"] |
name |
The one live sandbox with that name |
includeStopped |
A stopped sandbox whose disk is gone is left out unless this is true |
limit, cursor |
Page size and the next page, over HTTP |
| The page | page.data, page.hasMore, page.next(), page.toArray(), for await |
| Python page | page.data, page.has_more, page.next_page(), page.to_list(), for |
| Label limit | Up to 32 key: value labels a sandbox |
| MCP | runtime_sandboxes_list by state, name or label, or one by id |
| Cost | Listing and labels cost nothing |
Mistakes to avoid
- Reading only the first page.
page.datais one page. Loop withfor await(orforin Python), or calltoArray(), to reach every match. - Expecting every stopped sandbox in the list. A stopped sandbox that keeps
its disk is listed by default. One whose disk is gone is listed only with
includeStopped: true. Use that flag to audit what ran. - Putting secrets or personal data in labels. Labels are for finding
things. Anyone with a read-only key on the account can list them. Keep
secrets in
envor in Runtime secrets. - Using a label as a unique handle. Many sandboxes can share a label. For exactly one, use a name.
- Leaving cleanup to chance. An idle sandbox pauses itself. A paused sandbox keeps its disk, billed as storage. Stop sandboxes by label at the end of each job.
Related
- Find a sandbox by name for one handle per sandbox.
- Agent evals and SWE-bench, where each run labels its sandboxes.
- Keep a sandbox running for sandboxes you do not want cleaned up.
- Find sandboxes again in the SDK reference.
Facts on this page were checked on 26 October 2026.