Skip to main content

API Reference

Sandbox Class​

The main class for creating and managing sandboxes.

Class Methods​

Sandbox.create()​

Creates a new sandbox instance.

@classmethod
def create(
variant: str = "datalayer",
timeout: float | None = None,
environment: str | None = None,
gpu: str | None = None,
cpu: float | None = None,
memory: int | None = None,
env: dict[str, str] | None = None,
tags: dict[str, str] | None = None,
name: str | None = None,
network_policy: str | None = None,
allowed_hosts: list[str] | None = None,
examples: list[tuple[str, str]] | None = None,
config: SandboxConfig | None = None,
**kwargs,
) -> Sandbox

Parameters:

ParameterTypeDescription
variantstrSandbox type: "cloudflare", "coreweave", "datalayer", "daytona", "docker", "e2b", "eval", "google-colab", "jupyter-server", "kaggle", "modal", or "monty". Defaults to "datalayer".
timeoutfloatExecution timeout in seconds
environmentstrRuntime environment name
gpustrGPU type (e.g., "T4", "A100", "H100")
cpufloatNumber of CPU cores
memoryintMemory in MB
envdictEnvironment variables
tagsdictMetadata tags
namestrSandbox name
network_policystrNetwork access policy: "inherit", "none", "allowlist", "all"
allowed_hostslist[str]Hosts reachable when the policy is "allowlist"
exampleslist[tuple[str, str]](title, code) pairs the REPL lists under :examples
configSandboxConfigFull configuration object
**kwargsdictVariant-specific arguments handed to the sandbox constructor — template= (e2b), container_image= (coreweave), api_url= (cloudflare), snapshot_name= (datalayer), and the rest documented on each variant's page

Sandbox.from_id()​

Reconnects to an existing sandbox.

@classmethod
def from_id(sandbox_id: str, **kwargs) -> Sandbox

Datalayer only — it reconnects to a Datalayer runtime whatever else is running. **kwargs are that variant's: token=, run_url=.

Sandbox.list()​

Lists running sandboxes, as sandbox objects.

@classmethod
def list(tags: dict[str, str] | None = None, **kwargs) -> Iterator[Sandbox]

Datalayer only. For any other variant, ask its manager: get_manager(variant).list(), which answers with list[SandboxInfo] — see the CLI page.

Sandbox.list_environments()​

Lists available environments for a sandbox variant.

@classmethod
def list_environments(
variant: str = "datalayer",
**kwargs,
) -> list[SandboxEnvironment]

Parameters:

ParameterTypeDescription
variantstrSandbox type: "cloudflare", "coreweave", "datalayer", "daytona", "docker", "e2b", "eval", "google-colab", "jupyter-server", "kaggle", "modal", or "monty"
**kwargsdictVariant-specific arguments (e.g., credentials, run URL)

Legacy local-eval, local-docker, and local-jupyter variant names are not supported.

Instance Methods​

run_code()​

Executes Python code in the sandbox.

def run_code(
code: str,
language: str = "python",
context: Context | None = None,
on_stdout: Callable[[OutputMessage], None] | None = None,
on_stderr: Callable[[OutputMessage], None] | None = None,
on_result: Callable[[Result], None] | None = None,
on_error: Callable[[CodeError], None] | None = None,
envs: dict[str, str] | None = None,
timeout: float | None = None,
) -> ExecutionResult

run_code_streaming()​

Executes Python code and yields output/result/error events as they arrive.

def run_code_streaming(
code: str,
language: str = "python",
context: Context | None = None,
envs: dict[str, str] | None = None,
timeout: float | None = None,
) -> Iterator[OutputMessage | Result | CodeError]

run_code_streaming_async()​

Async version of run_code_streaming().

async def run_code_streaming_async(
code: str,
language: str = "python",
context: Context | None = None,
envs: dict[str, str] | None = None,
timeout: float | None = None,
) -> AsyncIterator[OutputMessage | Result | CodeError]

start()​

Starts the sandbox.

def start() -> None

stop()​

Stops the sandbox gracefully.

def stop() -> None

terminate() / kill()​

Datalayer only; both are aliases for stop(). Every other variant is stopped with stop() — or by leaving its with block.

def terminate() -> None
def kill() -> None

set_timeout()​

Extends the life of a running sandbox. Only two variants offer it, and the argument is not spelled the same in both.

def set_timeout(timeout_seconds: float) -> None # datalayer
def set_timeout(seconds: float) -> None # e2b

Properties​

