Metadata-Version: 2.4
Name: iteradian
Version: 1.0.0
Summary: Official Iteradian Control Plane SDK for Python
License: MIT
Keywords: iteradian,rpc,blockchain,api,sdk,web3
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: httpx>=0.25.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
Requires-Dist: mypy>=1.0; extra == "dev"
Requires-Dist: ruff>=0.1; extra == "dev"

<p align="center">
  <h1 align="center">Iteradian Python SDK</h1>
  <p align="center">Official Python client library for the Iteradian Control Plane API</p>
</p>

<p align="center">
  <img src="https://img.shields.io/badge/language-Python-3776AB?style=flat-square&logo=python&logoColor=white" alt="Python" />
  <img src="https://img.shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20%7C%203.11%20%7C%203.12-blue?style=flat-square&logo=python&logoColor=white" alt="Python Versions" />
  <img src="https://img.shields.io/badge/version-1.0.0-green?style=flat-square" alt="Version" />
  <img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" alt="License" />
  <img src="https://img.shields.io/badge/pypi-iteradian-blue?style=flat-square&logo=pypi&logoColor=white" alt="PyPI" />
  <img src="https://img.shields.io/badge/deps-httpx-orange?style=flat-square" alt="httpx" />
</p>

---

## Features

- **Modern HTTP client** — powered by [httpx](https://www.python-httplib2.org/) with async support
- **Automatic retry** — exponential backoff on 429 / 5xx responses (configurable max retries)
- **Resource-based API** — clean `client.auth`, `client.organizations`, `client.endpoints` access pattern
- **Full API coverage** — Auth, Organizations, API Keys, Endpoints, Usage, Dashboard, Alerts, Logs, Subscriptions, Plans, Support
- **Type-safe** — all responses returned as Python dataclasses with type hints
- **Configurable** — timeout, retry count, and base URL
- **Dual auth** — supports both Bearer tokens and API key authentication
- **Context manager** — use `with` statements for automatic resource cleanup

## Requirements

- **Python 3.8** or later
- **httpx** >= 0.25.0

## Installation

```bash
pip install iteradian
```

Or with a specific version:

```bash
pip install iteradian==1.0.0
```

### From Source

```bash
git clone https://github.com/iteradian/iteradian-python.git
cd iteradian-python
pip install -e .
```

## Quick Start

```python
from iteradian import IteradianClient

# Create client
client = IteradianClient(
    base_url="https://api.iteradian.com/api/v1"
)

# Login — token is set automatically
tokens = client.auth.login(
    email="user@example.com",
    password="password123"
)
print(f"Logged in as {tokens.user['email']}")

# List organizations
orgs = client.organizations.list()
for org in orgs:
    print(f"  {org.name} ({org.slug})")

if orgs:
    org_id = orgs[0].id

    # List endpoints
    endpoints = client.endpoints.list(org_id)
    for ep in endpoints:
        print(f"  {ep.name}: {ep.status} ({ep.region})")

    # Create API key
    key = client.api_keys.create(org_id, name="Production Key", environment="live")
    print(f"Created key: {key.prefix}")

    # Query logs
    logs = client.logs.query(org_id, page=1, page_size=20)
    print(f"Total logs: {logs.total}")

# Cleanup
client.close()
```

### Using API Key Authentication

```python
client = IteradianClient(
    base_url="https://api.iteradian.com/api/v1",
    api_key="itrd_live_abc123..."
)

endpoints = client.endpoints.list("org-id")
```

### Context Manager

```python
from iteradian import IteradianClient

with IteradianClient(base_url="https://api.iteradian.com/api/v1") as client:
    client.auth.login(email="user@example.com", password="password123")
    orgs = client.organizations.list()
    # client.close() is called automatically
```

### Configuration Options

```python
client = IteradianClient(
    base_url="https://api.iteradian.com/api/v1",
    timeout=60.0,       # Request timeout in seconds (default: 30.0)
    max_retries=5,       # Max retry attempts on 429/5xx (default: 3)
)
```

## API Reference

### Resource Accessors

| Resource               | Type                     | Description                         |
| ---------------------- | ------------------------ | ----------------------------------- |
| `client.auth`          | `_AuthResource`          | Authentication & account management |
| `client.organizations` | `_OrganizationsResource` | Organization CRUD & members         |
| `client.api_keys`      | `_ApiKeysResource`       | API key management                  |
| `client.endpoints`     | `_EndpointsResource`     | Endpoint & network management       |
| `client.usage`         | `_UsageResource`         | Usage analytics                     |
| `client.dashboard`     | `_DashboardResource`     | Dashboard data & health             |
| `client.alerts`        | `_AlertsResource`        | Alert management & rules            |
| `client.logs`          | `_LogsResource`          | Request log querying                |
| `client.subscriptions` | `_SubscriptionsResource` | Subscription & billing              |
| `client.plans`         | `_PlansResource`         | Plan catalog                        |
| `client.support`       | `_SupportResource`       | Support ticket management           |

### Authentication (`client.auth`)

```python
# Login (token set automatically)
tokens = client.auth.login(email="...", password="...")

# Register
tokens = client.auth.register(email="...", password="...", name="...")

# Refresh token
tokens = client.auth.refresh(refresh_token="...")

# Logout
client.auth.logout()

# Two-Factor Authentication
setup = client.auth.enable_2fa(password="...")
print(f"Secret: {setup.secret}")
print(f"QR Code: {setup.qr_code}")
client.auth.verify_2fa(code="123456")
client.auth.disable_2fa(password="...", code="123456")

# Magic Link
client.auth.send_magic_link(email="user@example.com")

# Password Reset
client.auth.forgot_password(email="user@example.com")
client.auth.reset_password(token="...", new_password="...")
```

### Organizations (`client.organizations`)

```python
# CRUD
orgs = client.organizations.list()
org = client.organizations.create(name="My Org", slug="my-org")
org = client.organizations.get(org_id)
client.organizations.delete(org_id)

# Members
members = client.organizations.list_members(org_id)
member = client.organizations.invite_member(org_id, email="user@example.com", role="member")
client.organizations.remove_member(org_id, member_id)
```

### API Keys (`client.api_keys`)

```python
keys = client.api_keys.list(org_id)
key = client.api_keys.create(org_id, name="Key Name", environment="live")
client.api_keys.revoke(org_id, key_id)
new_key = client.api_keys.rotate(org_id, key_id)
analytics = client.api_keys.get_analytics(org_id, key_id)
```

### Endpoints (`client.endpoints`)

```python
# Networks
networks = client.endpoints.get_networks()

# Endpoint management
endpoints = client.endpoints.list(org_id)
endpoint = client.endpoints.create(org_id, name="...", network_id="...", region="us-east-1")
client.endpoints.delete(org_id, endpoint_id)
endpoint = client.endpoints.pause(org_id, endpoint_id)
endpoint = client.endpoints.resume(org_id, endpoint_id)
health = client.endpoints.check_health(org_id, endpoint_id)
metrics = client.endpoints.get_metrics(org_id, endpoint_id)
```

### Usage (`client.usage`)

```python
usage = client.usage.get(org_id, from_date="2024-01-01", to_date="2024-01-31")
```

### Dashboard (`client.dashboard`)

```python
data = client.dashboard.get(org_id, period="7d")
health = client.dashboard.get_health(org_id)
stats = client.dashboard.get_quick_stats(org_id, period="24h")
```

### Alerts (`client.alerts`)

```python
# Alerts
alerts = client.alerts.list(org_id)
alert = client.alerts.acknowledge(org_id, alert_id)
alert = client.alerts.resolve(org_id, alert_id)

# Alert Rules
rules = client.alerts.list_rules(org_id)
rule = client.alerts.create_rule(
    org_id,
    name="High Latency",
    metric="latency_p95",
    condition="greater_than",
    threshold=500,
    severity="warning",
    is_enabled=True,
    cooldown_minutes=15
)
client.alerts.delete_rule(org_id, rule_id)

# Channels & Testing
channels = client.alerts.get_channels(org_id)
client.alerts.test_channel(org_id, channel_id)
```

### Logs (`client.logs`)

```python
# Query logs with filters
logs_response = client.logs.query(
    org_id,
    page=1,
    page_size=50,
    status="error",
    method="eth_call",
    network="ethereum",
    sort_by="timestamp",
    sort_order="desc"
)
print(f"Page {logs_response.page}/{logs_response.total_pages}")
for log in logs_response.logs:
    print(f"  [{log.status}] {log.method} ({log.latency_ms}ms)")

# Get single log
log = client.logs.get(org_id, log_id)

# Available filter options
filters = client.logs.get_filter_options(org_id)

# Log stats
stats = client.logs.get_stats(org_id, period="24h")
```

### Subscriptions (`client.subscriptions`)

```python
sub = client.subscriptions.get(org_id)
sub = client.subscriptions.change_plan(org_id, plan_id="pro")
sub = client.subscriptions.cancel(org_id)
sub = client.subscriptions.reactivate(org_id)
invoices = client.subscriptions.get_invoices(org_id)
```

### Plans (`client.plans`)

```python
plans = client.plans.list()
plan = client.plans.get(plan_id)
```

### Support (`client.support`)

```python
tickets = client.support.list_tickets(org_id)
ticket = client.support.create_ticket(
    org_id,
    subject="API returning 500 errors",
    category="technical",
    priority="high",
    message="Detailed description..."
)
ticket = client.support.get_ticket(org_id, ticket_id)
client.support.add_message(org_id, ticket_id, content="Follow-up message...")
```

## Data Types

All types are defined as Python `dataclasses` in `iteradian/types.py`:

| Type              | Key Fields                                                                                                                               |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `IteradianConfig` | `base_url`, `api_key?`, `timeout`, `max_retries`                                                                                         |
| `AuthTokens`      | `access_token`, `refresh_token`, `user`                                                                                                  |
| `TwoFASetup`      | `secret`, `qr_code`                                                                                                                      |
| `Organization`    | `id`, `name`, `slug`, `created_at`, `updated_at`                                                                                         |
| `OrgMember`       | `id`, `user_id`, `email`, `role`, `joined_at`                                                                                            |
| `ApiKey`          | `id`, `name`, `prefix`, `key?`, `environment`, `status`, `ip_allowlist?`, `allowed_networks?`, `rate_limit?`, `daily_limit?`             |
| `Network`         | `id`, `slug`, `name`, `chain_id`, `type`, `environment`, `is_active`                                                                     |
| `Endpoint`        | `id`, `organization_id`, `network_id`, `region`, `name`, `priority`, `status`, `is_enabled`, `timeout_ms`, `retry_count`, metrics fields |
| `RequestLog`      | `id`, `organization_id`, `method`, `status`, `status_code?`, `latency_ms?`, `network?`, `region?`, `timestamp`                           |
| `LogsResponse`    | `logs`, `total`, `page`, `page_size`, `total_pages`                                                                                      |
| `Alert`           | `id`, `organization_id`, `rule_id?`, `severity`, `status`, `title`, `message`, `metadata?`                                               |
| `AlertRule`       | `id`, `organization_id`, `name`, `metric`, `condition`, `threshold`, `severity`, `is_enabled`, `cooldown_minutes`                        |
| `Plan`            | `id`, `name`, `slug`, `price`, `currency`, `interval`, `features`, `limits`, `is_active`                                                 |
| `Subscription`    | `id`, `organization_id`, `plan_id`, `status`, `cancel_at_period_end`, `plan?`                                                            |
| `SupportTicket`   | `id`, `organization_id`, `subject`, `category`, `priority`, `status`                                                                     |

## Error Handling

```python
from iteradian import IteradianClient, IteradianError

client = IteradianClient(base_url="https://api.iteradian.com/api/v1")

try:
    client.auth.login(email="user@example.com", password="wrong")
except IteradianError as e:
    print(f"Status: {e.status_code}")   # 401
    print(f"Message: {e.message}")      # "Invalid credentials"
    print(f"Error: {e.error}")          # Error detail
except Exception as e:
    print(f"Network error: {e}")
```

### Retry Behavior

The SDK automatically retries requests on:

- **429 Too Many Requests** — with exponential backoff
- **5xx Server Errors** — with exponential backoff

Configure via `max_retries` (default: 3):

```python
client = IteradianClient(
    base_url="...",
    max_retries=5,   # Up to 5 retries
)
```

## Project Structure

```
sdks/python/
├── pyproject.toml           # Package metadata & build config
├── README.md                # This file
└── iteradian/
    ├── __init__.py          # Package exports
    ├── client.py            # IteradianClient & all resource classes (407 lines)
    ├── http.py              # HttpClient — httpx, retry, timeouts (107 lines)
    ├── exceptions.py        # IteradianError exception class
    └── types.py             # Dataclass types (235 lines)
```

## License

MIT © [Iteradian](https://iteradian.com)
