Metadata-Version: 2.5
Name: infiniteaudience
Version: 0.1.0
Summary: Official Python SDK for the Infinite Audience API — token lifecycle, async file-match orchestration, and typed errors on top of the raw REST surface.
Project-URL: Homepage, https://docs.infiniteaudience.ai
Project-URL: Repository, https://github.com/no-fait/platform
License-Expression: MIT
Classifier: Development Status :: 3 - Alpha
Classifier: Programming Language :: Python :: 3
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx>=0.27
Provides-Extra: dev
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Description-Content-Type: text/markdown

# infiniteaudience

Official Python SDK for the [Infinite Audience](https://infiniteaudience.ai) API.

**v0.1 — enrichment only.** Audience building (segments, audiences,
campaigns, discovery) is out of scope for this first cut; see the roadmap note
at the bottom.

```bash
pip install infiniteaudience==0.1.0
```

## Why this exists

Three gaps the raw REST API leaves every caller to solve themselves:

1. **Token lifecycle.** API-key mode automatically re-exchanges a `cf_live_`
   key for one-hour bearer tokens. OAuth mode serializes rotating refresh-token
   use and exposes a persistence callback for delegated user applications.
2. **Async workflow orchestration.** File match is create → signed upload →
   poll a status enum → deliver → poll again → fetch a 24h signed URL. Six
   steps, several of which are documented traps (see below) —
   `client.match.file` and `client.deliveries` collapse this into
   `create()` / `wait()` / `download()`.
3. **Result-shape traps.** Microbatch responses are not in input order;
   `iag_person_id` tiers have round-trip semantics; 402 responses carry one of
   two disjoint extras families depending on billing model.

## Quick start

```python
import os
from infinite_audience import InfiniteAudience

client = InfiniteAudience(api_key=os.environ["IA_API_KEY"])

# Microbatch -- re-keyed by row_id, matched/unmatched already partitioned.
result = client.match.microbatch([{"row_id": "1", "email": "jane@example.com"}])
print(result.by_row_id("1"))

# File match -- the match-namespaced happy path, never the audience routes.
job = client.match.file.create(name="Q1 list", file_format="csv")
client.match.file.upload(job, [csv_bytes])
status = client.match.file.wait(job["match_id"])
delivery = client.deliveries.create(job["match_id"], include_unmatched=True)
finished = client.deliveries.wait(job["match_id"], delivery["delivery_id"])
urls = client.deliveries.download(finished)
```

## Delegated OAuth

Use OAuth for applications acting with a user's consent. API keys remain the
recommended server-to-server credential. The host application securely stores
the token set and handles the browser redirect; the SDK builds PKCE requests
and refreshes tokens automatically.

```python
from infinite_audience import (
    InfiniteAudience, OAuthTokenManagerOptions, OAuthTokenSet,
    create_authorization_request, exchange_authorization_code,
)

authorization = create_authorization_request(
    client_id="https://client.example/oauth-metadata.json",
    redirect_uri="https://client.example/callback",
    resource="https://api.infiniteaudience.ai",
)
# Open authorization.url and retain authorization.code_verifier until callback.
stored_token_set = exchange_authorization_code(
    client_id="https://client.example/oauth-metadata.json",
    code=callback_code,
    code_verifier=authorization.code_verifier,
    redirect_uri="https://client.example/callback",
    resource="https://api.infiniteaudience.ai",
)
secure_store.save(stored_token_set)

client = InfiniteAudience(oauth=OAuthTokenManagerOptions(
    client_id="https://client.example/oauth-metadata.json",
    token_set=stored_token_set,
    on_token_set=secure_store.save,
))
```

The API URL defaults to `https://api.infiniteaudience.ai`. For an approved
alternate endpoint, pass `base_url` to `InfiniteAudience`. With OAuth, use that
same URL as `resource` and pass its `/v1/oauth/authorize` and `/v1/oauth/token`
URLs as `authorization_endpoint` and `token_endpoint` to the two initial PKCE
helpers. The client then uses `base_url` for token refresh and API calls. Keep
token sets separate for each resource.

MCP and A2A access tokens use their own resource URIs and cannot be replayed
against REST. Standards-compliant MCP clients perform OAuth directly and do
not need this SDK.

## Typed errors

```python
from infinite_audience import InsufficientBalance, PostpayCeilingExceeded, RateLimited
import time

try:
    client.deliveries.create(match_id)
except InsufficientBalance as err:
    print(f"Short ${err.shortfall} (have ${err.available}, need ${err.required})")
except PostpayCeilingExceeded as err:
    print(f"Ceiling ${err.ceiling}, accrued ${err.accrued}")
except RateLimited as err:
    time.sleep(err.retry_after_seconds)
```

## Status

`0.1.0` is the first functional SDK release. Breaking changes are possible
before `1.0.0`. Full docs, including the five file-match traps this SDK's
`match.file`/`deliveries` design exists to avoid:
<https://docs.infiniteaudience.ai/guides/enrichment-file-match/>.

**Not yet in v0.1:** segments, audiences, campaigns, discovery. These land in
`0.2+` once the enrichment surface above has been exercised end-to-end.
