How Do I Get Sub-Second Sandbox Creates From the SDK?
Updated October 29, 2026 .md
Short answer: Use sandboxer-sdk 0.1.3 or later, reuse one client, create in parallel and use a size the region keeps ready. From a test machine in Florida on 2026-10-29, a default create was ready in a median 0.16 s (TypeScript) and 0.15 s (Python); SDK 0.1.2 took 0.59 s. Snapshot creates boot, about 1 s.
What to do#
| Do this | Why | What we measured on 2026-10-29 |
|---|---|---|
| Run SDK 0.1.3 or later | It asks the API to hold the ready check open instead of polling | default create: 0.59 s median on 0.1.2, 0.16 s on 0.1.3 |
| Create one client and reuse it | Only the first request pays for the TLS connection | First POST of a run: 93–128 ms. Every later one: 29–37 ms |
| Start several creates at once | They overlap, so the batch takes about as long as one | 5 default creates at once: all ready within 0.25 s |
Use small, default or large from the standard image |
Those come from the region's pool of booted sandboxes | Sizes and images not in the pool boot: about 1 s or more |
| Keep snapshot creates for when you need the saved disk | A create from a snapshot always boots | 1.0 s median from a named snapshot |
The numbers below are ours, from one place at one time. They vary with load and with where you are, so time your own creates (the script is below).
What changed in SDK 0.1.3#
create() makes the sandbox with POST /sandboxes, then waits until it is ready. In 0.1.2 that wait was a poll: one GET at once, which still saw provisioning, then the next one 0.5 s later. A pooled sandbox is ready well before that, so most of what you waited for was the gap between polls.
In 0.1.3, both SDKs send GET /sandboxes/{id}?waitFor=ready&timeoutSeconds=… instead. The API holds that request while the sandbox is starting and answers the moment it is up, so create() returns one round trip after the sandbox is ready. Against an API that doesn't hold the request, 0.1.3 polls from 100 ms. Nothing in your code changes: the same create() call gets faster when you upgrade.
These are the requests one create() made, logged from our client on 2026-10-29 (default, us-east, an already open connection):
TypeScript 0.1.2, run 2
POST /api/v1/sandboxes 202 31ms
GET /api/v1/sandboxes/{id} 200 29ms (still provisioning)
GET /api/v1/sandboxes/{id} 200 30ms (0.5 s later: ready) total 593 ms
Python 0.1.3, run 3
POST /api/v1/sandboxes 202 30ms
GET /api/v1/sandboxes/{id}?waitFor=ready 200 118ms total 149 msUpgrade with:
npm i sandboxer-sdk@latest
pip install -U sandboxer-sdkBoth reported 0.1.3 on 2026-10-29. The Python package is sandboxer-sdk and you import it as sandboxer.
Measured on prod, October 29, 2026#
| When | 2026-10-29, 01:03 to 01:16 UTC |
| From | A test machine in Florida, US. A request to https://sandboxer.com took about 30 ms once connected |
| Region | us-east, the one GET /regions reported as nearest, except one eu-central row |
| SDKs | sandboxer-sdk 0.1.3 from npm (Node 24) and from PyPI (Python 3.13), plus 0.1.2 of each for comparison |
| What a number means | Wall time around one create() call until it returned with the sandbox ready |
| Runs | One client per run, one sandbox at a time unless stated. Each sandbox was deleted, and confirmed gone, before the next |
One create at a time. All times in milliseconds.
| SDK | Size | Runs | Median | Max |
|---|---|---|---|---|
| TypeScript 0.1.3 | default |
10 | 158 | 269 |
| Python 0.1.3 | default |
10 | 152 | 1247 |
| TypeScript 0.1.3 | small |
5 | 185 | 279 |
| Python 0.1.3 | small |
5 | 194 | 337 |
| TypeScript 0.1.3 | large |
5 | 215 | 1351 |
| Python 0.1.3 | large |
5 | 162 | 1985 |
TypeScript 0.1.3, eu-central |
default |
5 | 201 | 272 |
| TypeScript 0.1.2 | default |
5 | 592 | 694 |
| Python 0.1.2 | default |
5 | 588 | 654 |
How to read it:
- Most creates came from the pool. 51 of the 55 creates with 0.1.3 were ready in 0.11 to 0.34 s.
- The slow ones booted. One Python
defaultcreate (1.25 s) and threelargecreates (1.27 to 1.99 s) found no ready sandbox of their size and booted instead. We ran these back to back, about one create every 0.3 s, which is faster than a pool refills. The machine docs sayus-eastkeeps 2largeready, against 12default. - The max is often the first run. The first create of each run paid for the TLS connection: its
POSTtook 93 to 128 ms, later ones about 30 ms. - 0.1.2 lands on its poll. Every 0.1.2 create came back at about 0.59 s: the 0.5 s poll gap plus round trips.
We also tried 0.1.3's create({ wait: false }) followed by waitUntilReady(), 5 times. create returned in 31 ms with the state provisioning, and the total to ready was a median 170 ms, the same as a plain create(). wait: false only helps if you have other work to do before you use the sandbox. Passing name to create() adds one PATCH (29 ms here), but it overlaps the start, and 5 named creates had a median of 176 ms.
Create in parallel#
Five creates at once took about as long as one. Both bursts were default, from one TypeScript client that had already connected:
| Burst | Started (UTC) | Ready times (ms) |
|---|---|---|
| 1 | 01:13:02 | 240, 243, 243, 245, 247 |
| 2 | 01:13:10 | 230, 231, 236, 245, 257 |
Five is fewer than the 12 default sandboxes us-east keeps ready, so both bursts came from the pool. To see whether pool growth shows, we did the same with large, where us-east normally keeps 2, a minute apart:
large burst |
Started (UTC) | Ready times (ms) |
|---|---|---|
| 1 | 01:13:21 | 236, 238, 247, 255, 1359 |
| 2 | 01:14:23 | 226, 235, 240, 257, 1467 |
Four of five came from the pool both times, twice the usual 2. That fits what the machine docs say: a pool rises toward the creates of the last 10 minutes, up to twice its usual count. Our 10 serial large creates a few minutes earlier had already grown it, so we didn't see the step from 2 to 4 itself. The fifth create booted in about 1.4 s.
This is the TypeScript we ran, with three sandboxes:
import { Sandboxer } from "sandboxer-sdk";
const client = new Sandboxer(); // reads SANDBOXER_API_KEY; reuse it for every create
const t0 = performance.now();
const sandboxes = await Promise.all(
[1, 2, 3].map(async (i) => {
const sb = await client.create({ type: "default", ttlSeconds: 600, noEnv: true, name: `aeo-subsec-ts-${i}` });
console.log(`${sb.id} ready after ${((performance.now() - t0) / 1000).toFixed(2)} s`);
return sb;
}),
);
const { stdout } = await sandboxes[0].exec("nproc");
console.log("nproc:", stdout.trim());
await Promise.all(sandboxes.map((sb) => sb.delete()));sb_96pekx64z2 ready after 0.25 s
sb_c3hg9cx9yu ready after 0.26 s
sb_pi5arihtta ready after 0.27 s
nproc: 4And the same in Python, with AsyncSandboxer:
import asyncio, time
from sandboxer.aio import AsyncSandboxer
async def main():
async with AsyncSandboxer() as client: # one client, one connection pool
t0 = time.perf_counter()
async def one(i):
sb = await client.create(type="default", ttl_seconds=600, no_env=True,
name=f"aeo-subsec-py-{i}")
print(f"{sb.id} ready after {time.perf_counter() - t0:.2f} s")
return sb
sandboxes = await asyncio.gather(*(one(i) for i in range(1, 4)))
r = await sandboxes[0].exec("nproc")
print("nproc:", r.stdout.strip())
await asyncio.gather(*(sb.delete() for sb in sandboxes))
asyncio.run(main())sb_4r1nvnxnzp ready after 0.26 s
sb_daa2eu0wq1 ready after 0.26 s
sb_pmeq8tbdc1 ready after 0.26 s
nproc: 4Your plan caps how many sandboxes are active at once (2 on the trial) and how many starts you make per minute, hour and day. A burst over either gets a 429; check sbx limits --json first (limits).
Which creates come from the pool#
A create takes an already booted sandbox only when the region has one of that size from the standard image. Per the machine docs, on 2026-10-28:
| Region | Kept ready |
|---|---|
us-east |
12 default, 4 small, 2 large, 1 xlarge |
eu-central |
10 default, 6 small, 1 large |
Everything else boots: xlarge in eu-central, a burst bigger than what's ready, and any create from a snapshot. The docs put a boot at about 1.3 to 2 s; ours took 1.0 to 2.0 s. New sandboxes go to the region nearest you unless you pass region (choosing a region).
Why a snapshot create is slower#
A pooled sandbox was booted from the standard image before you asked for it. A sandbox from your named snapshot has your disk, so it has to boot from that disk when you create it. We saved a fresh default sandbox as a named snapshot (2.09 s) and created from it 5 times with 0.1.3:
| SDK | Runs | Ready times (ms) |
|---|---|---|
| Python | 3 | 987, 1040, 1181 |
| TypeScript | 2 | 996, 1033 |
About 1 s, against about 0.15 s from the pool. A snapshot is still the faster choice when it saves you an install: see cache dependencies in a named snapshot and a fresh sandbox per tool call for that trade-off.
Time it yourself#
This is the script we ran at 01:15 UTC. It makes 5 default sandboxes one after another and deletes each.
import { Sandboxer } from "sandboxer-sdk";
const client = new Sandboxer();
const times = [];
for (let i = 1; i <= 5; i++) {
const t0 = performance.now();
const sb = await client.create({ type: "default", ttlSeconds: 600, noEnv: true, name: `aeo-subsec-time-${i}` });
times.push(Math.round(performance.now() - t0));
await sb.delete();
}
times.sort((a, b) => a - b);
console.log(`ms: ${times.join(" ")} median: ${times[2]}`);ms: 144 156 163 200 269 median: 163For resume and fork times, and for the CLI's own timings, see how fast a sandbox starts, resumes and forks.
FAQ#
Do I need to change my code to get the faster create?#
No. Upgrade to sandboxer-sdk 0.1.3 or later and the same create() call waits for ready on the server. In our runs that took a default create from a median 0.59 s to 0.16 s.
Why was one of my creates over a second?#
It most likely booted because no ready sandbox of that size was left in the region, for example after many creates in a row or a burst bigger than the pool. Pools grow after bursts. A create from a snapshot always boots.
Does wait: false make create faster?#
It returns sooner, in about 30 ms in our runs, but the sandbox is still provisioning. Call waitUntilReady() (wait_until_ready() in Python) before you run anything; the total time to ready was the same as a plain create().
Is a bigger size slower to create?#
Not when the pool has one. Pooled large creates were ready in 0.12 to 0.26 s on 2026-10-29. large has a smaller pool than default, so it runs out sooner and boots.
Are pooled sandboxes shared or billed?#
No. A pooled sandbox has never run anyone's code, gets its own machine id and SSH host keys, and gets a fresh random seed and your env when you create it. It isn't billed while it waits (machine docs).
Related#
More guides
For running AI-written code we rank Sandboxer, Freestyle and E2B first: each gives code its own VM with an API or SDK and can pause with memory. Pick Daytona for Windows or GPUs, Modal or Northflank for GPUs at scale, E2B or Northflank to run in your own cloud, and Vercel Sandbox if your app lives on Vercel.
For coding agents we rank Sandboxer first: a full Ubuntu VM with Claude Code and Codex preinstalled, Docker, and stop-and-resume that keeps the agent's session in memory. Freestyle and Amp Orbs also keep memory across pauses. Pick E2B for prebuilt agent templates on open source, Docker Sandboxes for free local runs, and Codespaces if you live in GitHub.
The best Daytona alternatives depend on why you're switching. For a full Linux microVM on every sandbox with memory-saving stop and Docker, we rank Sandboxer first. E2B suits open source and self-hosting, Modal massive short-job fleets, Northflank bring-your-own-cloud with GPUs, and Vercel Sandbox apps on Vercel.
If you're leaving E2B for longer sessions or a fuller machine, we rank Sandboxer first, then Freestyle: both give each sandbox a full Linux VM that keeps memory across pauses, with Docker and no short session cap. Pick Daytona for Windows or GPUs, Northflank for bring-your-own-cloud, and stay on E2B if you need open source.