PropertyTypeDescription
sandbox_idstr | NoneUnique sandbox identifier — None until start() has run
filesSandboxFilesystemFilesystem interface
commandsSandboxCommandsCommand execution interface
tagsdict[str, str]Metadata tags

CodeSandboxClient​

CodeSandboxClient is a high-level, variant-agnostic facade over a sandbox. It normalizes one-shot outcomes and streaming events across all variants.

Methods​

execute_code()​

def execute_code(
code: str,
language: str = "python",
timeout: float | None = None,
envs: dict[str, str] | None = None,
) -> CodeExecutionOutcome

execute_code_async()​

async def execute_code_async(
code: str,
language: str = "python",
timeout: float | None = None,
envs: dict[str, str] | None = None,
) -> CodeExecutionOutcome

execute_code_streaming()​

def execute_code_streaming(
code: str,
language: str = "python",
timeout: float | None = None,
envs: dict[str, str] | None = None,
) -> Iterator[OutputMessage | Result | CodeError]

execute_code_streaming_async()​

async def execute_code_streaming_async(
code: str,
language: str = "python",
timeout: float | None = None,
envs: dict[str, str] | None = None,
) -> AsyncIterator[OutputMessage | Result | CodeError]

Compatibility-free execution facade​

Consumers that need a Jupyter-shaped reply can stay on the sandbox client without accessing a variant's underlying kernel implementation:

reply = client.execute(code, timeout=60)
reply = client.execute_interactive(code, output_hook=handle_output)

The client also exposes variant-neutral lifecycle and variable operations:

client.start()
client.interrupt()
client.restart()
client.set_variable("name", value)
client.set_variables({"one": 1, "two": 2})
value = client.get_variable("name")
client.stop()

Properties​

PropertyDescription
idActive backend identifier, including a remote kernel ID when applicable
variantConfigured sandbox variant
configVariant-neutral sandbox configuration
infoRuntime sandbox information after startup
kernel_infoLanguage metadata without exposing the underlying kernel client
is_startedWhether the sandbox has been started

Outcome model​

CodeExecutionOutcome includes normalized fields such as:

  • success
  • execution_ok
  • stdout
  • stderr
  • results
  • execution_error
  • code_error
  • exit_code
  • interrupted

Contents attachments​

The Datalayer Contents service writes down what a sandbox is to be given as a ContentManifest; the sandbox honours it and answers with one PreparedAttachment per attachment, whichever provider is underneath. The models are importable from code_sandboxes.

Models​

ModelWhat it is
ContentManifestcontract_version, sandbox_uid, sandbox_provider, generated_at, attachments, and the sandbox's own short-lived credentials to Contents: contents_url, token
ContentAttachmentSpecOne attachment: uid, source_uid, revision_uid, mode (ro/rw), mount_path, delivery (mount, local-bridge, materialize, client, environment), required, access_mode, provider_resource_id, filesystem_primitives, materialize
MaterializeEntryOne file to fetch: source_url (signed), path (absolute or under mount_path), sha256, size
PreparedAttachmentuid, status (ready, degraded, failed), mount_path, provider_resource_id, capabilities, detail, error_code
ContentCapabilitiesprovider, mount, bucket_mount, materialize, client, local_bridge_mount (a LocalBridgeCapability), filesystem_primitives
ContentAttachmentErrorRaised by attach() for a required attachment that is not ready: uid, code, attachments

A degraded attachment is an optional one that could not be honoured; a failed one is required. The error_code is one of DELIVERY_UNSUPPORTED, MOUNT_NEEDS_RESTART, MOUNT_MISSING, MOUNT_PATH_MISSING, CREDENTIAL_DELIVERY_UNSUPPORTED, CHECKSUM_MISMATCH, FETCH_FAILED.

CodeSandboxClient methods​

def content_capabilities() -> ContentCapabilities
def attach(manifest: ContentManifest) -> list[PreparedAttachment]
def prepare_contents(manifest: ContentManifest) -> list[PreparedAttachment]
def reconcile_contents(manifest: ContentManifest) -> list[PreparedAttachment]
def attachment_status(uid: str) -> PreparedAttachment | None
def detach(uid: str) -> None
contents_location -> ManifestLocation | None

prepare_contents configures a sandbox that has not started (the environment, and the mounts a provider only makes at creation), starts it, writes the manifest inside, then prepares every attachment. attach is the same and raises ContentAttachmentError when a required attachment is not ready — leaving the sandbox running. reconcile_contents re-checks and repairs after a restart: a mount that is there is ready, a materialized file with the right digest is not fetched again, and no mount is ever made twice. Every method is safe to call again.

