> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tensorlake.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Async SDK (Python)

> Use the AsyncSandbox class to drive sandboxes from asyncio code.

The Python SDK ships an async-native variant of the sandbox API on top of asyncio. Every method on the sync [`Sandbox`](/sandboxes/sdk-reference#sandbox-handle) handle has a one-to-one async counterpart on `AsyncSandbox`: same names, same parameters, just `async def` and awaited.

## When to use it

Reach for the async API when:

* You're driving multiple sandboxes concurrently (e.g. fanning out work with `asyncio.gather`).
* Your application is already asyncio-based (FastAPI, aiohttp, an LLM agent loop, etc.) and you don't want to mix in blocking calls.
* You're streaming output from many processes at once.

If you only ever use one sandbox at a time and your code is otherwise synchronous, the sync `Sandbox` API is simpler and equivalent.

## The shape of the API

```python theme={null}
from tensorlake.sandbox import AsyncSandbox
```

`AsyncSandbox` is the runtime handle for a single sandbox. Use `await AsyncSandbox.create(...)` to provision and connect, `await AsyncSandbox.connect(sandbox_id)` to attach to an existing one, or `await AsyncSandbox.get_or_create(name, ...)` to reuse a named sandbox and create it on first use. Every instance method is awaited:

```python theme={null}
sandbox = await AsyncSandbox.create()
result = await sandbox.run("python", ["-c", "print('hello')"])
await sandbox.write_file("/workspace/data.csv", b"name,score\nAlice,95\n")
content = await sandbox.read_file("/workspace/data.csv")
```

Refer to the [SDK Reference](/sandboxes/sdk-reference) for the full method list; the names, parameters, and return types are identical to the sync API. The pages below walk through the same workflow with async syntax.

## Create and run

```python theme={null}
import asyncio
from tensorlake.sandbox import AsyncSandbox

async def main():
    sandbox = await AsyncSandbox.create(cpus=2.0, memory_mb=2048)
    try:
        result = await sandbox.run("python", ["-c", "print('hello')"])
        print(result.stdout)
    finally:
        await sandbox.terminate()

asyncio.run(main())
```

`AsyncSandbox` is also an async context manager. Use `async with` to terminate the sandbox automatically when the block exits:

```python theme={null}
async with await AsyncSandbox.create(cpus=2.0, memory_mb=2048) as sandbox:
    result = await sandbox.run("python", ["-c", "print('hello')"])
    print(result.stdout)
# sandbox is terminated here
```

## Run many sandboxes in parallel

The async API is designed for fan-out. Use `asyncio.gather` to start and run sandboxes concurrently:

```python theme={null}
import asyncio
from tensorlake.sandbox import AsyncSandbox

async def evaluate(prompt: str) -> str:
    async with await AsyncSandbox.create(cpus=1.0, memory_mb=1024) as sandbox:
        result = await sandbox.run("python", ["-c", prompt])
        return result.stdout

async def main():
    prompts = [
        "print(2 + 2)",
        "print(sum(range(100)))",
        "import math; print(math.pi)",
    ]
    outputs = await asyncio.gather(*(evaluate(p) for p in prompts))
    for out in outputs:
        print(out.strip())

asyncio.run(main())
```

Each `evaluate` call creates, executes against, and terminates its own sandbox in parallel with the others.

## Get or create a named sandbox

Use `AsyncSandbox.get_or_create()` when concurrent tasks may request the same named sandbox. The method creates the sandbox on first use, connects to it on later calls, resolves concurrent create conflicts, and resumes it when suspended.

```python theme={null}
import asyncio
from tensorlake.sandbox import AsyncSandbox


async def session_sandbox(session_id: str) -> AsyncSandbox:
    return await AsyncSandbox.get_or_create(
        f"agent-{session_id}",
        timeout_secs=600,
    )


async def main():
    sandbox = await session_sandbox("session-123")
    result = await sandbox.run("python", ["-c", "print('ready')"])
    print(result.stdout)


asyncio.run(main())
```

Creation options apply only when the name is free. Pass `resume=False` to return an existing suspended sandbox without resuming it; call `await sandbox.resume()` before running commands against that handle. See [Get or create in the SDK Reference](/sandboxes/sdk-reference#get-or-create) for the complete behavior and limitations.

## Connect to an existing sandbox

Reattach to a named sandbox after `resume`, or operate on a sandbox another process created:

```python theme={null}
sandbox = await AsyncSandbox.connect("my-env")
info = await sandbox.info()
print(info.sandbox_id)  # sandbox.sandbox_id is now populated too
```

<Note>
  Unlike the sync `Sandbox.sandbox_id` property, which transparently fetches
  sandbox info on first access, the async `AsyncSandbox.sandbox_id` cannot
  block on a network call. Call `await sandbox.info()` (or any other awaited
  method that resolves the sandbox, like `status()`) once before reading
  `sandbox.sandbox_id` on a freshly connected handle.
</Note>

## Background processes and streaming output

Start a process, keep the handle, and collect its output once it finishes:

```python theme={null}
proc = await sandbox.start_process("python", ["-c", """
import time
for i in range(5):
    print(f'tick {i}')
    time.sleep(1)
"""])
print(proc.pid)

# follow_output blocks until the process exits, then returns a TracedIterator
# of the captured events you can iterate normally.
events = await sandbox.follow_output(proc.pid)
for event in events:
    print(event.line, end="")
```

For long-running processes you want to stop yourself, send a signal directly. Don't `follow_output` first, since it would block waiting for the process to exit:

```python theme={null}
import signal

proc = await sandbox.start_process("python", ["-m", "http.server", "8080"])
# ... do work that talks to the server ...
await sandbox.send_signal(proc.pid, signal.SIGTERM)
```

## File operations

```python theme={null}
await sandbox.write_file("/workspace/data.csv", b"name,score\nAlice,95\n")
content = await sandbox.read_file("/workspace/data.csv")
print(content.value.decode("utf-8"))

listing = await sandbox.list_directory("/workspace")
for entry in listing.value.entries:
    print(entry.name, entry.is_dir, entry.size)
```

## Suspend, resume, and snapshot

Suspend and resume require a named sandbox, so pass `name=` at creation time. `checkpoint` works on any sandbox, including ephemeral ones.

```python theme={null}
sandbox = await AsyncSandbox.create(name="my-env", cpus=1.0)
await sandbox.suspend()
await sandbox.resume()

snapshot = await sandbox.checkpoint()
restored = await AsyncSandbox.create(snapshot_id=snapshot.snapshot_id)
```

## Learn more

<CardGroup cols={2}>
  <Card title="SDK Reference" icon="rectangle-code" href="/sandboxes/sdk-reference">
    Full method list that applies to both sync and async APIs.
  </Card>

  <Card title="Lifecycle" icon="arrows-spin" href="/sandboxes/lifecycle">
    State machine, suspend/resume, timeouts.
  </Card>

  <Card title="Commands & Processes" icon="terminal" href="/sandboxes/commands">
    Run commands, capture output, and manage background processes.
  </Card>

  <Card title="Snapshots" icon="camera" href="/sandboxes/snapshots">
    Capture and restore full VM state.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.