Metadata-Version: 2.5
Name: meteroid
Version: 0.30.0
Summary: Official Python SDK for Meteroid, the open-source billing platform: subscriptions, invoicing, usage-based billing and webhooks.
Project-URL: Repository, https://github.com/meteroid-oss/meteroid-python
Project-URL: Homepage, https://meteroid.com
Project-URL: Documentation, https://docs.meteroid.com
Project-URL: Issues, https://github.com/meteroid-oss/meteroid-python/issues
Author-email: Meteroid <hey@meteroid.com>
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: api,billing,invoicing,metering,meteroid,pricing,sdk,subscriptions,usage-based-billing,webhooks
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx<1,>=0.27
Provides-Extra: dev
Requires-Dist: mypy>=1.18; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: ruff==0.14.10; extra == 'dev'
Description-Content-Type: text/markdown

# Meteroid Python SDK

The official Python SDK for [Meteroid](https://meteroid.com), the open-source billing and pricing platform. Meteroid manages subscriptions, usage-based billing and metering, invoicing and revenue analytics; this library calls its REST API and verifies its webhooks, against Meteroid Cloud (`https://api.meteroid.com`) or a self-hosted instance.

[Website](https://meteroid.com) · [Documentation](https://docs.meteroid.com) · [API reference](https://docs.meteroid.com/api-reference) · [Meteroid on GitHub](https://github.com/meteroid-oss/meteroid)

## Installation

```sh
pip install meteroid
```

Python 3.10 or newer. The SDK depends on `httpx` only and is fully typed. Every method of the API
is listed in [api.md](api.md).

## Usage

```python
from meteroid import Meteroid

client = Meteroid(api_key="your-api-key")

add_on = client.add_ons.retrieve("addon_id")
print(add_on)
```

Without `api_key`, the client reads `METEROID_API_KEY`, and `METEROID_BASE_URL`
overrides the default base URL, `https://api.meteroid.com`. Every argument
is a keyword argument. The constructor also takes `base_url`, `timeout`,
`max_retries`, `default_headers`, `http_client` (an `httpx.Client` of yours), `middleware`, and
the credentials the API accepts (`token_provider`, `basic_auth`, `api_keys`). Close the client,
or use it as a context manager, to release its connections.

`AsyncMeteroid` has the same resources for asyncio:

```python
from meteroid import AsyncMeteroid

async with AsyncMeteroid() as client:
    add_on = await client.add_ons.retrieve("addon_id")
```

## Requests

The fields of a JSON or form request body are keyword arguments, next to the query and header
parameters; path parameters come first:

```python
client.connect.create_onboarding_link("id", redirect_url="redirect_url")
```

An optional argument left out is not sent; `None` sends `null` where the API accepts it. An enum
argument takes the enum or its value as a string. Other bodies (lists, unions, files) are one
`body` argument.

Every method also takes, for that request only, `extra_headers=`, `extra_query=`, `extra_body=`
(merged into the body), `timeout=` and `max_retries=`. These headers, like `default_headers`, win
over the client's credentials.

## Models

Models are keyword-only dataclasses with `from_dict`/`to_dict`. In models requests send, an
optional field that accepts `null` defaults to `UNSET` (from `meteroid.models`): it is
left out of the request, while `None` sends `null`; in response models it is simply `None` when
absent. A field holding a string or an object, such as an expandable id, is typed `str | Model`.
A discriminated union is the union of its variant models (`Circle | Square | UnknownVariant`),
decoded into the variant the tag names; when its variants share fields, it is a model holding the
discriminator and the variant, and passing `content=` alone fills in the tag.

Properties the API added after this SDK was generated are kept in `extra_fields` (and read
as attributes at runtime), and sent back when the model is serialized. A property named
after a model member, such as `extra_fields` or `to_dict`, gets a trailing `_`.

## Pagination

List methods return their first page, which is also the response of the request: a
subclass of the response model, such as `AddOnsListPage` (in `meteroid.api`),
whose properties are read on the page. Iterating it walks every item of every page,
fetching the next ones on demand:

```python
for add_on in client.add_ons.list():
    print(add_on)

page = client.add_ons.list()
print(page.pagination_meta.total_pages)  # a property of the response
page.items  # this page's items
if page.has_next_page():
    page = page.get_next_page()
for page in client.add_ons.list().iter_pages():
    print(len(page.items))
```

A response property named like a paging member (`items`, `has_next_page`, `get_next_page`,
`iter_pages` or `body`) is read on `page.body`, the response as decoded.

With the async client, `async for item in client.add_ons.list()` walks the items, and
`page = await client.add_ons.list()` returns the first page, whose `get_next_page()` is
awaited and `iter_pages()` walked with `async for`.

Multipart bodies take `Upload(content, filename, content_type)` files, and binary bodies bytes or
file objects.

## Raw responses

Prefix a call with `with_raw_response` for the HTTP response next to the decoded result:

```python
response = client.with_raw_response.add_ons.retrieve("addon_id")
print(response.status_code, response.headers, response.request_id)
add_on = response.parse()
```

## Errors

Every error derives from `MeteroidError`. A non-2xx response raises an `APIStatusError`
subclass named after its status (`BadRequestError`, `AuthenticationError`,
`PermissionDeniedError`, `NotFoundError`, `ConflictError`, `UnprocessableEntityError`,
`RateLimitError`, `InternalServerError`), whose `body` is the error response decoded into its
schema (its JSON when the API declares none) and `request_id` the id to quote to support.

```python
from meteroid import APIConnectionError, APITimeoutError, NotFoundError

try:
    client.add_ons.retrieve("addon_id")
except NotFoundError as error:
    print(error.status_code, error.request_id, error.body)
except APITimeoutError:
    ...  # the request timed out, after retries
except APIConnectionError:
    ...  # no response, after retries
```

A successful response that does not decode raises `APIResponseValidationError`.

## Retries and timeouts

Connection errors, timeouts, 408, 429 and 5xx responses are retried twice with exponential
backoff, honoring `Retry-After`, when replaying the request is safe: for idempotent methods
and requests with an `Idempotency-Key` header. Requests time out after
60 seconds.

```python
client = Meteroid(max_retries=5, timeout=20.0)
client.with_options(max_retries=0).add_ons.retrieve("addon_id")
client.add_ons.retrieve("addon_id", timeout=5.0, max_retries=0)  # for one call
```

## Middleware

`middleware=[...]` wraps every HTTP attempt: a callable receiving the `httpx.Request` and
`next`, returning an `httpx.Response`, to cache, log or sign requests.

_Generated by [perseid](https://github.com/meteroid-oss/perseid)._
