Skip to main content

VoidRun Python SDK

Python client for VoidRun AI sandboxes: run commands, manage files, use pseudo-terminals, stream output, and execute multi-language code in isolated environments.

The high-level API is aligned with the official TypeScript SDK (create_sandbox, list_sandboxes, remove_sandbox, Sandbox.run_code, CodeExecutionResult, and shared defaults).

PyPI version License: MIT

Table of contents

Features

  • Sandbox lifecycle: Create, list, fetch, start/sleep/wake (stop/pause→sleep, resume→wake), and remove sandboxes (sync and async).
  • Shell execution: synchronous exec with optional ExecRequest (no background flag); SSE streaming via exec_stream; detached processes via .commands.run.
  • Code interpreter: run_code / interpreter.run return a structured CodeExecutionResult (stdout, stderr, success, exit_code, results, logs).
  • Files: Create, upload, download, list, move, copy, compress, extract, permissions, search, disk usage.
  • File watching: WebSocket-backed directory watches (async).
  • PTY: Ephemeral and persistent terminal sessions, resize, run_command with prompt detection.
  • Background commands: Run, list, attach, wait, kill long-running processes.

Requirements

  • Python 3.9+
  • Dependencies: see pyproject.toml (Pydantic v2, httpx, urllib3, websockets, python-dotenv, etc.).

Installation

pip install voidrun

With Poetry:

poetry add voidrun

From this repository (editable install for development):

cd py-sdk
pip install -e .

Configuration

VoidRun and AsyncVoidRun require an API key. The API base URL defaults to the hosted endpoint (DEFAULT_API_BASE_URL, aligned with the TypeScript SDK and OpenAPI servers). Set VR_API_URL or API_URL, or pass base_url=, only for self-hosted deployments.

Constructor

from voidrun import VoidRun

vr = VoidRun(
    api_key="your-api-key",  # optional if VR_API_KEY / API_KEY is set
)

Environment variables

Variable Purpose
VR_API_KEY or API_KEY API key (required unless passed to the constructor).
VR_API_URL or API_URL (Self-hosted only.) Overrides the default API base URL.

Defaults (aligned with TypeScript SDK)

Exported from voidrun:

Constant Value Meaning
DEFAULT_API_BASE_URL https://api.void-run.com/api Default API host when VR_API_URL / API_URL are unset.
DEFAULT_SANDBOX_IMAGE "code" Default image id when creating a sandbox without image=.
DEFAULT_SANDBOX_CPU 1 Default CPU count.
DEFAULT_SANDBOX_MEM 1024 Default memory in MB.

For self-hosted VoidRun, set VR_API_URL (or API_URL) to your instance’s API root (including /api if that is how your server is mounted).

Quick start

from voidrun import VoidRun

vr = VoidRun()  # uses VR_API_KEY; default base URL unless VR_API_URL / API_URL is set

sandbox = vr.create_sandbox(
    mem=1024,
    cpu=1,
    labels={"env": "prod", "team": "api"},
    publish_ports=[8080],  # expose ports as public URLs (set at create time only)
)

result = sandbox.exec('echo "Hello from VoidRun"')
# Exec returns VoidRunResponse whose .data is ExecResponseData
print(result.data.stdout)
print(sandbox.labels)
print(sandbox.publish_ports)

# Public URLs are only available for ports declared at creation
print(sandbox.get_public_urls())

listed = vr.list_sandboxes(labels={"env": "prod"})
print(listed.meta.total)

sandbox.remove()

publish_ports (alias publishPorts) must be set at creation to expose ports as public HTTPS URLs. Pass up to 4 ports (each 1–65535, no duplicates); the created sandbox echoes them on sandbox.publish_ports. Ports not published at creation are not reachable via get_public_urls().

Async

import asyncio
from voidrun import AsyncVoidRun

async def main():
    async with AsyncVoidRun() as vr:
        sandbox = await vr.create_sandbox(mem=1024, cpu=1)
        result = sandbox.exec('echo "Hello"')
        print(result.data.stdout)
        await sandbox.remove_async()

