Metadata-Version: 2.5
Name: creacortex
Version: 0.3.1
Summary: Python client for the Creacortex video-creative attention-analysis API
Project-URL: Homepage, https://creacortex.ai
Author: Creacortex
License: MIT
License-File: LICENSE
Keywords: advertising,analytics,api,attention,creacortex,video
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3.9
Requires-Dist: httpx>=0.24
Provides-Extra: dev
Requires-Dist: mypy>=1.8; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Description-Content-Type: text/markdown

# creacortex

Python client for the **Creacortex** video-creative attention-analysis API.

Upload a video creative, run it through the pipeline, and pull back per-second
attention data, a Gen-AI optimization payload, and PDF reports.

## Install

```bash
pip install creacortex
```

## Quick start

```python
from creacortex import Client

with Client("ace_your_api_key") as cx:
    # one call: upload + start + wait for completion
    run = cx.analyze("ad.mp4", with_audio=True, creative_id="summer-promo",
                     app_or_game="Raid Shadow Legends", platform="AppLovin")

    csv = cx.get_csv(run.run_id)                 # per-second analysis (bytes)
    payload = cx.get_export(run.run_id)          # Gen-AI insights (dict)
    pdf = cx.get_report(run.run_id, "diagnose")  # PDF report (bytes)
```

Get an API key from **Usage & billing → API keys** in the dashboard. It is sent
as the `X-API-Key` header.

## Async

```python
import asyncio
from creacortex import AsyncClient

async def main():
    async with AsyncClient("ace_your_api_key") as cx:
        run = await cx.analyze("ad.mp4")
        print(await cx.get_export(run.run_id))

asyncio.run(main())
```

## Step by step

`analyze()` is a convenience over the raw endpoints, which you can also call directly:

```python
video_id = cx.upload_video("ad.mp4")
run = cx.create_run(video_id, with_audio=True, audience="men 20-45, mobile gamers")
run = cx.wait(run.run_id)                # poll until done (or RunFailedError / WaitTimeout)

for item in cx.list_runs():
    print(item.run_id, item.status, item.ready)

print(cx.get_account().balance)          # credits left
```

`upload_video` / `analyze` accept a file path, raw `bytes`, or a binary file object.

## Segments: app or game × platform

A forecast is only meaningful against comparable creatives, so runs can be tagged with an
**app or game** and a **platform**. When both are set, the prediction and the percentiles are
computed **only from creatives of that same pair**.

```python
run = cx.create_run(video_id, app_or_game="Raid Shadow Legends", platform="AppLovin")
run.app_or_game        # "Raid Shadow Legends" — canonical spelling as stored
```

Tags are optional. Leave them out and the run stays untagged: the forecast is compared against
all videos in the project, exactly as before. Pass **both or neither** — one alone is rejected.

Unknown values are created automatically, and spelling is normalised: `"  applovin "` resolves to
the existing `"AppLovin"` instead of creating a duplicate. Platforms come with a shared base list
(AppLovin, Unity Ads, Mintegral, ironSource, Liftoff, Moloco, Meta Ads, Google Ads, TikTok Ads,
Pangle, Chartboost, Digital Turbine); apps and games are private to your account.

Check what a forecast would be based on before you spend credits:

```python
segs = cx.get_segments()
segs.apps_or_games          # your apps
segs.platforms              # base list + your own
segs.min_size               # 50 — below this a forecast is flagged as provisional

for pair in segs.pairs:
    print(pair.app_or_game, pair.platform, pair.size, pair.state, pair.forecastable)
```

Pair states: **`ok`** (>= `min_size`) — normal forecast; **`thin`** (1..`min_size`-1) — returned
but provisional; **`empty`** (no data) — **no forecast is issued** for that run. Upload calibration
data for the pair to unlock it.

## Calibration

Improve prediction accuracy (and unlock all metrics) by calibrating on your real performance data.
Any CSV works — map your columns to our fields. Calibration is fitted **per segment**, so
`app_or_game` and `platform` are required on commit: they tag the whole file.

Exports from Excel are handled as-is — semicolon separators, a leading BOM and decimal commas
(`3,55`) are all detected automatically.

```python
csv = open("performance.csv", "rb").read()
a = cx.calibration_analyze(csv)                       # detected columns + guessed mapping
mapping = {**a["guess"], "creative_id": "Ad name"}    # adjust if needed
cx.calibration_preview(csv, mapping, min_impressions=50000)   # cost + impression split
cx.calibration_commit(csv, mapping, process_unmatched=True,   # charge + fit; returns summary
                      app_or_game="Raid Shadow Legends", platform="AppLovin")

cx.get_account().calibrated   # True once fitted
```

## Errors

| Exception | When |
|---|---|
| `AuthError` | invalid/missing API key (401) |
| `InsufficientCreditsError` | not enough credits (402) |
| `RateLimitError` | too many requests (429) |
| `NotFoundError` | unknown run (404) |
| `RunFailedError` | the run finished with `status="failed"` |
| `WaitTimeout` | `wait()` exceeded its timeout |
| `ApiError` | any other HTTP error (has `.status_code`) |

All inherit from `CreacortexError`.

## Configuration

```python
Client("ace_...", base_url="https://api.creacortex.ai", timeout=60.0)
```

## License

MIT
