Metadata-Version: 2.4
Name: account-manager-sdk
Version: 1.0.0
Summary: Async Python client for the Account Manager API
Author-email: Maksim Anufriev <maximunya.dev@gmail.com>
License: MIT License
        
        Copyright (c) 2026 Maksim Anufriev
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Repository, https://github.com/maximunya/acc-manager-sdk
Project-URL: Issues, https://github.com/maximunya/acc-manager-sdk/issues
Keywords: sdk,api-client,proxy,accounts,async
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: OS Independent
Classifier: License :: OSI Approved :: MIT License
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: aiohttp>=3.13.2
Requires-Dist: pydantic>=2.12.4
Requires-Dist: pydantic-settings>=2.12.0
Dynamic: license-file

# account-manager-sdk

[![PyPI version](https://img.shields.io/pypi/v/account-manager-sdk?color=blue)](https://pypi.org/project/account-manager-sdk/)
[![Python](https://img.shields.io/pypi/pyversions/account-manager-sdk)](https://pypi.org/project/account-manager-sdk/)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE)
[![PyPI downloads](https://img.shields.io/pypi/dm/account-manager-sdk)](https://pypi.org/project/account-manager-sdk/)

**A Python async client for working with accounts and proxies through Account Manager API.**

Handles account rotation, rate limits, retries, and proxy management — so you can focus on the actual request logic.

---

## Features

| Feature | Description |
|---|---|
| **Auto-rotation** | Gets a fresh account + proxy pair on each call |
| **Rate limiting** | Tracks per-account limits and enforces delays |
| **Retries** | Automatic retries on 5xx / 429 / network errors with exponential backoff |
| **Auth strategies** | `TokenAuth()`, `PasswordAuth()`, `CookieAuth()` |
| **Usage reporting** | `require_report=True` — auto-sends usage stats after each request |
| **Proxy management** | Auto-applies proxies, disables them on proxy errors |

---

## Installation

```bash
pip install account-manager-sdk
```

**Requirements:** Python 3.10+, `aiohttp`, `pydantic`, `pydantic-settings`

---

## Quick start

```python
import asyncio
from account_manager import AccountManagerClient, TokenAuth

async def main():
    async with AccountManagerClient(api_key="YOUR_API_KEY") as client:
        pair = await client.get_pair("your_account_type")
        async with pair:
            resp = await pair.request(
                "https://api.example.com/check",
                params={"query": "value"},
                auth_strategy=TokenAuth(auth_header="Key"),
            )
            print(resp.json)

asyncio.run(main())
```

---

## API Reference

### Single pair

```python
async with AccountManagerClient(api_key="...") as client:
    pair = await client.get_pair("account_type")
    async with pair:
        resp = await pair.request("https://api.example.com")
```

### Bulk requests

```python
async for pair, url in client.iter_pairs(
    account_type="account_type",
    items=["url1", "url2", "url3"],
    limit=10,
    require_report=True,
):
    async with pair:
        resp = await pair.request(url)
```

### Auth strategies

```python
from account_manager import TokenAuth, PasswordAuth, CookieAuth

# Bearer / API key header
token_auth = TokenAuth(
    auth_header="Authorization",
    prefix="Bearer",
)

# Login + password (posts credentials, stores session cookies)
password_auth = PasswordAuth(
    login_url="https://site.com/login",
    auth_request_method="POST",
    payload_map={"email": "login", "password": "password"},
)
```

### `request()` parameters

```python
await pair.request(
    url="https://api.example.com",
    method="GET",               # GET / POST / PUT / PATCH / DELETE
    use_proxy=True,             # whether to use a proxy from Account Manager
    auth_strategy=TokenAuth(),

    # request params (passed through to aiohttp)
    params={"page": 1},
    json={"data": "value"},
    data={"name": "user"},
    headers={"User-Agent": "..."},
    cookies={"session": "abc"},

    # transport settings
    timeout=30,                 # request timeout in seconds
    retries=3,                  # retry attempts on 429 / 5xx / network error
    delay_multiplier=0.5,       # exponential backoff multiplier (0.5s → 1s → 2s)
)
```

### `TransportResponse` fields

| Field | Type | Description |
|---|---|---|
| `ok` | `bool` | `True` if `status_code < 400` |
| `status_code` | `int \| None` | HTTP status code |
| `reason` | `str \| None` | HTTP reason phrase |
| `text` | `str \| None` | Raw response text |
| `json` | `Any \| None` | Parsed JSON body |
| `headers` | `Mapping[str, str] \| None` | Response headers |
| `cookies` | `SimpleCookie \| None` | Response cookies |
| `url` | `str \| None` | Final URL (after redirects) |
| `proxy` | `str \| None` | Proxy that was used |
| `method` | `str \| None` | Request method |
| `elapsed` | `float \| None` | Request duration in seconds |
| `attempt` | `int \| None` | Successful attempt number |
| `exception` | `Exception \| None` | Exception raised, if any |
| `error` | `str \| None` | Serialized error string |

**Convenience properties:**

| Property | Type | Description |
|---|---|---|
| `result` | `str \| None` | Human-readable result summary |
| `is_proxy_error` | `bool` | `True` if the error was proxy-related |

---

## Configuration

SDK uses `pydantic-settings` for configuration. All settings can be overridden via environment variables with the `ACCOUNT_MANAGER_` prefix.

```python
from account_manager.config import settings

print(settings.API_BASE_URL)
print(settings.DEFAULT_RETRIES)
```

| Parameter | Type | Default | Description |
|---|---|---|---|
| `API_BASE_URL` | `str` | `"https://..."` | Account Manager API base URL |
| `MIN_SECONDS_BETWEEN_REQUESTS` | `float` | `0.25` | Min pause between requests per client |
| `DEFAULT_RETRIES` | `int` | `3` | Retry attempts on network / 5xx / 429 |
| `DEFAULT_RETRY_DELAY_MULTIPLIER` | `float` | `0.5` | Exponential backoff multiplier |
| `ITER_PAIRS_MAX_CONSECUTIVE_ERRORS` | `int` | `10` | Max consecutive errors before stopping iteration |

Example `.env`:

```dotenv
ACCOUNT_MANAGER_DEFAULT_RETRIES=5
ACCOUNT_MANAGER_MIN_SECONDS_BETWEEN_REQUESTS=0.1
```

---

## Examples

See the [`examples/`](./examples/) folder:

| Scenario | File |
|---|---|
| Single request | [`basic_usage.py`](./examples/basic_usage.py) |
| Bulk requests | [`iter_pairs_usage.py`](./examples/iter_pairs_usage.py) |
| Login / password auth | [`password_auth_usage.py`](./examples/password_auth_usage.py) |
| VirusTotal API | [`virustotal_real.py`](./examples/virustotal_real.py) |

---

## Contributing

1. Clone the repo and install dependencies including the dev group: `uv sync`
2. Make your changes
3. Run the build to make sure it's packaged correctly: `uv build`
4. Push your branch and open a PR

Releases are published to PyPI automatically by CI when a maintainer pushes a `vX.Y.Z` tag — no manual `twine upload` needed.

---

## License

[MIT](./LICENSE)