Inside the sandbox the manifest is written to /etc/datalayer/contents.json, or ~/.datalayer/contents.json when /etc cannot be written, and the environment is set — at creation where the provider takes it (Daytona, E2B, Modal) and in the kernel everywhere: DATALAYER_CONTENTS_MANIFEST, DATALAYER_CONTENTS_URL, DATALAYER_CONTENTS_TOKEN, DATALAYER_CONTENTS_TOKEN_FILE. The token never goes into the JSON; it is written to contents.token with mode 0600.

Sandbox hooks​

def content_capabilities() -> ContentCapabilities
def configure_contents(manifest: ContentManifest) -> None # before creation
def prepare_contents(manifest: ContentManifest) -> list[PreparedAttachment]
def reconcile_contents(manifest: ContentManifest) -> list[PreparedAttachment]
def attachment_status(uid: str) -> PreparedAttachment | None
def remove_attachment(uid: str) -> None

None of them is abstract. The base answers client only; an adapter overrides content_capabilities, configure_contents and the small _prepare_mount / _prepare_local_bridge / _forget_mount hooks rather than the walk over the manifest.

What each provider honours​

ProviderVolume mount (provider_resource_id)Shared filesystemBucket mountLocal bridgeMaterialize
datalayermade by the Operator before the pod starts; checked forsamesameClouder CSIfetched inside the sandbox
daytonaVolumeMount at creation; MOUNT_NEEDS_RESTART on a running sandboxDELIVERY_UNSUPPORTEDDELIVERY_UNSUPPORTEDnofetched inside the sandbox
e2bvolume_mounts at creation; MOUNT_NEEDS_RESTART on a running sandboxDELIVERY_UNSUPPORTEDDELIVERY_UNSUPPORTEDnofetched inside the sandbox
modalvolumes={path: Volume.from_name(id)} at creation; MOUNT_NEEDS_RESTART on a running sandboxDELIVERY_UNSUPPORTEDCREDENTIAL_DELIVERY_UNSUPPORTED — a CloudBucketMount needs the bucket credential handed to Modalnofetched inside the sandbox
othersDELIVERY_UNSUPPORTEDsamesamenoDELIVERY_UNSUPPORTED

Materialized files are fetched by the sandbox itself from the signed URLs, so the bytes never pass through the host; each is written beside its destination and moved into place once its digest checks out. detach removes materialized files and forgets a mount request; it never deletes a volume.

Kaggle and Colab Clients​

Code Sandboxes owns the provider-specific clients used by its managed notebook variants. They are exported from code_sandboxes:

from code_sandboxes import (
GoogleColabKernelClient,
KaggleExecutionResult,
KaggleKernelClient,
KaggleKernelExecutor,
parse_google_colab_channels_url,
parse_kaggle_channels_url,
)
  • KaggleKernelClient connects to an interactive Kaggle notebook kernel.
  • KaggleKernelExecutor submits and monitors Kaggle batch notebook jobs.
  • KaggleExecutionResult normalizes batch output into a Jupyter-like reply.
  • GoogleColabKernelClient connects to an already-running Google Colab kernel.
  • The parser helpers extract connection details from browser channels URLs.

See Kaggle and Google Colab for authentication and complete examples.


SandboxFilesystem​

File operations interface.

Methods​

read()​

def read(path: str) -> str

Reads file contents as a string.

read_bytes()​

def read_bytes(path: str) -> bytes

Reads file contents as bytes.

write()​

def write(path: str, content: str, make_dirs: bool = True) -> None

Writes text to a file, creating parent directories unless told not to.

write_bytes()​

def write_bytes(path: str, content: bytes, make_dirs: bool = True) -> None

Writes bytes to a file. write() handles text only.

list()​

def list(path: str = "/") -> list[FileInfo]

Lists directory contents.

mkdir()​

def mkdir(path: str, parents: bool = True) -> None

Creates a directory.

rm()​

def rm(path: str, recursive: bool = False) -> None

Removes a file or directory.

exists() / is_file() / is_dir()​

def exists(path: str) -> bool
def is_file(path: str) -> bool
def is_dir(path: str) -> bool

Asks about a path without reading it.

copy() / move()​

def copy(src: str, dst: str) -> None
def move(src: str, dst: str) -> None

get_info()​

def get_info(path: str) -> FileInfo

One entry's metadata, without listing its directory.

upload()​

