---
title: "Clone N sandboxes from a snapshot in ~10ms each; forks are free [rate class: sandbox_api_rps]"
method: POST
path: "/preview/sandboxes/forks"
tags: ["sandboxes"]
---

# Clone N sandboxes from a snapshot in ~10ms each; forks are free \[rate class: sandbox_api_rps\]

`POST /preview/sandboxes/forks`

## Request

- Base URL: `https://autoresearch.sfcompute.com`
- URL: `https://autoresearch.sfcompute.com/preview/sandboxes/forks`
- Auth: HTTP bearer

Clone running sandboxes from a snapshot - the cheap path, and how every batch should start. Each fork resumes the snapshot's EXACT state - memory, running processes, disk, warm caches. Forks use copy-on-write. You pay nothing for cloning and only each fork's own RAM-minutes while it lives. `from` takes any snapshot id: an env baked by create_sandbox_env, a mid-run state captured by snapshot_sandbox, or the `snapshot` a create_sandbox returned. Fan out with count (up to 256 per call) rather than looping single forks. N rollouts = one baked env, one fork_sandbox(count: N), one array-shaped sandbox_exec across the forks, N deletes. Never rebuild state that already exists in a snapshot - fork it. Forks inherit the snapshot's size AND its network (the response echoes `egress`); neither is per-fork, so an env baked with egress: "none" has networkless forks forever - bake a new env to change it.

## Query parameters

- `workspace` string

## Request body

- object
  - `count` integer, nullable: How many sandboxes to clone in this call, 1..=256 (default 1). One call with count N beats N calls with count 1.
  - `from` string, required: Snapshot id to fork from: an env baked by create_sandbox_env, a state captured by snapshot_sandbox, or the `snapshot` field a create_sandbox returned.
  - `package_cache` boolean, nullable: Serve npm and bun installs from a cache on the machine instead of from registry.npmjs.org. Default false. Faster on a cold install - the packages come off the same host rather than the wire. IT REWRITES YOUR LOCKFILE, which is why it is off by default and why you should not turn it on for a run whose lockfile you keep. The cache serves tarballs from a loopback address, so bun records THAT address for every package: a `bun.lock` produced under it differs from the one you committed, on every dependency. If you diff it, commit it, or hand it to a `--frozen-lockfile` check afterwards, that check fails. Use it for throwaway installs where only the speed matters. It applies to the sandbox you are handed. A `setup` runs during the bake, which always resolves upstream, so a setup that installs pays full price either way.

## Response `200`

The capability's JSON result.

- Result: The capability's result: the same object the MCP tool returns as structuredContent. Resources carry an `object` type discriminator and an immutable id; money is integer micro-dollars in `*_usd_micros` fields.
  - `object` string: The resource type, when the result is one resource.

## Other responses

- `400`: Malformed request: bad JSON, a parameter that does not parse, or a value outside its range. Code `invalid`.
- `401`: No bearer token, or one this API cannot verify. Code `unauthenticated`.
- `403`: Authenticated, but the token's scopes or the account's role do not reach this operation. The message names the scope word to request. Code `forbidden`.
- `404`: No such route. Code `not_found`; the body names the OpenAPI document.
- `422`: A well-formed request the platform declined: no such node, a cap reached, a state that forbids the move. The message says which, and what to do instead. Code `refused`.
- `429`: Over this door's per-customer budget. Wait `Retry-After` seconds. Code `rate_limited`.
- `500`: Our fault. Retry; if it persists, quote the request-id. Code `internal`.

---

[API](https://skmtc.dev/givemeanode/apis/autoresearch-customer-api.md) · [All operations](https://skmtc.dev/givemeanode/apis/autoresearch-customer-api/llms.txt) · [OpenAPI document](https://skmtc.dev/givemeanode/apis/autoresearch-customer-api/revisions/f88dcb70ac6c?raw)
