Metadata-Version: 2.4
Name: pixio-api
Version: 0.2.1
Summary: Official Python client for Pixio API — run ComfyUI workflows in the cloud
Project-URL: Documentation, https://docs.myapps.ai
License: MIT
Keywords: ai,comfyui,image-generation,pixio,video-generation
Requires-Python: >=3.8
Requires-Dist: requests>=2.25
Provides-Extra: async
Requires-Dist: httpx>=0.24; extra == 'async'
Description-Content-Type: text/markdown

# pixio-api

Official Python client for **[Pixio API](https://api.myapps.ai)** — run ComfyUI workflows in the cloud on GPUs from T4 to B300.

- **Light** — one dependency (`requests`); Python 3.8+
- **Batteries included** — polling helper, output collector, billing-aware errors, automatic retries
- **Async optional** — `AsyncPixioAPI` on httpx via `pip install pixio-api[async]`
- ~300 lines you can actually read

## Install

```bash
pip install pixio-api            # sync client
pip install "pixio-api[async]"   # + async client (httpx)
```

## Quickstart

```python
import os
from pixio_api import PixioAPI

pixio = PixioAPI(api_key=os.environ["PIXIO_API_KEY"])

# 1. Queue a run — returns immediately
run_id = pixio.queue_run(
    deployment_id="<your-deployment-id>",
    inputs={"prompt": "A cinematic photo of a lighthouse in a storm"},
)

# 2. Wait for it (polls every 3s, stops at a terminal state)
run = pixio.wait_for_run(run_id, on_progress=lambda r: print(r["status"], r["progress"]))

# 3. Collect the outputs
if run["status"] == "success":
    for img in pixio.collect_outputs(run, "images"):
        print(img["url"])
```

You need:

1. **An API key** → [api.myapps.ai/api-keys](https://api.myapps.ai/api-keys)
2. **A deployment ID** → deploy any workflow ([guide](https://docs.myapps.ai/docs/deployments/create)) and copy its ID

## API

### `PixioAPI(api_key, base_url=…, timeout=30.0, session=None, retries=3)`

| arg | default | notes |
|---|---|---|
| `api_key` | — | required |
| `base_url` | Pixio production | self-hosted / staging override |
| `retries` | `3` | automatic retries for GET requests |
| `session` | new `requests.Session` | bring your own (proxies, testing) |

### `queue_run(deployment_id, inputs=None, webhook=None, webhook_intermediate_status=None) → str`

Queue a deployment run; returns the run id immediately. `inputs` keys are the input names you exposed with external input nodes. Pass `webhook` for production — push beats polling.

### `get_run(run_id) → dict`

Current state: `status`, `progress` (0–1), `live_status`, `outputs`, timings, GPU.

### `wait_for_run(run_id, interval=3.0, timeout=None, on_progress=None) → dict`

Polls until a terminal state (`success` / `failed` / `timeout` / `cancelled`). Raises `TimeoutError` if the optional client-side `timeout` (seconds) elapses.

### `cancel_run(run_id)`

Cancels a queued/running run. Billed only for time already used.

### `collect_outputs(run, kind="images") → list[dict]`

Flattens a run's outputs. `kind` is `"images"`, `"files"`, `"gifs"`, or `"mesh"` — video workflows typically emit under `files`/`gifs`.

## Async

```python
from pixio_api import AsyncPixioAPI  # pip install "pixio-api[async]"

async with AsyncPixioAPI(api_key=os.environ["PIXIO_API_KEY"]) as pixio:
    run_id = await pixio.queue_run("<deployment-id>", inputs={"prompt": "..."})
    run = await pixio.wait_for_run(run_id)
```

Same surface as the sync client; context manager closes the connection pool.

## Error handling

Every non-2xx response raises `PixioAPIError`:

```python
from pixio_api import PixioAPIError

try:
    pixio.queue_run(deployment_id, inputs=inputs)
except PixioAPIError as e:
    if e.is_billing_error:   # 402: out of credits or plan required
        ...                   # send the user to top up
    else:
        print(e.status, e.detail)
```

| `status` | meaning |
|---|---|
| `401` | bad / revoked API key |
| `402` | out of credits / plan required (`is_billing_error == True`) |
| `404` | unknown run or deployment id |
| `422` | invalid inputs — `e.body` has field details |

**Retries:** GET requests auto-retry on network errors and `429/502/503/504` with exponential backoff + jitter (default 3 attempts, `retries=` arg). POSTs (queue/cancel) are **never** auto-retried — an ambiguous failure retried could queue and bill the same run twice. Handle queue failures explicitly.

## Run lifecycle

```
not-started → queued → started → running → uploading → success
                                         ↘ failed / timeout / cancelled
```

`TERMINAL_STATUSES` is exported. A `failed`/`timeout` run is **not** an HTTP error — the request succeeded; check the run's logs in the dashboard. Full reference: [Run Lifecycle & Errors](https://docs.myapps.ai/docs/api/lifecycle).

## Links

- **Docs**: [docs.myapps.ai](https://docs.myapps.ai) — [Getting Started](https://docs.myapps.ai/docs/api/quickstart) · [GPU pricing](https://docs.myapps.ai/docs/billing/gpu-pricing) · [Billing](https://docs.myapps.ai/docs/billing/overview)
- **Dashboard**: [api.myapps.ai](https://api.myapps.ai)
- **TypeScript client**: [`npm i pixio-api`](https://www.npmjs.com/package/pixio-api)

MIT © Pixio
