Skip to content
Ibby docs
Esc
↑↓navigate↵open⌘Jpreview
On this page

Python SDK

The ibby package for Python 3.13 and later. The same calls as the TypeScript SDK, in snake_case.

pip install ibby

The package needs Python 3.13 or later. Request and response shapes are the API’s own, as pydantic models.

Make a client

import os

from ibby import Ibby

client = Ibby(
    os.environ["IBBY_API_KEY"],
    # Optional, shown with their defaults:
    max_retries=2,
    timeout=150.0,
)
client.close()

Use the client in a with block, or call close() when you are done. max_retries is how many times a call is retried after a dropped connection or a 503. Set it to 0 to turn retries off. A command is never retried on its own. timeout is the most one read or write may wait; None turns it off.

The key is read once. Surrounding spaces and line breaks are ignored; a key with a space or a control character inside raises ValueError, without showing the key.

Sandboxes

create() returns a Sandbox. Its methods act on that sandbox.

import os

from ibby import Ibby

with Ibby(os.environ["IBBY_API_KEY"]) as client:
    sandbox = client.create(
        workdir="/work",
        name="report-run-1",
        lifetime_ms=60_000,
        resources={"cpu": 0.5, "memory_mib": 512},
    )
    print(sandbox.id, sandbox.info.status, sandbox.info.resources)

    sandbox.make_directories(["/work/in", "/work/out"])
    sandbox.write_files({"/work/in/data.csv": "a,b\n1,2\n", "/work/in/logo.bin": b"\x00\x01\x02"})

    for entry in sandbox.list_files("/work/in").entries:
        print(entry.type, entry.size_bytes, entry.path)

    print(sandbox.read_file("/work/in/data.csv", max_bytes=1024).decode())
    sandbox.remove()

To act on a sandbox you made earlier, use client.sandbox(sandbox_id). It makes no network call; its info is None until you call refresh(). client.get(sandbox_id) fetches the record as well. To remove a sandbox by its name after a crash, when you no longer have its id, use client.remove_by_name(name). An unknown name is fine.

create() also takes image= to run on an image you built (see Images), and store={"mount_path": "/work/in", "key_prefix": "runs/1/"} to attach part of the lane’s store folder read-only; the concepts page gives the rules.

A sandbox’s status and a listing entry’s type are plain strings, so a value added later never breaks your code’s parsing.

Running commands

import os

from ibby import ExecTimedOut, Ibby

with Ibby(os.environ["IBBY_API_KEY"]) as client:
    sandbox = client.create(workdir="/work")
    result = sandbox.exec(
        ["sh", "-c", "echo $GREETING"],
        cwd="/work",
        env={"GREETING": "hello"},
        timeout_ms=30_000,
    )
    if isinstance(result, ExecTimedOut):
        print("timed out")
    else:
        print(result.exit_code, result.stdout, result.stdout_truncated)
    sandbox.remove()

command is a list of arguments. A string raises TypeError, so "ls -la" is never sent as single characters. A timeout is a result, not an exception. stdout is None when the output is unavailable: when you repeat a finished command with the same idempotency_key (the outcome comes back, the output does not), or, in this version, when the command timed out. stdout_truncated is False whenever stdout is None.

Errors

Every failed call raises IbbyError.

import os

from ibby import Ibby, IbbyError

with Ibby(os.environ["IBBY_API_KEY"]) as client:
    try:
        client.get("sbx_missing")
    except IbbyError as error:
        print(error.status, error.code, error.message, error.request_id)

code is the API’s stable code, such as not_found or sandbox_stopped, and message is the API’s own sentence. When no answer came back at all, status is 0 and code is connection_error or timeout. A redirect is never followed: it is an IbbyError with code unexpected_response. A request that breaks the schema’s own rules, such as a relative path, raises pydantic’s ValidationError before anything is sent. A rule between two fields, such as store.mount_path sitting inside workdir, is checked by the API, which answers 400 invalid_request.

Safe retries

Calls that create or write send a fresh Idempotency-Key. To retry one yourself, for example after your process restarts, pass your own key both times:

import os

from ibby import Ibby

with Ibby(os.environ["IBBY_API_KEY"]) as client:
    sandbox = client.create(workdir="/work", idempotency_key="nightly-2026-11-13")
    again = client.create(workdir="/work", idempotency_key="nightly-2026-11-13")
    assert again.id == sandbox.id
    sandbox.remove()

Images

build_image() starts a build from a recipe and returns its job. When the job is done, its result names the image to create sandboxes from. The concepts page explains recipes. A recipe that breaks the rules, such as a version range, raises pydantic’s ValidationError before anything is sent. get_image() raises IbbyError with code image_not_built (status 409) when the image has no build on the lane’s current provider, which happens only after a lane changes provider; rebuild it from its recipe.

import os
import time

from ibby import Ibby

HASH = "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
recipe = {
    "base": {"reference": "python:3.13-slim", "digest": HASH},
    "packages": [{"name": "numpy", "version": "2.3.1", "hashes": [HASH]}],
    "import_check": ["numpy"],
}

with Ibby(os.environ["IBBY_API_KEY"]) as client:
    job = client.build_image(recipe)
    while job.status in ("accepted", "running"):
        time.sleep(5)
        job = client.get_job(job.id)
    if job.status == "done" and job.result is not None:
        image_id = job.result["image_id"]
        print(client.get_image(image_id).status)
        sandbox = client.create(workdir="/work", image=image_id)
        sandbox.remove()

Jobs

import os

from ibby import Ibby

with Ibby(os.environ["IBBY_API_KEY"]) as client:
    job = client.get_job("job_...")
    if job.status == "running":
        client.cancel_job(job.id)

Was this page helpful?