def upload(local_path: str, remote_path: str) -> None

Uploads a local file to the sandbox.

download()​

def download(remote_path: str, local_path: str) -> None

Downloads a file from the sandbox.

FileInfo​

What list() and get_info() answer with.

@dataclass
class FileInfo:
name: str
path: str
type: FileType = FileType.FILE # FILE, DIRECTORY, SYMLINK
size: int = 0 # bytes, for files
modified: float = 0.0 # Unix timestamp
permissions: str = ""

# Convenience properties
@property
def is_file(self) -> bool: ...
@property
def is_directory(self) -> bool: ...

SandboxCommands​

Command execution interface.

Methods​

run()​

def run(
command: str,
cwd: str | None = None,
env: dict[str, str] | None = None,
timeout: float | None = None,
shell: bool = True,
) -> CommandResult

Runs a command and waits for completion.

exec()​

def exec(*args: str, **kwargs) -> ProcessHandle

Executes a command with streaming output.

spawn()​

def spawn(command: str, **kwargs) -> ProcessHandle

Starts a background process.

CommandResult​

@dataclass
class CommandResult:
exit_code: int = 0
stdout: str = ""
stderr: str = ""
duration: float = 0.0

@property
def success(self) -> bool: ... # exit_code == 0

ProcessHandle​

A process still running. stdout and stderr are iterators that yield as output arrives.

@dataclass
class ProcessHandle:
command: str
pid: int | None = None

@property
def stdout(self) -> Iterator[str]: ...
@property
def stderr(self) -> Iterator[str]: ...
@property
def returncode(self) -> int | None: ...

def read_stdout(self) -> str: ...
def read_stderr(self) -> str: ...
def wait(self, timeout: float | None = None) -> int: ...
def poll(self) -> int | None: ...
def terminate(self) -> None: ...
def kill(self) -> None: ...

Data Models (Pydantic)​

ExecutionResult​

Complete result of a code execution with detailed status information.

class ExecutionResult(BaseModel):
# Results and logs
results: list[Result]
logs: Logs

# Execution-level (infrastructure) status
execution_ok: bool = True # Did sandbox infrastructure work?
execution_error: str | None = None # Infrastructure failure details

# Code-level (user code) status
code_error: CodeError | None = None # Python exception info

# Process exit status (sys.exit)
exit_code: int | None = None # Exit code if code calls sys.exit()

# Metadata
execution_count: int = 0
context_id: str | None = None
started_at: float | None = None
completed_at: float | None = None
interrupted: bool = False

# Properties
@property
def success(self) -> bool:
"""True if execution_ok and no code_error and not interrupted and exit_code is 0 or None"""

@property
def duration(self) -> float | None:
"""Execution duration in seconds"""

@property
def text(self) -> str | None:
"""Main text result"""

@property
def stdout(self) -> str:
"""All stdout as single string"""

@property
def stderr(self) -> str:
"""All stderr as single string"""

Usage:

result = sandbox.run_code("x = 1 + 1; print(x)")

# Check infrastructure success
if not result.execution_ok:
print(f"Sandbox failed: {result.execution_error}")
# Check explicit process exit
elif result.exit_code not in (None, 0):
print(f"Process exited with code: {result.exit_code}")

# Check code-level error
elif result.code_error:
print(f"Python error: {result.code_error.name}: {result.code_error.value}")

# Success!
else:
print(f"Result: {result.text}")
print(f"Duration: {result.duration:.2f}s")

# Or use convenience property
if result.success:
print("Everything worked!")

CodeError​

Error information when Python code raises an exception.

class CodeError(BaseModel):
name: str # Error class name (e.g., "ValueError")
value: str # Error message
traceback: str = "" # Full traceback

SandboxEnvironment​

class SandboxEnvironment(BaseModel):
name: str
title: str
language: str = "python"
owner: str = "local"
visibility: str = "local"
burning_rate: float = 0.0
metadata: dict[str, Any] | None = None
cpu: str | None = None
memory: str | None = None
gpu: str | None = None
gpu_count: int | None = None
gpu_memory: int | None = None

Result​

A single execution result with multiple representations.

class Result(BaseModel):
data: dict[str, Any] # MIME type to content mapping
is_main_result: bool = False
extra: dict[str, Any] # Additional metadata

