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

# Advertiser API Overview

> Programmatically create and manage Thrad ad campaigns and read combined Thrad + ChatGPT performance insights over a single REST surface.

The Thrad Advertiser API lets you build and run ad campaigns end-to-end from your own code — create the campaign tree, launch it, manage its lifecycle, and read back performance — without touching the Thrad Platform dashboard.

It is a plain REST surface: predictable resource paths, JSON in and out, one Bearer key. Responses are **bare** objects and lists rather than a wrapper envelope, money is in **integer micros**, and timestamps are **ISO 8601 strings**. Read [Conventions](#conventions) once and every endpoint on this site behaves the way you expect.

Insights numbers combine **Thrad** and **ChatGPT (OpenAI Ads)** supply by default, so a single read gives you the full picture across both surfaces.

## Base URL

```
https://api.thrad.ai/v1
```

Every endpoint path below is relative to this base URL. Paths use underscores, public UUID ids, and **no trailing slash**.

## Resource hierarchy

Resources nest four levels deep. A campaign owns its ad groups, and each ad group owns its ads. The ad account is the top scope — it is the advertiser organization itself.

```
Ad Account
└── Campaign
    └── Ad Group
        └── Ad
```

* **Ad Account** — the org's default ad account. Read-only; it is the root scope for account-level insights.
* **Campaign** — name, budget, schedule, and targeting. Created as one nested call (campaign + `ad_groups[]`, each with `ads[]`) that auto-launches on submit.
* **Ad Group** — `context_hints` and a `bidding_config.max_bid_micros`. Add ad groups to an existing campaign.
* **Ad** — the copy (`card` or `poll`) plus a required `asset_id`. The referenced [asset](https://docs.thrad.ai/advertisers/api-reference/assets.md) supplies the image and the destination URL.

## Two surfaces

The API is split into two surfaces that share authentication but differ in shape and purpose.

> [card] Resources
>
> Write surface. CRUD over campaigns, ad groups, and ads, plus `/activate` `/pause` `/archive` lifecycle actions, and read-only access to the ad account and the asset catalog. Returns **bare** list/object shapes. Resource money is in **integer micros**.

> [card] Insights
>
> Read-only analytics across the four resource levels. Combines Thrad + ChatGPT supply by default. Returns a **bare list** of metric rows where money is an integer `*_micros` value paired with a formatted `*_display` string.

## Endpoints at a glance

| Resource | Operation | Method & path |
| - | - | - |
| Ad account | Get | `GET /ad_account` |
| Asset | List | `GET /assets` |
| Asset | Get | `GET /assets/{id}` |
| Campaign | List | `GET /campaigns` |
| Campaign | Create | `POST /campaigns` |
| Campaign | Get | `GET /campaigns/{id}` |
| Campaign | Update | `POST /campaigns/{id}` |
| Campaign | Activate / Pause / Archive | `POST /campaigns/{id}/activate` · `/pause` · `/archive` |
| Ad group | List | `GET /ad_groups?campaign_id=` |
| Ad group | Create | `POST /ad_groups` |
| Ad group | Get | `GET /ad_groups/{id}` |
| Ad group | Update | `POST /ad_groups/{id}` |
| Ad group | Activate / Pause / Archive | `POST /ad_groups/{id}/activate` · `/pause` · `/archive` |
| Ad | List | `GET /ads?ad_group_id=` |
| Ad | Create | `POST /ads` |
| Ad | Get | `GET /ads/{id}` |
| Ad | Update | `POST /ads/{id}` |
| Ad | Activate / Pause / Archive | `POST /ads/{id}/activate` · `/pause` · `/archive` |
| Insights | Ad account | `GET /ad_account/insights` |
| Insights | Campaign | `GET /campaigns/{id}/insights` |
| Insights | Ad group | `GET /ad_groups/{id}/insights` |
| Insights | Ad | `GET /ads/{id}/insights` |

## Conventions

### Authentication

Every request is authenticated with an org-bound Bearer key on the `Authorization` header.

**Authorization** `string` *required*

`Bearer ak_...`. There is **one active key per organization**. Create the key in the Thrad Platform dashboard under **Settings → API keys**. Store it as the environment variable `THRAD_ADS_API_KEY` and never commit it to source control.

```bash theme={null}
curl https://api.thrad.ai/v1/ad_account \
  -H "Authorization: Bearer $THRAD_ADS_API_KEY"
```

A missing or invalid key is rejected with a uniform `401` — see [Authentication](https://docs.thrad.ai/advertisers/api-reference/authentication.md).

### Response shapes

Resource and insights responses are **bare** — a single object, or a list object. There is no `{ success, data, error, meta }` wrapper.

A **list** response wraps its rows and carries `"object": "list"`. Single-object responses (a campaign, ad group, ad, asset, or ad account) are returned **bare** — they do **not** include an `object` field.

```json theme={null}
{
  "object": "list",
  "data": [ ... ],
  "count": 1,
  "first_id": "...",
  "last_id": "...",
  "has_more": false
}
```

An error response carries a single `error` object:

```json theme={null}
{
  "error": {
    "message": "...",
    "type": "invalid_request_error",
    "param": "time_granularity",
    "code": "invalid_value"
  }
}
```

`type` is one of `invalid_request_error`, `authentication_error`, `rate_limit_error`, or `server_error`.

### Money — one scale, two shapes

Every money amount on `/v1/` is in **integer micros**, where `$1 = 1,000,000`. Resources take and return the raw integer; insights pair each integer with a formatted string.

* **Resource bodies.** `budget.amount_micros` has a minimum of `990000` (`$0.99`) and **no upper cap**. `bidding_config.max_bid_micros` ranges `100`–`100000000` (`$0.0001`–`$100`).
* **Resource reads add a display string.** A campaign reads back `budget.display` — the amount and its period resolved into one string, e.g. `"$1,000.00 per week"`.
* **Insights rows** carry `spend_micros`, `cpc_micros`, and `cpm_micros` (same micros scale) each paired with a `*_display` string (`"$0.70"`), plus `ctr_ratio` (a 0–1 fraction) paired with `ctr_display` (`"10.00%"`).

> [!TIP]
>
> The `*_micros` integer is the canonical, parseable value. `*_display` is for humans — its formatting may change, so never parse it.

### Currency

Currency is an **account-level invariant**: every campaign, ad group, and ad under an account is denominated in the same currency, and writes carry no per-entity currency. `GET /ad_account` exposes a lowercase ISO `currency` (`"usd"`); child resources inherit it. Insights rows carry a per-row `currency` because they travel as a flat list with no account object to reference.

### Timestamps

All `/v1/` timestamps — `start_time`, `end_time`, `created_at`, `updated_at`, `archived_at`, and the insights `time_ranges[]` bounds — are **ISO 8601 / RFC 3339 strings in UTC**, in and out:

```
2026-01-01T00:00:00.000000Z
```

> [!WARNING]
>
> No endpoint accepts a Unix integer anywhere. On input, a timestamp **must** carry a UTC offset or trailing `Z` — a bare wall-clock time is ambiguous and is rejected with `400 invalid_value` rather than assumed to be UTC.

### Status and review

Each campaign, ad group, and ad exposes a `status`, and campaigns and ads expose a read-only `review_status`.

| Field | Values |
| - | - |
| `status` | `active` · `paused` · `archived` |
| `review_status` (read-only) | `approved` · `in_review` · `rejected` |

> [!WARNING]
>
> **`status` is never writable through an update body** — not on campaigns, and not on ad groups or ads. Sending `status` in any update body returns `400 use_status_action`. Status moves only through the `/activate`, `/pause`, and `/archive` sub-actions, which validate the transition.

> [!NOTE]
>
> Thrad ad groups and ads have no separate archived state, so `/archive` on a child deactivates it and it **reads back as `paused`** — the same result as `/pause`.

### Pagination

List endpoints are cursor-paginated. The `limit` range depends on the surface: **resource** list endpoints (campaigns, ad groups, ads) accept `1`–`500`, while **insights** list endpoints accept `1`–`2000`. Both default to `20`.

**limit** `integer` (default: 20)

Number of items per page. Resource lists: `1`–`500`. Insights lists: `1`–`2000`.

**before** `string`

Return items before this cursor. Echo a prior response's `first_id`. Mutually exclusive with `after`.

**after** `string`

Return items after this cursor. Echo a prior response's `last_id`. Mutually exclusive with `before`.

**order** `string` (default: desc)

Sort direction: `asc` or `desc`.

### Rate limits

The API is throttled to **1000 requests per hour per key**. The charging `POST /v1/campaigns` create call is additionally limited to **60 per hour**.

### Idempotency

`POST /v1/campaigns` accepts an `Idempotency-Key` header so a retried create never charges twice.

**Idempotency-Key** `string`

A client-generated key. The same key replays the original response indefinitely — there is **no expiry window**. The same key with a **different** body returns `409 idempotency_key_reused`; re-sending while the first request is still in flight returns `409 idempotency_key_in_use`.

## Next steps

> [card] [Quickstart](https://docs.thrad.ai/advertisers/quickstart.md)
>
> Mint a key and create, launch, and read insights for your first campaign with cURL.

> [card] [Authentication](https://docs.thrad.ai/advertisers/api-reference/authentication.md)
>
> How the Bearer key works, where to create it, and how to rotate or revoke it.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.