Skip to main content
This tutorial finds breakout posts: you’ll list your tracked videos, ask for batched engagement deltas over a look-back window, then flag the fastest movers by view or engagement change. The result is a short list of posts worth a closer look.
Read fields off the live delta response rather than assuming names. You’ll need an API key first — see API setup or create one on the API keys page — and see the API reference for the shared conventions.
1

Set up a client with error parsing

Wrap requests so any non-2xx response surfaces the RFC 9457 code, detail, and the X-Request-ID — you’ll want both when a batch call fails.
from __future__ import annotations

import os
from typing import Any

import httpx

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


class CreatorAuditError(Exception):
    def __init__(self, *, status: int, code: str, detail: str, request_id: str | None):
        super().__init__(f"[{status} {code}] {detail}")
        self.status = status
        self.code = code
        self.detail = detail
        self.request_id = request_id


client = httpx.Client(
    base_url=BASE_URL,
    headers={"Authorization": f"Bearer {os.environ['CREATORAUDIT_API_KEY']}"},
    timeout=30.0,
)


def call(method: str, path: str, **kwargs: Any) -> dict[str, Any]:
    resp = client.request(method, path, **kwargs)
    if resp.is_error:
        try:
            problem = resp.json()
        except ValueError:
            problem = {}
        raise CreatorAuditError(
            status=problem.get("status", resp.status_code),
            code=problem.get("code", "INTERNAL_ERROR"),
            detail=problem.get("detail", resp.text),
            request_id=resp.headers.get("X-Request-ID"),
        )
    return resp.json()
2

Page through your tracked videos

GET /v2/account-videos lists the videos discovered for your tracked accounts (use GET /v2/videos if you track individual posts). Follow the cursor: read pagination.has_next and pass the previous pagination.next_cursor back as cursor, stopping when has_next is false. Collect each video id.
def collect_video_ids(path: str) -> list[str]:
    ids: list[str] = []
    cursor: str | None = None
    while True:
        params: dict[str, Any] = {"limit": 200}
        if cursor is not None:
            params["cursor"] = cursor

        page = call("GET", path, params=params)
        ids.extend(item["id"] for item in page["data"])

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


video_ids = collect_video_ids("/account-videos")
print(f"{len(video_ids)} videos")
3

Request engagement deltas in batches

POST /v2/videos/deltas takes up to 200 video_ids and optional windows — look-back periods in days (1–365, default [1, 7, 14, 30]) to compute each delta over. Send your ids in batches of 200, with a tight window like [7] for week-over-week movement, and read the per-video deltas off the live response.
def chunked(items: list[str], size: int = 200):
    for i in range(0, len(items), size):
        yield items[i : i + size]


deltas: list[dict[str, Any]] = []
for batch in chunked(video_ids, 200):
    result = call(
        "POST",
        "/videos/deltas",
        json={"video_ids": batch, "windows": [7]},
    )
    deltas.extend(result["data"])
GET /v2/videos/deltas accepts the same request as query parameters (video_ids repeated, optional windows) when a GET fits your tooling better.
4

Flag the breakouts

Sort the deltas by the change metric you care about — view growth for raw reach, or the engagement change for quality — and flag the top movers. Read the delta fields from the live response; here we sort on a views change and treat the top slice as breakouts.
def view_delta(row: dict[str, Any]) -> float:
    # Read whichever change field the delta response carries for the window.
    return float(row.get("views_delta", row.get("views", 0)) or 0)


ranked = sorted(deltas, key=view_delta, reverse=True)
breakouts = ranked[:10]

for row in breakouts:
    print(row.get("id"), "Δviews(7d):", int(view_delta(row)))

client.close()
A newly tracked video has last_scrape_time: null and no baseline yet, so its delta is meaningless until at least two refreshes have landed. Skip videos without history, or re-run once the window has filled — see Data freshness.

Error handling

The RFC 9457 problem shape the client parses.

Rate limits

Back off on 429 when looping over large video libraries.

Python client

A fuller typed client to build these loops on.