Metadata-Version: 2.4
Name: brightbean
Version: 1.0.0rc3
Summary: Official Python SDK for the BrightBean API — YouTube packaging scoring.
Project-URL: Homepage, https://brightbean.xyz
Project-URL: Documentation, https://api.brightbean.xyz/docs
Project-URL: Source, https://github.com/brightbean/brightbean
Project-URL: Changelog, https://github.com/brightbean/brightbean/releases
Author-email: BrightBean <support@brightbean.xyz>
License: MIT
Keywords: brightbean,ctr,scoring,thumbnail,title,youtube
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 :: Only
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 :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: attrs>=22.2
Requires-Dist: httpx<1,>=0.23
Requires-Dist: python-dateutil>=2.8
Requires-Dist: tenacity<10,>=8.2
Provides-Extra: dev
Requires-Dist: build>=1.0; extra == 'dev'
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: openapi-python-client>=0.28; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# brightbean — Python SDK

Official Python SDK for the [BrightBean API](https://brightbean.xyz). Score
YouTube packaging (titles, thumbnails, or both) for click-through-rate.

## Install

```bash
pip install brightbean
```

Requires Python 3.10+.

## Quickstart

```python
from brightbean import BrightBean

with BrightBean(api_key="bb_...") as client:
    result = client.score(
        title="10 Tips to Boost Your Coding Productivity",
        thumbnail_url="https://i.ytimg.com/vi/abc123/maxresdefault.jpg",
    )
    print(f"score={result.score:.2f} percentile={result.percentile}")
```

Mode is auto-detected on the server. Pass any combination of `title`,
`thumbnail_url`, and `thumbnail_base64`:

| Inputs | Mode | Credits |
|---|---|---|
| `title` only | `"title"` | 1 |
| `thumbnail_url` or `thumbnail_base64` only | `"thumbnail"` | 2 |
| `title` + thumbnail | `"combined"` | 3 |

## Async

```python
import asyncio
from brightbean import AsyncBrightBean

async def main():
    async with AsyncBrightBean(api_key="bb_...") as client:
        return await client.score(title="My epic video")

asyncio.run(main())
```

## Errors

The SDK raises typed exceptions for each documented status code:

| Status | Exception | Notes |
|---|---|---|
| 400 | `BadRequestError` | Validation failed. |
| 401 | `AuthenticationError` | Missing, invalid, or expired API key. |
| 402 | `InsufficientCreditsError` | Account is out of credits. |
| 429 | `RateLimitError` | Too many requests. `retry_after` (int seconds). |
| 503 | `ServiceUnavailableError` | Scoring service down. `retry_after` (int seconds). |
| 5xx | `ServerError` | Other server-side failure. |
| network | `NetworkError` | DNS, connection refused, TLS, timeout. |

All API errors inherit `BrightBeanAPIError`. Network failures raise
`NetworkError`. Both inherit `BrightBeanError` for catch-all handling.

## Retries

Idempotent GETs (`me()`, `usage()`) retry automatically on 5xx and network
errors with exponential backoff + jitter. Tune via `max_retries`:

```python
client = BrightBean(api_key="bb_...", max_retries=5)
```

`score()` is **never** auto-retried because it charges credits. Catch
`RateLimitError` or `ServiceUnavailableError` and back off using
`retry_after` if you need retry semantics.

## Configuration

```python
BrightBean(
    api_key="bb_...",                   # required
    base_url="https://api.brightbean.xyz",
    timeout=30.0,                       # seconds, or httpx.Timeout(...)
    max_retries=3,                      # idempotent GETs only
    user_agent=None,                    # override the default UA string
)
```

## Account info

```python
me = client.me()              # plan, credits remaining, period end
usage = client.usage()        # balance + last 20 ledger entries
```

## Development

```bash
pip install -e .[dev]
openapi-python-client generate \
    --path ../../openapi.yaml \
    --config generator.yaml \
    --meta=none \
    --output-path ./brightbean/_generated \
    --overwrite
pytest && ruff check . && mypy brightbean
```

The `brightbean/_generated/` directory is regenerated from the upstream
OpenAPI spec. The hand-written facade (`brightbean/_client.py`,
`brightbean/errors.py`, `brightbean/__init__.py`) wraps it to give the
ergonomic `BrightBean` import shape.
