# GeoScored Python SDK

Typed async Python client for the [GeoScored](https://geoscored.ai) GEO audit API.

## Installation

```bash
pip install geoscored
```

## Quickstart

```python
import asyncio
from geoscored import GeoScoredClient

async def main():
    async with GeoScoredClient(api_key="geo_your_key_here") as client:
        # Create a scan
        scan = await client.create_scan(
            "https://example.com",
            brand_name="Example Corp",
        )
        print(f"Scan created: {scan.id}")

        # Wait for completion
        scan = await client.poll_scan(scan.id)
        print(f"Score: {scan.overall_score} ({scan.grade})")

        # Get the full report
        report = await client.get_report(scan.id)
        for check in report.checks:
            print(f"  [{check.severity}] {check.check_name}: {check.score}")

asyncio.run(main())
```

## Sandbox mode

Use a test key (starting with `geo_test_`) to get deterministic mock responses
without creating real scans:

```python
async with GeoScoredClient(api_key="geo_test_demo") as client:
    scan = await client.create_scan("https://example.com")
    # Returns instantly with status="complete" and fixed scores
```

## API Reference

### `GeoScoredClient(api_key, base_url="https://geoscored.ai", timeout=30.0)`

All methods are async and return typed Pydantic models.

| Method | Returns | Description |
|--------|---------|-------------|
| `create_scan(url, brand_name, callback_url)` | `Scan` | Create a new GEO audit scan |
| `get_scan(scan_id)` | `Scan` | Get scan status and scores |
| `list_scans(limit, status, starting_after)` | `ScanList` | List scans with pagination |
| `get_report(scan_id)` | `Report` | Get full audit report |
| `list_checks(scan_id)` | `CheckList` | List all check results |
| `get_check(scan_id, check_id)` | `Check` | Get a specific check |
| `delete_scan(scan_id)` | `None` | Delete a scan |
| `export_scans(days)` | `ExportData` | Export scan history |
| `poll_scan(scan_id, interval, timeout)` | `Scan` | Poll until complete/failed |

### Error handling

All API errors raise `GeoScoredError` with structured fields:

```python
from geoscored import GeoScoredClient, GeoScoredError

try:
    scan = await client.get_scan("nonexistent")
except GeoScoredError as e:
    print(e.status_code)  # 404
    print(e.code)         # "scan_not_found"
    print(e.message)      # "Scan not found."
    print(e.request_id)   # "req_7f3a2b1c..."
```

## Requirements

- Python 3.10+
- httpx >= 0.28.0
- pydantic >= 2.0
