> ## Documentation Index
> Fetch the complete documentation index at: https://docs.inkrelay.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Python SDK

> inkrelay for Python 3.10 and later.

The Python SDK is published on PyPI as `inkrelay` and imported as `inkrelay`. It offers the same helpers as the TypeScript SDK: retries with backoff, automatic idempotency keys, pagination iterators, webhook verification and `create_and_wait`.

```bash theme={null}
pip install inkrelay
```

```python theme={null}
from inkrelay import InkRelay

ir = InkRelay()  # reads INKRELAY_API_KEY
for printer in ir.printers.iterate():
    print(printer["id"])

job = ir.print_jobs.create_and_wait(
    {"printerId": printer_id, "title": "Invoice", "contentType": "pdf_uri", "content": url},
    timeout_s=60,
)
print(job["state"])
```

Responses are plain dictionaries with the same camelCase keys as the API. `create_and_wait` creates the job and polls until it reaches `done`, `error`, `cancelled` or `expired`, then returns it. Check `job["state"]` and `job["stateMessage"]` rather than expecting an exception for a failed print.

## Configuration

```python theme={null}
ir = InkRelay(
    api_key="ir_test_...",  # defaults to the INKRELAY_API_KEY environment variable
    base_url="https://api.inkrelay.app",  # the default
    max_retries=3,  # the default
    timeout=30.0,  # seconds per request, the default
)
```

The constructor raises `ValueError` when neither `api_key` nor `INKRELAY_API_KEY` is set. The SDK retries `429` responses (waiting for `Retry-After` when present), and retries `5xx` responses and network errors with exponential backoff, but only for replay-safe requests: `GET`, `PATCH`, `DELETE`, and print job creation (which carries an idempotency key). Other `POST` requests are not retried on `5xx` errors. `print_jobs.create` and `create_and_wait` send an `Idempotency-Key` automatically and reuse it on every retry. Pass `idempotency_key="..."` to `create` to use your own key.

## Errors

A non-2xx response that survives the retries raises `InkRelayError` with `status`, `code` and `request_id`. `create_and_wait` raises `InkRelayTimeoutError` when the job is still not in a final state after `timeout_s` seconds (default 60); its `job` attribute holds the last known job.

```python theme={null}
from inkrelay import InkRelay, InkRelayError, InkRelayTimeoutError

ir = InkRelay()
try:
    ir.print_jobs.create_and_wait({"printerId": printer_id, "title": "Invoice", "contentType": "pdf_uri", "content": url})
except InkRelayTimeoutError as err:
    print("still not done", err.job["id"], err.job["state"])
except InkRelayError as err:
    print(err.status, err.code, err.request_id)
```

See [Errors](/guides/errors) for the codes the API returns.

## Pagination

`list(**params)` returns one page as `{"data": [...], "hasMore": ..., "nextCursor": ...}`. `iterate(**params)` follows `nextCursor` for you and yields every item:

```python theme={null}
page = ir.print_jobs.list(limit=50, state="error")
print(len(page["data"]), page["hasMore"], page["nextCursor"])

for job in ir.print_jobs.iterate(state="error"):
    print(job["id"], job["stateMessage"])
```

`computers`, `printers`, `print_jobs` and `webhooks` have `list`, `iterate` and `get`. `events` (and webhook deliveries) have `list` and `iterate` only, with no `get`. Pass `after` and `dir="asc"` to `events.list` to poll for new events.

## Webhooks

`inkrelay.webhooks.verify(body, headers, secret)` checks the signature of a request and raises `WebhookVerificationError` if it is invalid or older than 5 minutes. Pass the raw body, not parsed JSON.

```python theme={null}
import os

from inkrelay import webhooks, WebhookVerificationError

try:
    webhooks.verify(raw_body, request_headers, os.environ["INKRELAY_WEBHOOK_SECRET"])
except WebhookVerificationError:
    ...  # respond with 400
```

The client also manages endpoints: `ir.webhooks.create`, `update`, `delete`, `deliveries(id)` and `replay(delivery_id)`. `create` returns the signing secret once. See [Webhooks](/guides/webhooks) for a full receiver.

## Generated client

The convenience methods sit on top of a client generated from the OpenAPI spec. `ir.raw` is that client, already configured with your key, base URL and the retrying transport. Use it for an endpoint the wrapper has no helper for.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.