# modelpricing-ai

Python client for the [ModelPricing.ai](https://modelpricing.ai) API — estimate LLM usage costs and track spending with a single call.

## Installation

```bash
pip install modelpricing-ai
```

For async support (requires [aiohttp](https://docs.aiohttp.org)):

```bash
pip install modelpricing-ai[async]
```

## Quick Start

```python
from modelpricing_ai import ModelPricingClient

with ModelPricingClient(api_key="YOUR_API_KEY") as client:
    estimate = client.estimate(
        model="gpt-4o-mini",
        tokens_in=1000,
        tokens_out=500,
        trace_id={"requestId": "abc-123"},
    )
    print(f"Cost: ${estimate.total:.6f}")
```

## Async Usage

Install the `async` extra, then use `AsyncModelPricingClient` as an async context manager:

```python
import asyncio
from modelpricing_ai import AsyncModelPricingClient

async def main():
    async with AsyncModelPricingClient(api_key="YOUR_API_KEY") as client:
        estimate = await client.estimate(
            model="gpt-4o-mini",
            tokens_in=1000,
            tokens_out=500,
            trace_id={"requestId": "abc-123"},
        )
        print(f"Cost: ${estimate.total:.6f}")

asyncio.run(main())
```

## Response Structure

Both `estimate()` and `await estimate()` return an `EstimateResponse` object:

```python
estimate.total        # float — total USD cost
estimate.model        # str   — canonical model name
estimate.traceId      # dict | None — your pass-through trace ID
estimate.breakdown    # EstimateBreakdownGroup
  .input              # EstimateBreakdown
    .unit             #   str   — e.g. "token"
    .branch           #   str   — pricing tier that matched
    .qty              #   int   — number of input tokens
    .rate             #   float — per-unit rate
    .subtotal         #   float — input cost
  .output             # EstimateBreakdown (same fields for output tokens)
```

## Configuration

| Parameter     | Default                         | Description                                                              |
| ------------- | ------------------------------- | ------------------------------------------------------------------------ |
| `api_key`     | _required_                      | Your ModelPricing.ai API key (also reads `MODELPRICING_API_KEY` env var) |
| `base_url`    | `"https://api.modelpricing.ai"` | API base URL (also reads `MODELPRICING_BASE_URL` env var)                |
| `timeout`     | `30.0`                          | Request timeout in seconds                                               |
| `max_retries` | `3`                             | Maximum retry attempts for transient errors                              |
| `session`     | `None`                          | Optional `requests.Session` (sync) or `aiohttp.ClientSession` (async)    |

Parameters are resolved in order: constructor argument > environment variable > default.

```python
client = ModelPricingClient(
    api_key="YOUR_API_KEY",
    base_url="https://api.modelpricing.ai",
    timeout=30.0,
    max_retries=3,
)
```

## Error Handling

The client raises typed exceptions for different failure modes:

| Exception         | HTTP Status | When                          |
| ----------------- | ----------- | ----------------------------- |
| `Unauthorized`    | 401         | Invalid or missing API key    |
| `ValidationError` | 422         | Invalid model name or metrics |
| `NotFound`        | 404         | Unknown endpoint              |
| `ServerError`     | 5xx         | Server-side failures          |

All exceptions inherit from `ModelPricingError` and include a `status_code` attribute.

```python
from modelpricing_ai.errors import Unauthorized, ValidationError, ServerError

try:
    estimate = client.estimate(model="gpt-4o-mini", tokens_in=1000, tokens_out=500)
except Unauthorized:
    print("Check your API key")
except ValidationError as e:
    print(f"Bad request: {e}")
except ServerError:
    print("Server error — will be retried automatically")
```

## Retry Behavior

The client automatically retries on transient errors with exponential backoff:

- **Retries**: 5xx server errors and network/connection errors
- **No retry**: 4xx client errors (401, 404, 422)
- **Default**: 3 retries with exponential backoff (0.1 s initial, 2 s max)

```python
# Increase retries for unreliable networks
client = ModelPricingClient(api_key="YOUR_API_KEY", max_retries=5)

# Disable retries (no retry attempts)
client = ModelPricingClient(api_key="YOUR_API_KEY", max_retries=0)
```

## License

MIT
