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

# Python SDK

> Official Python SDK for the Alguna Public API: installation, sync and async clients, resources, pagination, error handling, retries and configuration.

`alguna` is the official Python client for the Alguna Public API. It has one typed method per endpoint: customers, products and plans, subscriptions, usage events, credits and wallets, invoices, payments, and checkout and portal sessions. Requests and responses are Pydantic v2 models generated from the versioned OpenAPI spec. Use it from Python services where you would otherwise write HTTP calls by hand. It sets the `Authorization` and `Alguna-Version` headers, maps failed responses to typed errors, sends an idempotency key on writes that use one, and retries transient failures only when a retry is safe. The objects it works with are described in [How Alguna works](/docs/concepts/how-alguna-works).

<Note>
  The SDK is for servers only. API keys are secret: never put one in a browser or a mobile app. For a purchase flow in your frontend, use [Embedded Checkout](/docs/hosted/embedded-checkout). Your server creates the session with this SDK, and your frontend mounts it.
</Note>

***

## How it works

1. **Create an API key** under **Settings → Connections → Developers → API keys**. A key looks like `<key_id>.<key_secret>`.
2. **Create the client** with the key. Each request carries the key and pins the API version in the `Alguna-Version` header.
3. **Call resources** such as `client.customers` and `client.subscriptions`. Each method returns a typed model. A failed request raises a typed error.

## Installation

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

