DevelopSandboxes Early access

Short-lived GPU sandboxes,
created and destroyed by code.

Sandboxes are isolated CPU and GPU environments for AI agents: one per RL episode, eval case or CI run. Your code creates them, runs untrusted code inside, and each one ends when its time-to-live does. Metering stops with it.

Being built for the first cohort. Join by request.
PLT 0124 sandboxes from one call Illustrative
Running
9 / 24
Ended
15
Billed
12,779 sandbox·s
Snapshot at 12:00
fx.sandboxes.create(n=24, ttl="15m", network="closed")
  • Running
  • Ended
  • TTL at 15:00
  • Closed
  • Restricted while running

One call creates 24 sandboxes with a 15-minute TTL. At 12 minutes, 9 are running and 15 have exited. Five more exit before 15 minutes, when the TTL stops the last 4. From 15 minutes on, nothing runs and nothing is billed. 4 sandboxes were switched from closed to restricted networking while running. Billed in total: 14,025 sandbox-seconds.

§01 Capabilities Early access

Capabilities

TTL
A time-to-live on every sandbox
Every sandbox ends on time, even if your agent forgets.
§03Lifecycle
GPU
CPU or GPU
Attach a GPU from the instance catalog when the task needs one.
§04Placement
Network
Open, restricted or closed
Allow-list domains, or cut egress entirely.
§06Network
Isolation
A runtime per sandbox
On nodes no other account uses.
§06Isolation
Interfaces
Python, REST, MCP
Agents call the same API you do.
§05Interfaces
§02 Use cases Python · CLI · MCP · REST

One sandbox per task.

RL environments

Give every rollout its own environment. Create one sandbox per episode, step it from your trainer, and let the TTL clean up after it.

Preview API · subject to change
import fantasti

fx = fantasti.Client()         # reads FANTASTI_API_KEY

def rollout(policy, seed):
    # one sandbox per episode; its TTL cleans up after it
    sb = fx.sandboxes.create(image="ghcr.io/acme/env:latest",
                             ttl="15m", network="closed")
    obs = sb.exec(f"python env.py reset --seed {seed}").stdout
    total = 0.0
    for _ in range(MAX_STEPS):
        step = sb.exec(f"python env.py step {policy.act(obs)}")
        obs, reward, done = parse(step.stdout)
        total += reward
        if done:
            break
    return total               # nothing to tear down
$ fantasti sandbox create --image ghcr.io/acme/env:latest \
    --ttl 15m --network closed
$ fantasti sandbox exec sb_41c9 -- python env.py reset --seed 7

Output · illustrative

sb_41c9  placing   cpu · ttl 15m · network closed
sb_41c9  ready
sb_41c9  running   python env.py reset --seed 7
sb_41c9  exit 0    {"obs": [0.02, -0.41, 0.03, 0.27]}

Evals

Run each eval case in a clean environment with the network closed, so nothing leaks or caches between cases.

Preview API · subject to change
import fantasti

fx = fantasti.Client()         # reads FANTASTI_API_KEY

cases = load_cases("evals/swe-tasks.jsonl")

# one clean sandbox per case: nothing leaks or caches between
boxes = fx.sandboxes.create(n=len(cases), gpu="L40S",
                            image="ghcr.io/acme/eval:latest",
                            ttl="20m", network="closed")

results = [box.exec(case.command)
           for box, case in zip(boxes, cases)]
passed = sum(run.exit_code == 0 for run in results)
print(f"{passed} of {len(cases)} cases passed")
$ fantasti sandbox create --gpu L40S --ttl 20m \
    --network closed --image ghcr.io/acme/eval:latest
$ fantasti sandbox exec sb_7q2m -- python grade.py --case 0412

Output · illustrative

sb_7q2m  placing   L40S:1 · ttl 20m · network closed
sb_7q2m  ready
sb_7q2m  running   python grade.py --case 0412
sb_7q2m  exit 1    case 0412 · 3 of 5 checks passed

GPU CI/CD

Run GPU tests on every pull request without keeping a GPU machine on. The sandbox ends when the job does.

Preview API · subject to change
import sys
import fantasti

fx = fantasti.Client()         # reads FANTASTI_API_KEY

