Skip to main content
InstaBox SDK

Python SDK for InstaBox — cloud machines for agents.

PyPI Python License

  • Sync (Client) and async (AsyncClient) clients on httpx
  • Typed models for machines, operations, snapshots, ports, templates and usage
  • Structured error hierarchy mirroring the API error codes
  • Automatic Idempotency-Key (create/start/stop/suspend/resume/fork/delete) and X-Confirm-Delete headers
  • Long-polling operations.wait() — no sleep loops
  • Machine pagination via iter_machines()
  • Streaming command output (exec_stream, SSE) and SSH tokens/certificates
  • Org & machine secrets, orgs, usage
  • SDK-level tool policy (ToolPolicy) and an optional local MCP server (instabox[mcp])

Install

uv add instabox           # from PyPI, once published
# or from a checkout:
uv sync

Quick start

from instabox import Client

client = Client(api_key="ibk_...")  # or os.environ["INSTABOX_API_KEY"]

# Create and wait for a machine
op = client.machines.create(name="dev", template_id="agent-browser")
op = client.operations.wait(op.id)  # long-polls, no sleeping

# Run a command
result = client.machines.exec("dev", cmd="echo hello")  # get by name or id
print(result.stdout)  # hello

# Files
client.machines.upload_file("dev", "/data/home/notes.txt", b"hi")
data = client.machines.download_file("dev", "/data/home/notes.txt")
listing = client.machines.read_file("dev", "/data/home")

# Lifecycle
client.machines.suspend("dev")  # pause (memory preserved)
client.machines.resume("dev")
op = client.machines.delete("dev")  # X-Confirm-Delete sent automatically
client.operations.wait(op.id)

# Snapshots, ports, account
snap = client.machines.snapshot("dev", "dev-base")
port = client.machines.publish_port("dev", 3000, name="Web app")
print(port.url)
usage = client.account.usage()

Async

from instabox import AsyncClient

async with AsyncClient(api_key="ibk_...") as client:
    op = await client.machines.create(name="dev")
    await client.operations.wait(op.id)
    result = await client.machines.exec(op.machine_id, cmd="uname -a")
    async for machine in client.machines.iter_machines(metadata="task:review"):
        print(machine.id, machine.state)

Machine handles (for agents / models)

Get a stateful handle once and call machine methods directly — mirrors the TS SDK's Sandbox object:

machine = client.machine("dev")        # by name or id; async: client.machine(...)
machine.connect()                      # resume/start if needed, wait until running
result = machine.exec("echo hello")    # does not raise on non-zero exit
machine.run("pytest -q")               # raises RuntimeError on non-zero exit
for kind, chunk in machine.exec_stream("npm test"):
    print(chunk, end="") if kind == "stdout" else None

machine.write_file("/data/home/note.txt", b"hi")
machine.download_file("/data/home/note.txt")
machine.publish_port(3000)
machine.set_metadata({"task": "review"})
machine.wait_operation(machine.start())   # wait for a lifecycle operation
machine.stop()
machine.delete()

Available methods: refresh, id, is_running, wait_until_ready, connect, start, stop, suspend, resume, restart, delete, wait_operation, exec, run, exec_stream, read_file, write_file, download_file, publish_port, snapshot, ssh_token, ssh_certificate, set_metadata, update.

Handles delegate to the client's wrapped resources, so a client-level ToolPolicy (see below) applies to every handle method.

Streaming output

for kind, payload in client.machines.exec_stream("dev", cmd="npm test"):
    if kind in ("stdout", "stderr"):
        print(payload, end="")
    elif kind == "exit":
        print("exit code:", payload["exitCode"])

AsyncClient mirrors this with await-free async for via exec_stream.

SSH, secrets, orgs

token = client.machines.ssh_tokens.create("dev", name="ci")  # token.token — secret, shown once
print(token.command)  # ready-to-run ssh string
client.machines.ssh_tokens.revoke_all("dev")

