> ## Documentation Index
> Fetch the complete documentation index at: https://docs.creatoraudit.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Python

> A small, typed CreatorAudit API client built on httpx.

This page shows a compact Python client for the CreatorAudit API. It sets the
bearer header and base URL once, follows cursor pagination automatically, and
turns [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) error responses into a
typed exception that carries the `code`, `detail`, and request id.

<Info>
  The examples use [`httpx`](https://www.python-httpx.org/) (`pip install httpx`). The
  same shapes work with `requests` — swap the transport calls. Create a key on the [API
  keys page](https://app.creatoraudit.com/app/api-keys) and pass it as a bearer token.
</Info>

## The client

```python client.py theme={null}
from __future__ import annotations

import os
from collections.abc import Iterator
from typing import Any

import httpx

BASE_URL = "https://api.creatoraudit.com/v2"


class CreatorAuditError(Exception):
    """An RFC 9457 problem document returned by the API."""

    def __init__(
        self,
        *,
        status: int,
        type: str,
        title: str,
        detail: str,
        code: str,
        request_id: str | None,
    ) -> None:
        super().__init__(f"[{status} {code}] {title}: {detail}")
        self.status = status
        self.type = type
        self.title = title
        self.detail = detail
        self.code = code
        self.request_id = request_id


class CreatorAudit:
    def __init__(self, api_key: str, base_url: str = BASE_URL) -> None:
        self._client = httpx.Client(
            base_url=base_url,
            headers={"Authorization": f"Bearer {api_key}"},
            timeout=30.0,
        )

    def request(
        self,
        method: str,
        path: str,
        *,
        params: dict[str, Any] | None = None,
        json: dict[str, Any] | None = None,
    ) -> dict[str, Any]:
        response = self._client.request(method, path, params=params, json=json)
        if response.is_error:
            self._raise_for_problem(response)
        return response.json()

    @staticmethod
    def _raise_for_problem(response: httpx.Response) -> None:
        request_id = response.headers.get("X-Request-ID")
        # Errors are application/problem+json; fall back gracefully if a
        # non-JSON body sneaks through (e.g. a proxy 502).
        try:
            problem = response.json()
        except ValueError:
            problem = {}
        raise CreatorAuditError(
            status=problem.get("status", response.status_code),
            type=problem.get("type", "about:blank"),
            title=problem.get("title", response.reason_phrase),
            detail=problem.get("detail", response.text),
            code=problem.get("code", "INTERNAL_ERROR"),
            request_id=request_id,
        )

    def close(self) -> None:
        self._client.close()
```

## Identify your key

`GET /whoami` confirms the key works and returns the organization it belongs to.

```python theme={null}
client = CreatorAudit(os.environ["CREATORAUDIT_API_KEY"])

me = client.request("GET", "/whoami")["data"]
print(me["organization_id"], me.get("api_key_name"))
```

## Paginate any list endpoint

List endpoints return `{ "data": [...], "pagination": { next_cursor, has_next, limit } }`.
This helper yields every item across pages by passing the previous
`next_cursor` back as `cursor` and stopping when `has_next` is `false`.

```python theme={null}
def paginate(
    client: CreatorAudit,
    path: str,
    *,
    params: dict[str, Any] | None = None,
    limit: int = 200,
) -> Iterator[dict[str, Any]]:
    """Yield every item from a cursor-paginated endpoint."""
    cursor: str | None = None
    while True:
        page_params: dict[str, Any] = {**(params or {}), "limit": limit}
        if cursor is not None:
            page_params["cursor"] = cursor

        page = client.request("GET", path, params=page_params)
        yield from page["data"]

        pagination = page["pagination"]
        if not pagination["has_next"]:
            break
        cursor = pagination["next_cursor"]


# Walk every tracked TikTok account:
accounts = list(paginate(client, "/accounts", params={"platform": "tiktok"}))
print(f"{len(accounts)} accounts")
```

<Note>
  Treat `next_cursor` as opaque — pass back the exact value you received. See
  [Pagination](/api-reference/pagination) for the full contract.
</Note>

## Track an account

`POST /accounts` requires `platform` (`instagram` or `tiktok`) and `username`.
Optional fields tune how the account is tracked.

```python theme={null}
created = client.request(
    "POST",
    "/accounts",
    json={
        "platform": "tiktok",
        "username": "creatoraudit",
        "category": "tech",
        "scrape_interval_hours": 24,
    },
)["data"]
print("tracking", created["id"])
```

To track a single video instead, `POST /videos` takes `platform` and an
`identifier` (a full URL, a numeric id, or — for Instagram — a bare shortcode):

```python theme={null}
video = client.request(
    "POST",
    "/videos",
    json={
        "platform": "tiktok",
        "identifier": "https://www.tiktok.com/@creatoraudit/video/712345",
        "is_active": True,
    },
)["data"]
print("tracking video", video["id"])
```

## Organization overview

`GET /overview` accepts a `period` of `7d`, `30d`, or `90d`.

```python theme={null}
overview = client.request("GET", "/overview", params={"period": "30d"})["data"]
print(overview["current"], overview["changes"])
```

## Handling errors

Any non-2xx response raises `CreatorAuditError` with the parsed problem fields
and the `X-Request-ID`, so you can branch on `code` and include the request id
when reporting an issue.

```python theme={null}
try:
    client.request("POST", "/accounts", json={"platform": "tiktok"})  # missing username
except CreatorAuditError as exc:
    if exc.code == "VALIDATION_ERROR":
        print("fix the request body:", exc.detail)
    print("status:", exc.status, "request-id:", exc.request_id)
finally:
    client.close()
```

## Next steps

* [Introduction](/api-reference/introduction) — auth, base URL, and conventions
* [Pagination](/api-reference/pagination) — the cursor contract in detail
* [Quickstart](/quickstart) — your first request end to end