# a fresh GPU for this pull request; a hung job ends at the TTL
with fx.sandboxes.create(
    gpu="L40S", image="ghcr.io/acme/ci:latest", ttl="30m",
    network="restricted",
    allow_domains=["pypi.org", "files.pythonhosted.org"],
) as sb:
    run = sb.exec("pytest -q tests/gpu", timeout="20m")
    print(run.stdout[-2000:])

sys.exit(run.exit_code)        # the sandbox has already ended
$ fantasti sandbox create --gpu L40S --ttl 30m --network closed
$ fantasti sandbox exec sb_41c9 -- pytest -q tests/gpu
$ fantasti sandbox delete sb_41c9

Output · illustrative

sb_41c9  placing   L40S:1 · ttl 30m · network closed
sb_41c9  ready
sb_41c9  running   pytest -q tests/gpu
sb_41c9  exit 0    212 passed
sb_41c9  ended     disk deleted · metering stopped

Untrusted agent code

Execute code your agent wrote with the network closed and a short TTL, on nodes isolated from other accounts.

Preview API · subject to change
import fantasti

fx = fantasti.Client()         # reads FANTASTI_API_KEY

with fx.sandboxes.create(
    gpu="L40S", ttl="15m", network="closed",
) as sb:
    sb.files.write("/work/patch.py", patch)
    run = sb.exec("python /work/patch.py --check", timeout="5m")
    print(run.exit_code, run.stdout[-2000:])
# ends here, or at its TTL, whichever comes sooner
{
  "mcpServers": {
    "fantasti": {
      "type": "http",
      "url": "https://mcp.fantasti.ai/mcp"
    }
  }
}

Tools · early access

sandboxes_create
sandboxes_exec
sandboxes_read_file
sandboxes_write_file
sandboxes_delete        asks for confirmation
curl -X POST https://api.fantasti.ai/v1/sandboxes \
  -H "Authorization: Bearer $FANTASTI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "gpu": "L40S", "ttl": "15m", "network": "closed" }'
# { "id": "sb_41c9…", "status": "placing" }
§03 Lifecycle Four steps

Created by code.
Ended by the clock.

A sandbox moves through four steps, each visible in the API and on your statement.

  1. 01 Status: placing then ready

    Create

    Ask for a sandbox with a GPU, an image, a network mode and a TTL.

    fx.sandboxes.create(gpu="L40S", ttl="15m")
  2. 02 Status: running

    Run

    Run commands and stream their output.

    sb.exec("pytest -q")
  3. 03 Status: running

    Exchange

    Write inputs in and read results out.

    sb.files.read("/work/out.json")
  4. 04 Status: ended

    End

    The TTL ends it, or you delete it sooner. Its disk is deleted with it.

    sb.delete()

0 seconds metered after the time-to-live.

Every sandbox, by its TTL
EQ 01 Source · Sandboxes API preview

In the illustrative run at the top of this page, the clock stops 4 sandboxes at 15:00. Nothing runs and nothing is metered from then on.

§04 Placement Illustrative

Placed in your own pool.

Sandboxes run on a node pool reserved for your account, inside your isolated cluster. The Fantasti Orchestrator places each sandbox on a node with the GPU it asks for and keeps the pool sized to your request rate. Sandboxes from different accounts never share a node.

How capacity is placed

Pool
Reserved for your account
Nodes
Never shared between accounts
Size
Follows your request rate
How one sandbox request is placed. Illustrative.

One call asks for 12 sandboxes with one L40S each, a 15m time-to-live and a closed network. The Fantasti Orchestrator checks the request against the limits your admins set (200 concurrent sandboxes, a maximum time-to-live of 60m) and places the sandboxes on 3 nodes of the node pool reserved for your account, 4 on each. One of them, sb_41c9, sits on node-1 with its own kernel and a closed network; it ends at its time-to-live and its disk is deleted with it. The pool gains a node when your request rate rises. Another account's pool never runs your sandboxes. Connections: Orchestrator to Another account's pool: Never placed here; policy.yaml to Orchestrator; fx.sandboxes.create to Orchestrator; Orchestrator to node-1; Orchestrator to node-2; Orchestrator to node-3; node-1 to sb_41c9.

