# How Do I Cap What Agent Sandboxes Can Cost?

> Stack the levers that exist. Per sandbox, pick a small size, a short TTL and an idle timeout, and delete it when the task ends. Per key, give agents scoped, expiring keys. Per account, your plan's concurrency and start limits, a prepaid balance without auto-refill and alerts set the ceiling. A watchdog deletes stragglers.

Source: https://sandboxer.com/guides/cap-what-agent-sandboxes-can-cost. Updated 2026-10-27.

**Short answer:** Stack the levers that exist. Per sandbox, pick a small size, a short TTL and an idle timeout, and delete it when the task ends. Per key, give agents scoped, expiring keys. Per account, your plan's concurrency and start limits, a prepaid balance without auto-refill and alerts set the ceiling. A watchdog deletes stragglers.

There is no "max dollars per sandbox" setting. What you get instead is a set of controls that each bound one dimension: how fast a sandbox burns time, how long it can run per start, how many can run at once, how many can start per day, and how much balance there is to spend. This page lists every one we could find in the [docs](/docs/billing) and the API, shows which ones we tested on prod, and links the watchdog that catches the rest. For the price tables themselves, see [sandbox cost tables](/guides/sandbox-cost-tables).

## Every lever, at a glance

| Lever | Scope | What it bounds | Set with | Stops a running sandbox? |
| - | - | - | - | - |
| Size | Sandbox | Burn rate: `small` is 0.5× `default` | `type` / `--type` | No |
| TTL (auto-stop) | Sandbox | Run time per start | `ttlSeconds` / `--ttl`, `sbx extend` | Yes, at `archiveAfter` |
| Idle timeout | Sandbox | Run time with nothing happening | `idleStopMinutes` / `--idle`, `sbx idle` | Yes, pauses it |
| Delete on finish | Sandbox | Everything after the task, plus storage | `sbx delete --yes`, `DELETE /sandboxes/{id}` | Yes |
| Scoped, expiring key | Key | Which actions an agent can take, and until when | `sbx api-key create --actions … --ttl …` | No |
| In-sandbox token | Sandbox | Code inside can't create, extend or delete sandboxes | Automatic (`SANDBOXER_TOKEN`) | No |
| Concurrency limit | Account | Sandboxes active at once (100 on the $20 plan) | Your plan | No, refuses new ones |
| Start limits | Account | Creates, resumes and forks per minute, hour, day | Your plan | No, refuses new ones |
| Prepaid balance, auto-refill off | Account | Total machine time you can spend | Billing page, `sbx billing autorefill` | Yes, after a 24-hour grace |
| Alerts | Account | Nothing: they notify you | Dashboard **Alerts** | No |
| Member caps | Organization | One member's sandboxes at once, and machine time per plan period | Organization owner (usage cap: API only) | No, refuses new ones |

We tested the per-sandbox levers and the in-sandbox token on prod. We did **not** change any account-level setting (plan, auto-refill, alerts, keys) on the shared test account; those rows come from the [billing](/docs/billing), [limits](/docs/limits), [API keys](/docs/api-keys) and [organizations](/docs/organizations) docs.

## The ceiling, as arithmetic

Prices come from the [pricing page](/pricing): `default` costs $0.036 an hour, and `small` costs $0.018 an hour. A dollar buys 100,000 seconds of `default` time.

**One sandbox.** Its worst case per start is TTL × hourly rate. A `small` sandbox with a 15-minute TTL: 0.25 h × $0.018 = $0.0045 per start. The same sandbox with `--no-auto-stop` and idle pause off can run the whole month: 730 h × $0.018 = $13.14.

**A runaway fleet, per hour.** Your concurrency limit caps how many run at once. On the $20 plan that's 100. If all 100 are `default`: 100 × $0.036 = $3.60 for each hour they all run. If you only ever create `small`: 100 × $0.018 = $1.80.

**A runaway fleet, per day.** Every create, resume, fork and wake on use counts as a start, and each start gets at most one TTL of run time. So, as long as nothing extends a sandbox or turns auto-stop off, run time per day is at most starts per day × TTL. On the $20 plan (200 starts a day) with a 1-hour TTL: 200 × 1 h = 200 h. At the `default` rate that's 200 × $0.036 = $7.20; for `small`, 200 × $0.018 = $3.60. Without TTLs the concurrency limit is the only bound: 100 × 24 h × $0.036 = $86.40 a day.

**The account.** With auto-refill off, the balance is the hard ceiling: your plan's included time plus any packs you bought. On the $20 plan that's 2,000,000 seconds of `default` time ($20 × 100,000), or 555 hours of `default`.

## Per sandbox: size, TTL, idle timeout

Set all three when you create the sandbox, so a sandbox the agent forgets about still stops. We created a `small` sandbox through the REST API with a 15-minute TTL, a 10-minute idle timeout and two labels to find it by later:

```bash
curl -sS -X POST https://sandboxer.com/api/v1/sandboxes \
  -H "Authorization: Bearer $SANDBOXER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"agent-job-1","type":"small","ttlSeconds":900,"idleStopMinutes":10,
       "labels":{"job":"agent-job","tenant":"demo"}}'
```