asyncio.run(main())

Context managers (auto cleanup)

Sync: exiting the block calls remove(). Async: __aexit__ calls remove_async().

with vr.create_sandbox() as sandbox:
    print(sandbox.id)
async with await vr.create_sandbox() as sandbox:
    print(sandbox.id)

Parity with the TypeScript SDK

Use the same mental model as @voidrun/sdk (or the internal TS client):

TypeScript (concept) Python
createSandbox VoidRun.create_sandbox / await AsyncVoidRun.create_sandbox
getSandbox get_sandbox
listSandboxes list_sandboxes → ListSandboxesResult
removeSandbox remove_sandbox or sandbox.remove()
sandbox.runCode sandbox.run_code
CodeExecutionResult CodeExecutionResult (Pydantic model)
CodeInterpreter CodeInterpreter (alias: Interpreter on sandbox.interpreter)

Listing sandboxes returns a ListSandboxesResult with:

  • .sandboxes: list of Sandbox instances
  • .meta: ListSandboxesMeta (total, page, limit, total_pages)

Core concepts

VoidRun / AsyncVoidRun

Recommended methods

  • create_sandbox(...) → Sandbox (optional labels=, publish_ports=)
  • get_sandbox(sandbox_id) → Sandbox
  • list_sandboxes(page=..., limit=..., labels=...) → ListSandboxesResult
  • remove_sandbox(sandbox_id) → None

AsyncVoidRun exposes the same names with await and provides aclose() (and async with support) to shut down the thread pool used for API calls.

Keyword aliases

Create options accept both snake_case and camelCase where noted in code (e.g. env_vars / envVars, org_id / orgId).

Sandbox

Notable attributes: id, name, cpu, mem, org_id, status, env_vars, image, region, ref_id, auto_sleep, disk_mb, labels, publish_ports, created_at, created_by.

Sub-clients:

  • .fs: file operations
  • .pty: pseudo-terminal
  • .interpreter: CodeInterpreter
  • .commands: background processes

Lifecycle:

  • start, sleep, wake (and *_async variants)
  • stop / pause alias sleep; resume aliases wake
  • remove() / remove_async(): delete sandbox on the API
  • delete() / delete_async(): deprecated aliases for remove

info() returns self (same idea as TS sandbox.info()).

Code execution and interpreter

sandbox.exec

Accepts a command string, or keyword command=, or a full ExecRequest:

from voidrun.api_client.models.exec_request import ExecRequest

r = sandbox.exec("uname -a")
r = sandbox.exec(command="pwd", cwd="/tmp", timeout=60)
r = sandbox.exec(ExecRequest(command="ls", cwd="/"))

Return type: VoidRunResponse[ExecResponseData]: the API’s outer ExecResponse envelope is unwrapped so r.data is ExecResponseData (stdout / stderr / exit_code):

print(r.data.stdout, r.data.stderr, r.data.exit_code)

sandbox.exec is synchronous only — it waits for the command. Do not use a background flag on ExecRequest; for a PID and long-running processes, use sandbox.commands.run (Background commands).

Streaming (exec_stream)

Provide callbacks for SSE events (on_stdout, on_stderr, on_exit, on_error).

Interpreter and run_code

sandbox.interpreter is a CodeInterpreter. Both interpreter.run(...) and sandbox.run_code(...) return CodeExecutionResult directly (no .data nesting):

result = sandbox.run_code("print(2 + 2)", language="python")
print(result.stdout.strip())   # "4"
print(result.success)

result = sandbox.interpreter.run("console.log(1+1)", language="javascript")

Supported languages (typical): python, javascript, typescript, node, bash, sh (as supported by your VoidRun deployment).

Background commands

run_result = sandbox.commands.run(
    "sleep 5 && echo done",
    {"DEBUG": "true"},
    "/tmp",
    0,
)
print(run_result.data.pid)