Node poolReserved for your account
  • Requestfx.sandboxes.create
    n=12,gpu="L40S",image="acme/eval:latest",ttl="15m",network="closed"
  • OrchestratorChecks
    GPU
    L40S × 12
    concurrent
    12 of 200
    ttl
    15m of 60m
    network
    closed
    capacity
    on_demand
  • node-1L40S:4
    4 sandboxes
  • node-2L40S:4
    4 sandboxes
  • node-3L40S:4
    4 sandboxes
  • sb_41c9Running
    node
    node-1 · GPU 0
    runtime
    its own kernel
    network
    closed
    disk
    deleted at end
  • node-4L40S:4

    Added as your request rate rises.

  • Another account's pool
    none of yours

    Its nodes run only its ownsandboxes. Yours are neverplaced there.

  • Limitspolicy.yamlSet by admins
    limits: sandboxes: concurrent: 200 max_ttl: 60m
Node poolReserved for your account
  • Requestfx.sandboxes.create
    n=12,gpu="L40S",image="acme/eval:latest",ttl="15m",network="closed"
  • OrchestratorChecks
    GPU
    L40S × 12
    concurrent
    12 of 200
    ttl
    15m of 60m
    network
    closed
    capacity
    on_demand
  • node-1L40S:4
    4 sandboxes
  • node-2L40S:4
    4 sandboxes
  • node-3L40S:4
    4 sandboxes
  • sb_41c9Running
    node
    node-1 · GPU 0
    runtime
    its own kernel
    network
    closed
    disk
    deleted at end
  • node-4L40S:4

    Added as your request raterises.

  • Limitspolicy.yamlSet by admins
    limits: sandboxes: concurrent: 200 max_ttl: 60m
  • Another account's pool
    none of yours

    Its nodes run only its own sandboxes.Yours are never placed there.

Limits it is checked againstNever placed here
  • Requestfx.sandboxes.create
    n=12,gpu="L40S",image="acme/eval:latest",ttl="15m",network="closed"
  • OrchestratorChecks
    GPU
    L40S × 12
    concurrent
    12 of 200
    ttl
    15m of 60m
    network
    closed
    capacity
    on_demand
  • node-11 of 3 · L40S:4
    4 sandboxes
  • sb_41c9Running
    node
    node-1 · GPU 0
    runtime
    its own kernel
    network
    closed
    disk
    deleted at end
  • node-4L40S:4

    Added as your request rate rises.

  • Limitspolicy.yamlSet by admins
    limits: sandboxes: concurrent: 200 max_ttl: 60m
  • Another account's pool
    none of yours

    Its nodes run only its own sandboxes. Yoursare never placed there.

Node pool · For your account only
  • This request
  • Limits it is checked against
  • Added as your request rate rises
  • Never shared
§05 Agent interfaces Preview API

The interface your agent already speaks.

  • 01

    Python

    The SDK your harness imports.

    Python
    import fantasti
    
    fx = fantasti.Client()
    
    with fx.sandboxes.create(
        gpu="L40S", ttl="15m", network="closed",
    ) as sb:
        sb.files.write("/work/patch.py", patch)
        run = sb.exec("python /work/patch.py")
        out = sb.files.read("/work/out.json")
    # ended: by exit, or by its TTL
    fx = fantasti.Client()
  • 02

    REST

    Any language, any runtime.

    REST
    curl -X POST \
      https://api.fantasti.ai/v1/sandboxes \
      -H "Authorization: Bearer $FANTASTI_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "gpu": "L40S", "ttl": "15m",
            "network": "closed" }'

    Response · illustrative

    { "id": "sb_41c9…",
      "status": "placing" }
    POST /v1/sandboxes
  • 03 Early access

    MCP

    For agents that call tools: create, run, read, delete. MCP server

    MCP
    {
      "mcpServers": {
        "fantasti": {
          "type": "http",
          "url": "https://mcp.fantasti.ai/mcp"
        }
      }
    }

    Session · illustrative

    > Run my evals on an L40S, no network.
    sandboxes_create     sb_7q2m  ready
    sandboxes_write_file /work/cases.jsonl
    sandboxes_exec       pytest -q  exit 0
    sandboxes_read_file  /work/out.json
    sandboxes_delete     confirm? yes ended
    "mcpServers": { "fantasti": … }