```json
{
  "id": "sb_wp6oo0ktg2",
  "name": "agent-job-1",
  "state": "provisioning",
  "type": "small",
  "ttlSeconds": 900,
  "idleStopMinutes": 10,
  "archiveAfter": "2026-10-27T10:51:57.1552Z",
  "labels": { "job": "agent-job", "tenant": "demo" }
}
```

The response was `202` (trimmed with `jq`), and the sandbox was `ready` five seconds later. `archiveAfter` is creation time plus 900 seconds. The CLI equivalent is `sbx new --type small --ttl 900 --idle 10 --name agent-job-1`; labels are REST and MCP only ([labels](/docs/api#labels)).

Three details that change the arithmetic:

- **The TTL counts from each start, not from creation.** `sbx extend --ttl` sets the deadline to now + TTL, and a resume or a wake on use starts a fresh TTL window. A sandbox that idles, wakes, idles and wakes can run indefinitely in TTL-sized pieces. Only deleting it ends that.
- **Idle pause stops compute, not storage.** A paused sandbox bills no compute, but its disk and memory count toward your plan's [included storage](/docs/billing#storage). The idle defaults are 15 to 120 minutes depending on your plan; see [idle auto-pause](/guides/idle-auto-pause-and-wake-on-use) for what counts as activity.
- **`--no-auto-stop` removes the per-start bound.** We turned it on and back off on our sandbox:

```bash
sbx extend sb_wp6oo0ktg2 --no-auto-stop
sbx --json info sb_wp6oo0ktg2 | jq '.sandbox | {ttlSeconds, archiveAfter, idleStopMinutes}'
sbx extend sb_wp6oo0ktg2 --ttl 600
sbx --json info sb_wp6oo0ktg2 | jq '.sandbox | {ttlSeconds, archiveAfter, idleStopMinutes}'
```

```json
{ "ttlSeconds": null, "archiveAfter": null, "idleStopMinutes": 10 }
{ "ttlSeconds": 600, "archiveAfter": "2026-10-27T10:47:09.249674Z", "idleStopMinutes": 10 }
```

With auto-stop off, the idle timeout is the only thing left that stops it, and only when nothing is using it. A dev server polled by a health check, a `~/.sandboxer/keep-awake` file or a load average of 0.5 or more keeps it awake. If a sandbox needs `--no-auto-stop`, keep its idle timeout on and give it a label your watchdog skips. On the free trial, TTLs above 8 hours and `--no-auto-stop` are refused (`400 trial_auto_stop_required`).

## Per sandbox: delete when the task ends

A stopped sandbox bills no compute, but it still holds storage, and when storage is full new sandboxes answer `402 storage_limit_reached`. For agent work that leaves nothing worth keeping, delete in a `finally` block, so a crash in the tool call still cleans up:

```bash
sbx delete "$id" --yes
```

The REST call is `DELETE /sandboxes/{id}` with the header `X-Sandboxer-Confirm-Delete` set to the sandbox id. [A fresh sandbox per tool call](/guides/a-fresh-sandbox-per-tool-call) shows the create-run-delete loop with timings. A watchdog catches the ones a `finally` misses: a killed process, a lost network, a deploy mid-task.

## Per key: scoped, expiring keys

API keys can't carry a dollar limit or a TTL ceiling, but they can limit which actions a caller may take and expire on their own. Two patterns from the [API keys docs](/docs/api-keys):

```bash
# The backend that runs agents: create, run, delete only what it created; expires in 30 days
sbx api-key create agent-runner --preset ci --ttl 30d
# The watchdog: read and delete, nothing else
sbx api-key create sandbox-watchdog --actions sandbox.read,sandbox.delete --ttl 90d
```

We didn't run these: creating keys changes the shared test account. The `ci` preset includes `sandbox.delete-own`, which deletes only sandboxes that key created. A key without `sandbox.create`, `sandbox.resume` and `sandbox.fork` can't start anything, so it can't add cost at all.

A key with `sandbox.create` can still ask for `"ttlSeconds": null` or `xlarge`. If an agent picks the arguments, don't give it the key. Have your backend create sandboxes with the size and TTL you choose, and expose only "run this command" to the model. The MCP server's `sbx mcp --type small --ttl 1800` sets defaults for sandboxes the agent creates without those arguments; it is not a ceiling.

### The token inside the sandbox can't add cost

Code running in a sandbox gets `SANDBOXER_TOKEN`, a key limited to that sandbox. We tried to use it from inside our sandbox to create a `large` sandbox and to turn off its own auto-stop:

```bash
sbx exec sb_wp6oo0ktg2 -- bash -c 'curl -sS -X POST https://sandboxer.com/api/v1/sandboxes \
  -H "Authorization: Bearer $SANDBOXER_TOKEN" -H "Content-Type: application/json" \
  -d "{\"type\":\"large\"}"'
```

```json
{"code":"api_key_action_forbidden","error":{"code":"api_key_action_forbidden","details":{"action":"sandbox.create"},"message":"This API key does not allow sandbox.create.","status":403}, …}
```

The same token sending `PATCH /sandboxes/sb_wp6oo0ktg2` with `{"ttlSeconds":null}` got `403 api_key_action_forbidden` with `"action":"sandbox.update"`. An agent with a shell can't spawn more sandboxes or keep its own running past the TTL, as long as you don't put a broader key in its environment.

## Per account: limits, balance, alerts

None of these were changed in our test. They come from the docs and the API.

- **Concurrency and start limits.** Your plan sets how many sandboxes can be active at once and how many starts you get per minute, hour and day. Going over returns `429 limit_reached`, `rate_limited` or `daily_limit_reached`. Check what's left with `sbx limits --json | jq '.starts'`. See [rate limits and concurrency](/docs/limits).
- **Prepaid balance with auto-refill off.** This is the closest thing to a hard spend cap. When the balance reaches zero, new creates, resumes and forks fail with `402 billing_required`, the account owner gets an email, and running sandboxes keep running for a 24-hour grace period, then stop with memory and disk kept. With auto-refill on, packs are bought automatically below your threshold, so there is no ceiling. Check it with `sbx billing`.
- **Alerts.** The dashboard's **Alerts** page has four rules: balance falls below N hours, spend in the last 24 hours (rolling, at list price) exceeds N dollars, a sandbox runs longer than N hours, and start-limit usage reaches N% of a limit (checked against the per-minute, per-hour and per-day start limits). Each sends an email or a webhook, and each rule fires at most once per 24 hours per subject. Alerts notify only; nothing is stopped.
- **Organization member caps.** An organization owner can cap a member's concurrent sandboxes (`429 member_limit_reached`) and a member's machine time with the API: `PATCH /orgs/{orgId}/members/{userId}` with `usageCapSeconds`, counted in machine-time seconds from the start of the plan period (or over the last month without a plan). Once it's reached, the member's creates, resumes and forks get `402 team_member_cap_reached`; running sandboxes keep running. `clearUsageCap: true` removes it. The usage cap is API-only ([organizations](/docs/organizations)). Giving each agent service its own member is a way to give it its own budget.

## A watchdog that deletes stragglers

The levers above bound the damage; a watchdog removes what's left. Run it from cron with a server-side label filter, a dry run by default, and an age threshold a bit above your longest real task. [Clean up orphaned sandboxes](/guides/clean-up-orphaned-sandboxes) has the full script, with the label query, paging and the confirmation header a delete needs.

We ran a small Python version against our sandbox, filtered by its label and name prefix. A 60-minute threshold matched nothing, a 0.5-minute one matched it, and the `--delete` run removed it:

```text
would delete sb_wp6oo0ktg2 agent-job-1 ready small age=0.6m
deleted sb_wp6oo0ktg2 agent-job-1 ready small age=0.7m
```

`sbx --json info sb_wp6oo0ktg2` answered `404 not_found` (exit code 4) in the same second. For cost, two choices matter:

- **Delete or stop.** Deleting also frees storage. To keep the data, list with `state=ready,idle,running` and call `POST /sandboxes/{id}/stop` instead.
- **Give it its own key.** A key with only `sandbox.read` and `sandbox.delete` can't start anything, so the watchdog itself can't add cost.

Keep the watchdog as the backstop. The primary path is still deleting in your own code when the task ends.

## What our test cost

`sbx usage` read the meter while the sandbox ran: 19 seconds of `default` time after 40 seconds of wall time, because `small` runs at 0.5×. That's 19 / 100,000 = $0.00019. The whole test, including the in-sandbox calls, ran from 10:36:57 to 10:37:37 UTC on October 27, 2026, with no errors from the platform.

## FAQ

### Is there a hard dollar limit per account?

Not as a separate setting. The prepaid balance is the hard ceiling when auto-refill is off: new starts stop at zero and running sandboxes stop after a 24-hour grace period. Alerts can warn you before that.

### Does the TTL limit a sandbox's total lifetime?

No. It limits each run. A resume, a wake on use or `sbx extend` sets a new deadline of now + TTL. To end a sandbox for good, delete it.

### Can an agent inside a sandbox start more sandboxes?

Not with the token it gets. We tried from inside: `SANDBOXER_TOKEN` got `403 api_key_action_forbidden` for `sandbox.create` and for changing its own TTL. Don't put a broader key in the sandbox's environment.

### Does a stopped or paused sandbox cost anything?

No compute. Its disk and memory count toward your plan's included storage, and when that's full new sandboxes are refused until you delete some.

### Can I give one API key its own budget?

No: keys limit actions, sandboxes, environments and expiry, not spend. For a separate budget, use an organization member with a usage cap, or enforce one in your backend with `sbx usage` per sandbox.

## Related

- [How much does a cloud sandbox cost?](/guides/sandbox-cost-tables)
- [Idle auto-pause and wake on use](/guides/idle-auto-pause-and-wake-on-use)
- [API keys and scopes](/docs/api-keys)
- [Limits and error codes](/docs/limits)
