Limits and performance
Every hard number the API enforces, in one place — plus an honest account of how long a sandbox takes to start.
At a glance
| Limit | Value | Enforced by |
|---|---|---|
| Requests per minute, per account | 120 | 429 rate_limit_exceeded |
| Requests per minute, per source IP | 300 | 429 rate_limit_exceeded |
| Sandbox creations per minute, per account | 40 | 429 rate_limit_exceeded |
| Concurrent sandboxes | 20 (provisioning + running) | 429 quota_exceeded |
| Default sandbox lifetime | 15 minutes | automatic teardown |
| Maximum sandbox lifetime | 1440 minutes after creation | hard cap, extension included |
| Command timeout | 60,000 ms default, 600,000 ms maximum | exit_code: 124 |
| Command length | 100,000 characters | 400 invalid_request |
| Maximum file, read or written | 8 MiB | 413 file_too_large |
| Request body size | 1 MB on every endpoint except the file routes, which accept 10.7 MiB — enough for a 8 MiB file base64-encoded into JSON | 413 |
| Output captured | 1 MiB per stream | truncated: true |
| Active API keys | 10 per account | dashboard |
| Sandbox list page size | 20 default, 100 maximum | 400 invalid_request outside 1–100 |
Rate limits
Two independent windows apply to every API key on an account: 120 requests a minute across all endpoints, and 40 sandbox creations a minute on top of that. Both are fixed 60-second windows counted per account, so extra keys give you no extra headroom.
A third window is counted per source IP address rather than per account: 300 requests a minute, applied before the API key is verified. Set well above the per-account limit, it exists so an unauthenticated caller cannot spend our CPU on key verification, and you will only meet it if several accounts share one egress address — a NAT gateway, or a fleet of workers behind one address. If that is you, the fix is to spread the workers across addresses rather than to slow any one account down.
Every response carries the current state, so you can pace yourself without guessing:
RateLimit-Limit: 120
RateLimit-Remaining: 94
RateLimit-Reset: 37 # seconds until the window resets
Retry-After: 37 # only on a 429
The most common way to hit the general limit is a tight poll loop against a long-running command. Poll every few seconds rather than continuously — see the pattern in running commands.
Concurrency
You can hold 20 sandboxes at once. Sandboxes still provisioning count;
deleted and failed ones do not. Exceeding it is a 429 with
error.type: "quota_exceeded", which — unlike a rate limit — does not clear on its own: you
have to destroy a sandbox first. If your workload is a queue of jobs, cap your worker pool at
20 and make each worker destroy its sandbox in a finally block. Need a
higher ceiling? Ask us.
Timeouts
- Per command:
timeout_ms, default 60,000, maximum 600,000. On expiry the process is killed and you getexit_code: 124with whatever output it had produced. - Per sandbox:
timeout_minutes, default 15, maximum 1440. A sweeper runs every minute and destroys anything past itsexpires_at. - Hard ceiling: 1440 minutes after creation, no matter how often you extend. Work that needs longer must be split across sandboxes, persisting state to storage you control.
Output caps
Each stream is captured up to 1 MiB; beyond that output is dropped and truncated
becomes true. Because the cap is applied as whole chunks arrive, the returned string can
land slightly either side of exactly 1 MiB — treat 1 MiB as the design point, not a byte-exact
guarantee, and check the truncated flag rather than measuring the string. Redirect large
output to a file inside the sandbox and return only the part you need.
Resource limits inside a sandbox
Beyond the vCPU, memory and disk of the size you chose, the sandbox user runs under
per-user limits that stop one runaway process from taking the machine down:
| Limit | Soft | Hard |
|---|---|---|
Processes (nproc) | 512 | 1024 |
Open files (nofile) | 4096 | 8192 |
Single file size (fsize) | 4 GiB | 8 GiB |
Fork bombs hit the process limit rather than the machine. Outbound SMTP (ports 25, 465 and 587) is
blocked as an anti-abuse measure. No inbound port is opened on a sandbox either: a server started
inside one binds its own localhost, and the way to reach it from outside is a
preview URL, which tunnels the request in rather than exposing the machine.
Startup latency
POST /v1/sandboxes is synchronous: it returns only once the machine has booted, finished
its bootstrap and accepted a connection. So the call itself takes as long as the sandbox takes to
start, and how long that is depends on which of two paths it takes.
Warm path — not running today
There is no warm pool running right now, so every create takes the cold path below. This page said it would tell you if the pool were withdrawn rather than leave a latency budget resting on a line that had quietly stopped being true, so: it is withdrawn, and the warm row is gone from the table.
Cold path
If the pool is empty — a burst of requests, or a size other than small, which the pool
does not hold — a machine is created from scratch. That means provisioning, boot, and waiting for the
bootstrap to finish before the sandbox is handed over. It is materially slower than the warm path:
tens of seconds rather than seconds, and longer under load.
What this means for you
- Every size starts the same way, from a prepared image, so a larger sandbox is not slower to get.
- Set a generous client-side HTTP timeout on create — several minutes, not 30 seconds. A client that gives up early leaves a machine running that you are paying for.
- Amortise the cost: create one sandbox and run many commands in it, rather than one sandbox per command.
- Create early. If you know a sandbox will be needed, start provisioning it while you do other work.
- Bursts hurt more than steady load. With no pool running, all ten of ten simultaneous creations are cold.
Measured numbers
We publish measurements rather than marketing figures. The table below is generated from real provisioning times on this platform; it is updated as the numbers change.
| Path | What happens | Time to ready |
|---|---|---|
| Standard | A machine is created from our prepared image and booted. Any size other than small, or a small arriving while the pool is empty. | ~24–47 s |
| Fallback | Prepared image unavailable; the machine is built from a base image. | ~160 s |
Measured on 23 August 2026, small size, from the time each POST /v1/sandboxes was
received to the moment the sandbox was handed over — every such create this deployment has
recorded, not a chosen run. Standard path, 33 creates: fastest 23.6 s, median
30.6 s, ninetieth percentile 40.4 s, slowest 46.9 s. Warm path, 4 creates:
0.46 s to 0.84 s, median 0.56 s. The previous figure here was a single timed
create, and it put the standard floor at 30 s — a third of real creates finish faster
than that, so the range now spans what actually happens. These are real numbers from this
platform, not a target: they will move as the fleet and regions change, and thirty-three
creates is a small sample at the tails. Treat sandbox creation as an operation that takes tens
of seconds and design your retries accordingly.
If a create call takes materially longer than the figures above, that is worth telling us about —
include the request_id from the response headers.
Limits that are not published
The platform also enforces a ceiling on how many machines it will run in total. When it is reached,
creation returns 503 with error.type: "capacity_unavailable". This is not a
per-account quota and normally clears within minutes; retry with backoff.