MCP tools
  • sandboxes_create
  • sandboxes_exec
  • sandboxes_read_file
  • sandboxes_write_file
  • sandboxes_delete
sandboxes_delete asks for confirmation.
Limits
Your agent works inside the limits your admins set: concurrent sandboxes, maximum time-to-live and GPU types. Its key cannot raise them.
Where limits are set
§06 Network Default mode · closed

Closed unless you open it.

Each sandbox has one of three network modes. Closed blocks all outbound traffic. Restricted allows the domains you list and nothing else. Open allows outbound traffic except to private ranges and the metadata address.

TAB 01Network policy by destination Source · Sandboxes API preview
Network policy by destination. Private networks and the cloud metadata address are blocked in every mode.
Destination
pypi.org AllowAllowBlock
files.pythonhosted.org AllowAllowBlock
github.com AllowBlockBlock
ghcr.io AllowBlockBlock
10.0.0.0/8 private BlockBlockBlock
169.254.169.254 cloud metadata BlockBlockBlock
0.0.0.0/0 anything else AllowBlockBlock
Open
network="open"
Restricted
allow_domains=["pypi.org", "files.pythonhosted.org"]
Closed
network="closed"
§07 Billing Fantasti Terms §3

Metered by the second.
Stopped by the clock.

A sandbox is metered per second from start to end, at the rate of the CPU or GPU it uses. When the TTL ends it, metering ends with it. Sandboxes appear on the same statement as the rest of your usage.

EQ 02Seconds billed for a 4 min 10 s task Source · Fantasti Terms §3
Seconds billed for a 4 min 10 s task.
GranularitySeconds charged
Per hour3,600 s
Per minute300 s
Per second250 s

Fantasti meters by the second and bills hourly (Terms §3). A 4 min 10 s task is charged 250 seconds.

§08 Compare 4 of 7 products

Pick the right product.

TAB 02Pick the right product Source · Fantasti
Pick the right product
Question Sandboxes Early access Workspaces Early access Serverless Early access GPU Instances
You bring An agent or test harness Your editor and code A container An image, or your own stack
Fantasti runs Short-lived isolated environments A dev environment that scales to a cluster One job or one endpoint One machine with 1 or 8 GPUs
Ends At its time-to-live When you stop it When the job exits or you stop the endpoint When you stop it or its term ends
Keeps Nothing; copy results out Your files Nothing on the container disk Its volumes
Capacity On-demand On-demand head; workers on-demand or spot On-demand or spot On-demand, spot, reserved
Use it when Code is untrusted or disposable You are writing or debugging The job is one container You need a GPU and root
Sheet
01 / 01
Title
Sandboxes: network, billing, fit
Reviewed
2026-10-09
§09 Questions

Questions about Sandboxes.

How long can a sandbox live?

Until the TTL you set. Early-access accounts have a maximum TTL set per account.

Can a sandbox use a GPU?

Yes. Request a GPU type from the instance catalog when you create it.

Do sandboxes share nodes with other accounts?

No. Your sandboxes run on nodes that belong to your account.

What happens when the TTL ends?

The sandbox stops and its disk is deleted. Copy results out before then.

Can my agent create sandboxes on its own?

Yes. It calls the same API you do, or the tools of the MCP server, inside the limits your admins set. Deleting a sandbox over MCP asks for confirmation.

Can I run sandboxes on spot?

No. Sandboxes run on on-demand capacity. Spot is for work that checkpoints and can wait.

How do I get access?

Sandboxes is in early access. Request access with your use case.

§10 Early access

Bring us your rollouts.

Access opens in cohorts. Companies can request a place in the first, and places are limited. Tell us about your RL, eval or agent workload.

A request reads200 × sandbox · L40S · ttl 15m · network closed

01Capacity

GPU

Choose every type that would work. The first listing with a list price drives the estimate.

GPU count
Capacity
Term
Start
Interconnect
Region

Any country or region your data must stay in.

What will you run? Framework, model size and how it scales.

02Contact

Use your company email address. Requests from personal email addresses join the waitlist for the next phase.

Optional. One email asks you to confirm the address before any announcement is sent.