# Convenience properties
@property
def text(self) -> str | None: ... # text/plain
@property
def html(self) -> str | None: ... # text/html
@property
def json(self) -> Any: ... # application/json
@property
def png(self) -> str | None: ... # image/png (base64)
@property
def jpeg(self) -> str | None: ... # image/jpeg (base64)
@property
def svg(self) -> str | None: ... # image/svg+xml
@property
def markdown(self) -> str | None: ... # text/markdown

OutputMessage​

class OutputMessage(BaseModel):
line: str
timestamp: float = 0.0
error: bool = False # True for stderr

Logs​

class Logs(BaseModel):
stdout: list[OutputMessage]
stderr: list[OutputMessage]

@property
def stdout_text(self) -> str: ... # All stdout as string
@property
def stderr_text(self) -> str: ... # All stderr as string

Context​

class Context(BaseModel):
id: str
language: str = "python"
cwd: str | None = None
env: dict[str, str]

SandboxConfig​

class SandboxConfig(BaseModel):
timeout: float = 30.0
memory_limit: int | None = None
cpu_limit: float | None = None
environment: str = "ai-agents-env"
working_dir: str | None = None
env_vars: dict[str, str]
gpu: str | None = None
name: str | None = None
network_policy: str = "inherit" # "inherit", "none", "allowlist", "all"
allowed_hosts: list[str]
idle_timeout: float | None = None
max_lifetime: float = 86400.0 # 24 hours
examples: list[tuple[str, str]] # (title, code) pairs for the REPL

SandboxInfo​

class SandboxInfo(BaseModel):
id: str
variant: str
status: SandboxStatus = SandboxStatus.RUNNING
created_at: float = 0.0
end_at: float | None = None
config: SandboxConfig | None = None
name: str | None = None
metadata: dict[str, Any]
resources: ResourceConfig | None = None

@property
def remaining_time(self) -> float | None: ... # Seconds until termination

SnapshotInfo​

What create_snapshot() and list_snapshots() answer with — datalayer only.

class SnapshotInfo(BaseModel):
id: str
name: str
sandbox_id: str
created_at: float = 0.0
size: int = 0
description: str = ""

JupyterServerOptions​

What prepare_jupyter_server() is asked for — see Jupyter over Provider Ingress.

class JupyterServerOptions(BaseModel):
port: int = 8888
token: str | None = None # generated when omitted
install_if_missing: bool = True
install_timeout: float = 180.0

JupyterServerEndpoint​

What it answers with. headers authenticate the provider ingress and query authenticates Jupyter itself; both are secrets, and both are kept out of the repr.

class JupyterServerEndpoint(BaseModel):
port: int
http_url: str
websocket_url: str
headers: dict[str, str]
query: dict[str, str]

ResourceConfig​

class ResourceConfig(BaseModel):
cpu: float | None = None # CPU cores
memory: int | None = None # Memory in MB
gpu: str | None = None # GPU type ("T4", "A100", etc.)
gpu_count: int = 1 # Number of GPUs
disk: int | None = None # Disk in GB

Management API​

The verbs the CLI runs on, from Python. A manager exists per variant, and states what it can do rather than failing late.

from code_sandboxes import get_manager, manageable_variants

manageable_variants() # the variants a manager exists for
manager = get_manager("modal")
manager.capabilities # frozenset of the verbs this variant answers
manager.list() # list[SandboxInfo]
manager.get(sandbox_id) # SandboxInfo | None
manager.create(**kwargs) # SandboxInfo
manager.update(sandbox_id, **changes)
manager.delete(sandbox_id) # bool

A verb outside capabilities raises SandboxManagementError. See the CLI page for what each variant answers.

Provider Registry​

What a variant needs before it can run anything — credentials, and the extra that installs it. It reports what is present; it starts nothing and reads no secret's value.

from code_sandboxes import PROVIDERS, available_providers, get_provider

PROVIDERS # every provider declared
available_providers() # those whose credentials are on this machine
get_provider("daytona") # one, or None

Exceptions​

ExceptionDescription
SandboxErrorBase exception for all sandbox errors
SandboxTimeoutErrorExecution timed out
SandboxExecutionErrorCode execution failed
SandboxNotStartedErrorThe sandbox was used before start()
SandboxConnectionErrorConnection to sandbox failed
SandboxConfigurationErrorThe variant cannot be used as configured — a missing extra, or a setting it refuses
ContextNotFoundErrorNo such execution context
VariableNotFoundErrorNo such variable in the context
SandboxSnapshotErrorSnapshot operation failed
SandboxResourceErrorResource limit exceeded
SandboxAuthenticationErrorAuthentication failed
SandboxQuotaExceededErrorUsage quota exceeded