Requires Python 3.10 or later. The package is [`alguna` on PyPI](https://pypi.org/project/alguna/), and its source is [alguna-io/alguna-python](https://github.com/alguna-io/alguna-python) on GitHub. The package ships `py.typed`, so mypy and pyright check your calls.

```python theme={null}
from alguna import Alguna

# Reads the API key from ALGUNA_API_KEY when you do not pass api_key.
client = Alguna(
    api_version="2026-04-01",  # recommended: pin the version you integrate against
)
```

Without a key, `Alguna()` raises `ValueError` before it sends a request.

***

## Quick start: customer → subscription → invoice → portal session

```python theme={null}
from datetime import date

from alguna import (
    Alguna,
    ContactRequest,
    ContractInput,
    CreateInvoiceLineItemRequest,
    CreateSubscriptionItemInput,
    FixedPricingModelInput,
    PriceInput,
)

client = Alguna()

# 1. Create a customer
customer = client.customers.create(
    name="Acme Corp",
    currency="USD",
    contacts=[ContactRequest(email="billing@acme.com", is_primary=True)],
)

# 2. Create a product and start a subscription
product = client.products.create(
    name="Platform Fee",
    fee_type="fixed",
    billing_frequency="recurring",
    payment_terms="advance",
)

subscription = client.subscriptions.create(
    customer_id=customer.id,
    currency="USD",
    items=[
        CreateSubscriptionItemInput(
            product_id=product.id,
            price=PriceInput(
                type="fixed",
                fee_type="fixed",
                billing_direction="advance",
                billing_frequency="recurring",
                billing_interval="monthly",
                fixed_pricing_model=FixedPricingModelInput(price_per_unit="99.00", units=1),
            ),
        )
    ],
    contract=ContractInput(period_type="fixed", duration_months=12, start_date=date(2026, 7, 1)),
    auto_activate=True,
)

# 3. Issue a one-off invoice
invoice = client.invoices.create(
    customer_id=customer.id,
    currency="USD",
    line_items=[CreateInvoiceLineItemRequest(description="Onboarding", unit_price="500.00", quantity="1")],
)

# 4. Open a customer portal session so the customer can pay and manage billing
session = client.portal.create_session(customer_id=customer.id)
print(session.url)  # send the customer here
```

<Note>
  Subscriptions are created in `draft` status. Pass `auto_activate=True` (as above) or call `client.subscriptions.activate(id)` when you are ready to go live. See [Creating a Subscription](/docs/api-reference/v2/examples/creating-a-subscription) for the full set of patterns.
</Note>

Money amounts are decimal strings, such as `"1250.50"`. Use `decimal.Decimal` for arithmetic, not `float`.

***

## Resources

The client has one attribute for each API resource, and every public operation has a method. Method names are snake\_case, for example `client.customers.create(...)` and `client.invoices.get(...)`. Examples:

| Resource | Methods |
| - | - |
| `client.customers` | `create`, `get`, `list`, `update`, connected accounts |
| `client.subscriptions` | `create`, `get`, `list`, `update`, `activate`, `download_pdf`, credit grants |
| `client.invoices` | `create`, `get`, `list`, `generate_pdf`, line items |
| `client.portal` | `create_session` for the [customer portal](/docs/hosted/customer-portal) |
| `client.events` | `ingest` usage events |
| `client.credits` | `check`, `track`, `get_reservation`; see [Credits & wallets](/docs/credits/overview) |

Other resources include `products`, `plans`, `bundles`, `quotes`, `credit_notes`, `payments`, `refunds`, `wallets`, `wallet_grants`, `metrics`, `checkout_sessions`, `subscription_versions`, `subscription_changes` and `tax`. [reference.md](https://github.com/alguna-io/alguna-python/blob/main/reference.md) in the SDK repository lists every method with its parameters. The generated [API reference](/docs/api-reference/v2/overview) describes each endpoint.

Request and response models, and the webhook payload models, are exported from the package root:

```python theme={null}
from alguna import CustomerResponse, InvoiceResponse
```

Every method takes an optional `request_options` dictionary for one request. It accepts `timeout`, `max_retries`, `additional_headers`, `additional_query_parameters`, `additional_body_parameters` and `chunk_size`. Methods that write also take `idempotency_key`.

## Async client

`AsyncAlguna` takes the same arguments and has the same methods, as awaitables:

```python theme={null}
import asyncio

from alguna import AsyncAlguna


async def main() -> None:
    client = AsyncAlguna()
    customer = await client.customers.get("cus_1")
    print(customer.name)


asyncio.run(main())
```

## Pagination and filtering

A list method accepts `limit`, `offset`, `sort` (in `field:order` format) and the same filters as the [HTTP API](/docs/api-reference/v2/overview#filtering-and-sorting). It returns one page. `data` holds the items of the page:

```python theme={null}
page = client.customers.list(limit=25, offset=0)
print([customer.id for customer in page.data or []])
```

To get every item, use `paginate`. It asks for the largest page that the list method allows, moves the offset, and stops after the first short page. Filters go through to the list method. `apaginate` does the same for `AsyncAlguna`.

```python theme={null}
from alguna import Alguna, apaginate, paginate

client = Alguna()

for customer in paginate(client.customers.list, has_active_subscriptions=True):
    print(customer.id, customer.name)

# With AsyncAlguna:
# async for customer in apaginate(client.customers.list): ...
```

## PDF downloads

`client.invoices.generate_pdf`, `client.quotes.get_pdf` and `client.subscriptions.download_pdf` return an iterator of `bytes` chunks. Write the chunks to a file:

```python theme={null}
with open("invoice.pdf", "wb") as file:
    for chunk in client.invoices.generate_pdf(invoice.id):
        file.write(chunk)
```

***

## Error handling

An error status raises `alguna.ApiError` or one of its subclasses. The error has `status_code`, `body` (the API's error response) and `headers`. `str(error)` is `HTTP <status>: <message>`. The message is the API's `detail`, else its `title`, else its `message`, else the HTTP status text.

```python theme={null}
from alguna import Alguna, ApiError, NotFoundError

client = Alguna()

try:
    client.customers.get("cus_missing")
except NotFoundError as error:
    print(error.status_code, error.body.detail)
except ApiError as error:
    print(error)
```

| Error | Status |
| - | - |
| `BadRequestError` | 400 |
| `UnauthorizedError` | 401 |
| `ForbiddenError` | 403 |
| `NotFoundError` | 404 |
| `ConflictError` | 409 |
| `UnprocessableEntityError` | 422 |
| `TooManyRequestsError` | 429 |
| `InternalServerError` | 500 |
| `BadGatewayError` | 502 |
| `ServiceUnavailableError` | 503 |
| `GatewayTimeoutError` | 504 |
| `ApiError` | base class of all errors above |

All of these extend `ApiError`. When no response arrives, after the retries, the SDK raises the `httpx` exception, such as `httpx.ConnectError`. A successful response never raises because of an unexpected body. Fields that the SDK does not know stay available on the model. Status meanings and retry guidance are on the [Errors](/docs/api-reference/v2/errors) page.

***

## Idempotency and retries

Every request that is not a GET sends an `Idempotency-Key`. Pass your own `idempotency_key`, or the SDK makes a UUID v4 for each call. The API then returns the original result instead of repeating the action. See [Idempotency](/docs/api-reference/v2/idempotency).

`client.events.ingest`, `client.credits.check` and `client.credits.track` are different. The request body identifies their work, so the SDK makes no key for them and still retries them. An `idempotency_key` that you pass is sent on every attempt. See [Writes identified by their events](/docs/api-reference/v2/idempotency#writes-identified-by-their-events). Pass your own key when you retry a call yourself, for example after a restart:

```python theme={null}
customer = client.customers.create(
    name="Acme Corp",
    currency="USD",
    idempotency_key="create-acme-2026-04-01",
)
```

The client retries a failed request up to 2 times (3 attempts in total) when:

* the status is 408, 429, 502, 503 or 504.
* the status is 409 and the response has `Retry-After`. The API sends this only while a request with the same key is still running. Any other 409 raises `ConflictError` at once.
* the connection is refused, reset or closed, or the attempt times out.

Before a retry, the client waits for the time in `Retry-After`, up to 60 seconds. Without it, the client backs off exponentially with jitter, starting at 1 second. Every retry sends the same key. Set `max_retries` on the client or in `request_options` for one request. `0` turns retries off. When you cancel an async call, for example with `asyncio.timeout`, the client does not retry it.

```python theme={null}
client = Alguna(max_retries=5)
client.customers.get("cus_1", request_options={"max_retries": 0})
```

Each attempt times out after 60 seconds. Set `timeout`, in seconds, on the client or in `request_options`.

***

## Configuration

```python theme={null}
from alguna import Alguna

client = Alguna(
    api_key="key_id.key_secret",      # optional: defaults to ALGUNA_API_KEY
    api_version="2026-04-01",         # optional: defaults to the version this release was built for
    base_url="https://api.alguna.io", # optional: the default
    timeout=60.0,                     # optional: seconds for each attempt
    max_retries=2,                    # optional: see Idempotency and retries
)
```

For a sandbox, pass `base_url="https://api.sandbox.alguna.io"` and a sandbox API key. For a proxy, TLS settings or another transport, pass your own `httpx.Client` (an `httpx.AsyncClient` for `AsyncAlguna`) as `httpx_client`. If you pass `httpx_client` and no `timeout`, the timeout of your `httpx_client` applies.

```python theme={null}
import httpx

from alguna import Alguna

client = Alguna(httpx_client=httpx.Client(proxy="http://proxy.example.com:8080"))
```

Pass `api_version` to pin the version your integration is written against. Without it, the SDK sends the version it was generated from, so a package upgrade can move you to a newer API version. See [Versioning](/docs/api-reference/v2/overview#versioning). Each request also sends `User-Agent: alguna-python/<version>`.

***

## FAQ

**Can I use the SDK in the browser?**
No. It uses your secret API key. For browser purchase flows, create a checkout session on your server and mount it with [Embedded Checkout](/docs/hosted/embedded-checkout).

**Does the SDK cover every endpoint?**
Yes. The client is generated from the versioned OpenAPI spec, so every operation in the [API reference](/docs/api-reference/v2/overview) has a method.

**Are there types for webhook payloads?**
Yes. Each event type has a model, such as `WebhookInvoicePaidPayload`. The SDK does not verify signatures. Before you trust a payload, verify its `svix-id`, `svix-timestamp` and `svix-signature` headers with the [`svix`](https://pypi.org/project/svix/) package, as [Verifying signatures](/docs/api-reference/v2/webhooks#verifying-signatures) shows. Payload keys are camelCase, and the models have snake\_case fields.

**How do I point it at a sandbox?**
Pass `base_url="https://api.sandbox.alguna.io"` with a sandbox API key. Sandbox and production are separate environments with separate keys. See [Base URLs](/docs/api-reference/v2/overview#base-urls).

**Is there an SDK for another language?**
Yes. There are [TypeScript](/docs/api-reference/v2/sdks/typescript), [Go](/docs/api-reference/v2/sdks/go) and [Rust](/docs/api-reference/v2/sdks/rust) SDKs and an [`alguna` CLI](/docs/api-reference/v2/sdks/cli). See [SDKs and CLI](/docs/api-reference/v2/sdks/overview).

**Which version am I on?**
The package version is the PyPI release of `alguna`. Run `pip show alguna` to read it. The API version is the date sent as `Alguna-Version`, and it alone decides request and response shapes. When you pass `api_version`, a package upgrade never changes it.

**How do I report a bug?**
Open an issue in [alguna-io/alguna-python](https://github.com/alguna-io/alguna-python). Alguna generates the SDK in a private repository, so a pull request there is not merged directly. The change arrives with a later release.


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