client.machines.secrets.update("dev", secrets=["DB_PASS"])  # inject org secrets
client.org_secrets.put("org_id", "DB_PASS", "hunter2", delivery="proxy", hosts=["*"])
orgs = client.account.orgs()

Restricting what a client may do

Two layers:

1. API key scopes (server-side, authoritative). Keys carry scopes — machines:read, machines:write, machines:delete, images:read/write/delete, workspace:admin (write includes read). Create a scoped key in the dashboard or instabox keys create --scope machines:read,images:read; calls outside the scopes fail with 403 missing_scope. To stop an agent from creating or deleting machines, give it a key without machines:write/machines:delete.

2. SDK tool policy (client-side). Allow/deny resource.method operation names — checked before any request is made:

from instabox import Client, ToolPolicy

client = Client(
    api_key=key,
    policy=ToolPolicy(
        allow=[
            "machines.exec",
            "machines.read_file",
            "machines.write_file",
            "machines.get",
            "machines.list",
            "operations.*",
        ],
        deny=["machines.delete"],  # deny always wins
    ),
)
client.machines.exec("dev", cmd="ls")  # ok
client.machines.delete("dev")  # PermissionDeniedError (sdk_not_allowed)

MCP server for AI models

InstaBox ships a hosted MCP server at https://api.instabox.sh/mcp (instabox mcp init <client>), but it has no per-tool toggles — access is governed only by API key scopes. This SDK adds a local stdio MCP server with a per-tool allowlist on top:

uv add "instabox[mcp]"
export INSTABOX_API_KEY=ibk_...
# default: safe tools only (exec, files, read) — create/delete/snapshot are OFF
export INSTABOX_ALLOWED_TOOLS="exec,read_file,write_file,list_machines,get_machine"
instabox-mcp

Register with a client, e.g. Claude Code:

claude mcp add instabox -- uvx --from "instabox[mcp]" instabox-mcp

Errors

Every API error raises an APIError subclass; the machine-readable code and the server's retryable flag are always available:

from instabox import exceptions, Client

client = Client(api_key="ibk_...")
try:
    client.machines.exec("dev", cmd="true")
except exceptions.RateLimitError as e:
    if e.retryable and e.retry_after_seconds:
        time.sleep(e.retry_after_seconds)
except exceptions.ConflictError as e:
    ...

Status → exception: 400 BadRequestError, 401 AuthenticationError, 402 PaymentRequiredError, 403 PermissionDeniedError, 404 NotFoundError, 409 ConflictError, 429 RateLimitError, 5xx ServerError.

Development

uv sync          # install deps (dev group included)
uv run pytest    # run tests
uv run ruff check . && uv run ruff format --check .
uv run mypy      # strict type checking

Reference

Metadata

Release files for instabox 0.1.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 instabox 0.1.0
File Size Uploaded Provenance Actions
instabox-0.1.0.tar.gz 567.4 kB Without provenance Details for instabox-0.1.0.tar.gz

Built distribution (wheel)

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

Total release size: 607.3 kB

Release files / instabox-0.1.0.tar.gz

Download URL instabox-0.1.0.tar.gz
Size 567.4 kB
Tags Source
SHA-256 checksum
How to use checksums
64c31de20f1d3f499079eecf309d4d46f26012d7320c6320f757c399c4f6b590
BLAKE2b-256 checksum
How to use checksums
6f4ada24a85a5e811aa742bdbc262028605f0bc2a01cd12b4c65f515a6439470
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / instabox-0.1.0-py3-none-any.whl

Download URL instabox-0.1.0-py3-none-any.whl
Size 39.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c6038f7bf7fdecbdf9e8e38e0492ec42f16c74397eb7519b7b5a35cf1d69508c
BLAKE2b-256 checksum
How to use checksums
7b3042505ff75086478d4578709433b8aba81304a6994128b002cd789dba84e5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for instabox 0.1.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.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

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