sandbox.commands.list()
sandbox.commands.connect(pid, on_stdout=..., on_stderr=..., on_exit=...)
sandbox.commands.wait(pid)
sandbox.commands.kill(pid)

Response shapes follow the generated OpenAPI models; access fields via .data on VoidRunResponse where applicable.

File system

sandbox.fs.create_file("/tmp/hello.txt")
sandbox.fs.upload_file("/tmp/hello.txt", "Hello, World!")
sandbox.fs.upload_file_from_path("/tmp/remote.txt", "/local/file.txt")

data = sandbox.fs.download_file("/tmp/hello.txt")
sandbox.fs.delete_file("/tmp/hello.txt")

result = sandbox.fs.list_files("/tmp")
files = result.data

sandbox.fs.stat_file("/tmp/hello.txt")
sandbox.fs.create_directory("/tmp/mydir")
sandbox.fs.move_file("/tmp/a.txt", "/tmp/b.txt")
sandbox.fs.copy_file("/tmp/a.txt", "/tmp/copy.txt")
sandbox.fs.change_permissions("/tmp/a.txt", "755")

sandbox.fs.head_tail("/tmp/log.txt", head=True, lines=10)
sandbox.fs.search_files("/tmp", "*.txt")
sandbox.fs.disk_usage("/tmp")

archive = sandbox.fs.compress_file("/tmp", "tar.gz")
sandbox.fs.extract_archive("/tmp/archive.tar.gz", "/tmp/extracted")

File watching (async)

import asyncio
from voidrun import AsyncVoidRun

async def watch_tmp():
    async with AsyncVoidRun() as vr:
        sandbox = await vr.create_sandbox()

        watcher = await sandbox.fs.watch(
            "/app",
            recursive=True,
            on_event=lambda evt: print(evt.get("path"), evt.get("type")),
            on_error=lambda err: print("watch error:", err),
        )

        await asyncio.sleep(60)
        watcher.close()
        await sandbox.remove_async()

asyncio.run(watch_tmp())

Pseudo-terminal (PTY)

Ephemeral session

import asyncio
from voidrun import AsyncVoidRun

async def ephemeral():
    async with AsyncVoidRun() as vr:
        sandbox = await vr.create_sandbox()
        pty = await sandbox.pty.connect(
            on_data=lambda data: print(data, end=""),
            on_error=lambda err: print("PTY error:", err),
        )
        pty.send_input('echo "Hello"\n')
        await asyncio.sleep(2)
        await pty.close()
        await sandbox.remove_async()

asyncio.run(ephemeral())

Persistent session

async def persistent():
    async with AsyncVoidRun() as vr:
        sandbox = await vr.create_sandbox()
        response = sandbox.pty.create_session()
        session_id = response.data.data.session_id

        pty = await sandbox.pty.connect(
            session_id=session_id,
            on_data=lambda data: print(data, end=""),
        )
        pty.send_input('echo "Hello"\n')
        await pty.close()

        reconnected = await sandbox.pty.connect(
            session_id=session_id,
            on_data=lambda data: print(data, end=""),
        )
        await reconnected.close()

        sandbox.pty.delete_session(session_id)
        await sandbox.remove_async()

Helpers

# After connect(...)
output = await pty.run_command("ls -la", timeout=5000, prompt="# ")
await pty.resize(80, 24)

sandbox.pty.list()
sandbox.pty.delete_session(session_id)

API reference (summary)

Exports (from voidrun import ...)

VoidRun, AsyncVoidRun, Sandbox, CodeInterpreter, Interpreter (alias), CodeExecutionResult, ListSandboxesResult, ListSandboxesMeta, DEFAULT_SANDBOX_*.

CodeExecutionResult

Field Type Description
success bool Derived from exit code.
stdout / stderr str Combined streams.
exit_code int | None Process exit code.
results Any Parsed output when applicable.
error str | None Error hint (often stderr).
logs dict e.g. stdout / stderr line lists.

OpenAPI client

The package includes a generated voidrun.api_client module. Regenerate it when the VoidRun OpenAPI specification changes (same spec as other official clients). After regen, confirm sandbox status values still match the server (e.g. running, stopped, paused, error, killed, deleted, …).

Runnable examples

The examples/ directory contains scripts for sync/async usage, exec, FS, lifecycle, PTY, background commands, and the code interpreter.

Run all examples (loads py-sdk/.env if present, sets PYTHONPATH):

chmod +x scripts/run_all_examples.sh   # once
./scripts/run_all_examples.sh

The script exits with status 1 if any example fails (useful for CI).

Single example

From the py-sdk directory (with PYTHONPATH including the repo root, same as the batch script):

export VR_API_KEY="your-api-key"
export PYTHONPATH="$(pwd)"

python3 examples/sync_usage.py
python3 examples/async_usage.py

Use an API key from the same VoidRun deployment as the API host (hosted default, or set VR_API_URL for self-hosted).

Development and tests

cd py-sdk
pip install -e .
pip install pytest pytest-asyncio pytest-mock

pytest tests/

Hatch shortcut:

hatch run test
hatch run all-examples   # runs scripts/run_all_examples.sh

Building and publishing

pip install build
python -m build

Or with Poetry: poetry build / poetry publish. See pyproject.toml for package metadata.

Error handling

Failures raise exceptions from the OpenAPI client (for example ApiException) with HTTP status and body. Parse the error body for error and details fields returned by the VoidRun API.

from voidrun import VoidRun
from voidrun.api_client.rest import ApiException

vr = VoidRun()

try:
    vr.get_sandbox("nonexistent-id")
except ApiException as e:
    print(e.status, e.body)

Troubleshooting

"API key is required …"

Set VR_API_KEY or pass api_key= to the constructor.

"Base URL is required …"

You cleared the base URL (for example empty VR_API_URL). Omit the variable to use the packaged default, or set VR_API_URL / API_URL (self-hosted) with the correct prefix (often /api).

Unauthorized / invalid API key

The key must match the VoidRun API you are calling (hosted default host or your self-hosted VR_API_URL). A local .env pointing at localhost with a cloud key (or vice versa) will return 401.

Sandbox creation failures

Respect minimum CPU and memory enforced by your org/plan. Defaults are 1 CPU and 1024 MB; increase if the API rejects smaller values.

PTY timeouts

Increase timeout in run_command, or allow more time before closing the session.

File not found

List parent directories with sandbox.fs.list_files and confirm paths inside the sandbox.

Contributing, license, support

Contributions are welcome; see the repository’s contributing guidelines if present.


Made with care by VoidRun.

Metadata

Release files for voidrun 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for voidrun 0.3.0
File Size Uploaded Provenance Actions
voidrun-0.3.0.tar.gz 65.8 kB Without provenance Details for voidrun-0.3.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for voidrun 0.3.0
File Interpreter ABI Platform Actions
voidrun-0.3.0-py3-none-any.whl Python 3 none any Details for voidrun-0.3.0-py3-none-any.whl

Total release size: 221.8 kB

Release files / voidrun-0.3.0.tar.gz

Download URL voidrun-0.3.0.tar.gz
Size 65.8 kB
Tags Source
SHA-256 checksum
How to use checksums
7627d402f6f76f27fa7515348439f1346dbd112a7abc26a86331a9ba111563f3
BLAKE2b-256 checksum
How to use checksums
d46750e8d89e845b342784fe8790898a6604aa07cb957043d76faaed83cfd04a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.16

Release files / voidrun-0.3.0-py3-none-any.whl

Download URL voidrun-0.3.0-py3-none-any.whl
Size 156.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
89637479c17fb36af8026716d60e279d14ecf19fad02dfa0fae16a1a7f3b9ebd
BLAKE2b-256 checksum
How to use checksums
ecd56c5dc19e28c695bde99e3376c7d7f11795b4ce1285060f57391ddb61ef86
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.16

Security for voidrun 0.3.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page