> ## 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.

# Ads

> Create and manage ads (the creative that runs inside an ad group) — cards and polls.

An **ad** is a single creative inside an [ad group](https://docs.thrad.ai/advertisers/api-reference/ad-groups.md). Every ad belongs to exactly one ad group, and its **creative type must match the ad group's type** — an ad group is either all cards or all polls, never a mix.

Two creative types are supported:

* **`card`** — an in-chat sponsored message (title, body, CTA, image).
* **`poll`** — a Thrad-only sponsored poll (question + 2–4 options).

> [!WARNING]
>
> **An ad is copy plus an asset.** Every ad requires an `asset_id` pointing at an existing [asset](https://docs.thrad.ai/advertisers/api-reference/assets.md), and that asset supplies the ad's **image** (card) and **destination URL** (card and poll). The creative body carries text only — there is no `file_id`, no `image_url`, and no `target_url` on input, and `/v1` never creates or mutates assets.

> [!NOTE]
>
> All paths are relative to the base URL `https://api.thrad.ai/v1`. Authenticate every request with `Authorization: Bearer ak_...`. See [Authentication](https://docs.thrad.ai/advertisers/api-reference/authentication.md).

## The ad object

**id** `string`

The ad's unique identifier (UUID).

**ad\_group\_id** `string`

The UUID of the ad group this ad belongs to.

**asset\_id** `string | null`

The UUID of the [asset](https://docs.thrad.ai/advertisers/api-reference/assets.md) backing this ad — the source of its image and destination URL.

**name** `string`

Human-readable ad name. When omitted on create, it defaults to the creative title (card headline or poll question), or `"Ad"` if there is none.

**status** `string`

One of `active` or `paused`. Change it **only** through [/activate](#activate-an-ad), [/pause](#pause-an-ad), and [/archive](#archive-an-ad).

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

> [!WARNING]
>
> **A newly created ad's status depends on how it was created.** An ad added with `POST /v1/ads` comes back `active`. An ad created inside the nested [`POST /v1/campaigns`](https://docs.thrad.ai/advertisers/api-reference/campaigns#create-a-campaign) call comes back **`paused`** — the whole tree is deactivated while the campaign awaits review, so nothing can serve before approval. Those ads return to `active` automatically when the campaign is approved; you do not need to call `/activate`.

**review\_status** `string`

Read-only review state inherited from the parent campaign: `approved`, `in_review`, or `rejected`. You cannot set this — it reflects where the campaign sits in Thrad's approval flow.

**creative** `object`

The ad creative. Shape depends on `type`.

**card creative**

**type** `string`

Always `card`.

**title** `string`

The card headline (max 30 characters). Empty on an `auto_generate` card — its headline is written per impression at serve time.

**body** `string`

The card description text.

**cta** `string`

Call-to-action text. Always present on a card read — it defaults to `"Learn More"` when you don't send one.

**image\_url** `string | null`

Resolved URL of the card image, or `null` when the card has no image. **Read-only** — it comes from the attached asset.

**target\_url** `string | null`

The destination users land on. **Read-only** — resolved from the attached asset's `url`.

**poll creative**

**type** `string`

Always `poll`.

**title** `string`

The poll question (max 120 characters).

**options** `array`

The poll answer options (2–4 strings).

**target\_url** `string | null`

The destination users land on from the poll. **Read-only** — derived from the attached asset's `url` when the ad is created or re-pointed.

**logo\_url** `string | null`

The logo shown on the poll. **Read-only** — resolved from the custom poll logo, then the ad account's logo, then the organization's.

## List ads

`GET /v1/ads`

Lists the ads in a single ad group. `ad_group_id` is **required** — there is no all-ads listing.

**ad\_group\_id** `string` *required*

The UUID of the ad group whose ads to list.

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

Number of ads to return, `1`–`500`.

**before** `string`

Cursor for the previous page — pass a prior response's `first_id`. Mutually exclusive with `after`.

**after** `string`

Cursor for the next page — pass a prior response's `last_id`. Mutually exclusive with `before`.

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

Sort order by creation time: `asc` or `desc`.

```bash cURL theme={null}
curl https://api.thrad.ai/v1/ads?ad_group_id=9a1f2c3d-4e5f-6789-abcd-ef0123456789 \
  -H "Authorization: Bearer $THRAD_ADS_API_KEY"
```

```python Python theme={null}
import os, requests

resp = requests.get(
    "https://api.thrad.ai/v1/ads",
    headers={"Authorization": f"Bearer {os.environ['THRAD_ADS_API_KEY']}"},
    params={"ad_group_id": "9a1f2c3d-4e5f-6789-abcd-ef0123456789", "limit": 20},
)
print(resp.json())
```

```json 200 OK theme={null}
{
  "object": "list",
  "data": [
    {
      "id": "4a9b6c1d-7e2f-4b83-9c15-6d0e8f7a2b91",
      "ad_group_id": "9a1f2c3d-4e5f-6789-abcd-ef0123456789",
      "name": "Premium Running Shoes",
      "status": "active",
      "review_status": "approved",
      "asset_id": "3d9e7b21-5c4a-4f8e-b1d6-7a2c9e0f4b83",
      "creative": {
        "type": "card",
        "title": "Premium Running Shoes",
        "body": "Lightweight and built for marathon training.",
        "cta": "Shop Now",
        "image_url": "https://cdn.thrad.ai/uploads/2026/04/pro-racer.png",
        "target_url": "https://acme.example.com/products/pro-racer"
      }
    }
  ],
  "count": 1,
  "first_id": "4a9b6c1d-7e2f-4b83-9c15-6d0e8f7a2b91",
  "last_id": "4a9b6c1d-7e2f-4b83-9c15-6d0e8f7a2b91",
  "has_more": false
}
```

## Create an ad

`POST /v1/ads`

Adds an ad to an existing ad group. The creative `type` must match the ad group's type, or the request is rejected with `400 invalid_value` on `creative.type`.

**ad\_group\_id** `string` *required*

The UUID of the ad group to add this ad to.

**asset\_id** `string` *required*

The UUID of an existing [asset](https://docs.thrad.ai/advertisers/api-reference/assets.md) — browse them with `GET /v1/assets`. The asset supplies the card image and the destination URL. Omitting it returns `400 required`; an asset from another ad account returns `400 invalid_value`. For a **poll**, the asset must have a `url`.

**name** `string`

Ad name. Defaults to the creative title (card headline or poll question) when omitted.

**auto\_generate** `boolean` (default: false)

**Cards only.** When `true`, Thrad writes the card headline per impression at serve time. `creative.title` must then be **omitted** — sending one returns `400 invalid_value`, because a title set here could never take effect. Setting `auto_generate` on a `poll` creative is rejected with `poll_cannot_be_ai`.

**creative** `object` *required*

The ad copy. Its `type` must match the ad group.

**card creative**

**type** `string` *required*

Must be `card`.

**title** `string` *required*

Card headline, max 30 characters. Must be omitted when `auto_generate` is `true`.

**body** `string`

Card description text.

**cta** `string`

Call-to-action text.

**poll creative**

**type** `string` *required*

Must be `poll`.

**title** `string` *required*

The poll question, max 120 characters.

**options** `array` *required*

2–4 answer option strings.

> [!NOTE]
>
> The older spellings `chat_card` and `product_ad_template` are **rejected**. One concept, one word: `card`.

```bash Card (cURL) theme={null}
curl https://api.thrad.ai/v1/ads \
  -H "Authorization: Bearer $THRAD_ADS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "ad_group_id": "9a1f2c3d-4e5f-6789-abcd-ef0123456789",
    "asset_id": "3d9e7b21-5c4a-4f8e-b1d6-7a2c9e0f4b83",
    "name": "Premium Running Shoes",
    "creative": {
      "type": "card",
      "title": "Premium Running Shoes",
      "body": "Lightweight and built for marathon training.",
      "cta": "Shop Now"
    }
  }'
```

```bash Card, AI headline (cURL) theme={null}
curl https://api.thrad.ai/v1/ads \
  -H "Authorization: Bearer $THRAD_ADS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "ad_group_id": "9a1f2c3d-4e5f-6789-abcd-ef0123456789",
    "asset_id": "3d9e7b21-5c4a-4f8e-b1d6-7a2c9e0f4b83",
    "auto_generate": true,
    "creative": {
      "type": "card",
      "body": "Lightweight and built for marathon training."
    }
  }'
```

```bash Poll (cURL) theme={null}
curl https://api.thrad.ai/v1/ads \
  -H "Authorization: Bearer $THRAD_ADS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "ad_group_id": "b7c2d1e0-3f4a-5b6c-8d9e-0f1a2b3c4d5e",
    "asset_id": "6c0b8a94-2d1e-4f37-9a5b-8e7d6c5b4a39",
    "creative": {
      "type": "poll",
      "title": "What kind of playlist are you making?",
      "options": ["Wedding", "Study", "Workout"]
    }
  }'
```

```python Python theme={null}
import os, requests

resp = requests.post(
    "https://api.thrad.ai/v1/ads",
    headers={"Authorization": f"Bearer {os.environ['THRAD_ADS_API_KEY']}"},
    json={
        "ad_group_id": "9a1f2c3d-4e5f-6789-abcd-ef0123456789",
        "asset_id": "3d9e7b21-5c4a-4f8e-b1d6-7a2c9e0f4b83",
        "name": "Premium Running Shoes",
        "creative": {
            "type": "card",
            "title": "Premium Running Shoes",
            "body": "Lightweight and built for marathon training.",
            "cta": "Shop Now",
        },
    },
)
print(resp.json())
```

```json 201 Created theme={null}
{
  "id": "4a9b6c1d-7e2f-4b83-9c15-6d0e8f7a2b91",
  "ad_group_id": "9a1f2c3d-4e5f-6789-abcd-ef0123456789",
  "name": "Premium Running Shoes",
  "status": "active",
  "review_status": "in_review",
  "asset_id": "3d9e7b21-5c4a-4f8e-b1d6-7a2c9e0f4b83",
  "creative": {
    "type": "card",
    "title": "Premium Running Shoes",
    "body": "Lightweight and built for marathon training.",
    "cta": "Shop Now",
    "image_url": "https://cdn.thrad.ai/uploads/2026/04/pro-racer.png",
    "target_url": "https://acme.example.com/products/pro-racer"
  }
}
```

## Retrieve an ad

`GET /v1/ads/{ad_id}`

**ad\_id** `string` *required*

The UUID of the ad to retrieve.

```bash cURL theme={null}
curl https://api.thrad.ai/v1/ads/4a9b6c1d-7e2f-4b83-9c15-6d0e8f7a2b91 \
  -H "Authorization: Bearer $THRAD_ADS_API_KEY"
```

```json 200 OK theme={null}
{
  "id": "4a9b6c1d-7e2f-4b83-9c15-6d0e8f7a2b91",
  "ad_group_id": "9a1f2c3d-4e5f-6789-abcd-ef0123456789",
  "name": "Premium Running Shoes",
  "status": "active",
  "review_status": "approved",
  "asset_id": "3d9e7b21-5c4a-4f8e-b1d6-7a2c9e0f4b83",
  "creative": {
    "type": "card",
    "title": "Premium Running Shoes",
    "body": "Lightweight and built for marathon training.",
    "cta": "Shop Now",
    "image_url": "https://cdn.thrad.ai/uploads/2026/04/pro-racer.png",
    "target_url": "https://acme.example.com/products/pro-racer"
  }
}
```

## Update an ad

`POST /v1/ads/{ad_id}`

Patches the ad's `name`, `creative`, and/or `asset_id`. Only the fields you send are changed.

> [!WARNING]
>
> `status` is **not** accepted here. Sending it returns `400 use_status_action` — use [/activate](#activate-an-ad), [/pause](#pause-an-ad), or [/archive](#archive-an-ad).

**ad\_id** `string` *required*

The UUID of the ad to update.

**name** `string`

New ad name.

**asset\_id** `string`

Re-point the ad at a different [asset](https://docs.thrad.ai/advertisers/api-reference/assets.md). The creative moves with it: a poll's destination always re-derives from the new asset, and a card's image re-derives when the image it was showing came from the asset being replaced (a custom image or logo fallback survives the swap).

**creative** `object`

A replacement creative. It must match the ad group's type and follow the same field rules as on create (card `title` ≤ 30; poll `title` ≤ 120 with 2–4 `options`). Copy only — swap the image or destination by re-pointing `asset_id`.

```bash cURL theme={null}
curl https://api.thrad.ai/v1/ads/4a9b6c1d-7e2f-4b83-9c15-6d0e8f7a2b91 \
  -H "Authorization: Bearer $THRAD_ADS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Premium Trail Shoes",
    "asset_id": "8f2b5c17-9d4e-4a60-b3c8-1e5f7a9d2c04",
    "creative": {
      "type": "card",
      "title": "Premium Trail Shoes",
      "body": "Grip for any terrain.",
      "cta": "Shop Now"
    }
  }'
```

```json 200 OK theme={null}
{
  "id": "4a9b6c1d-7e2f-4b83-9c15-6d0e8f7a2b91",
  "ad_group_id": "9a1f2c3d-4e5f-6789-abcd-ef0123456789",
  "name": "Premium Trail Shoes",
  "status": "active",
  "review_status": "approved",
  "asset_id": "8f2b5c17-9d4e-4a60-b3c8-1e5f7a9d2c04",
  "creative": {
    "type": "card",
    "title": "Premium Trail Shoes",
    "body": "Grip for any terrain.",
    "cta": "Shop Now",
    "image_url": "https://cdn.thrad.ai/uploads/2026/04/trail.png",
    "target_url": "https://acme.example.com/products/trail"
  }
}
```

## Activate an ad

`POST /v1/ads/{ad_id}/activate`

Reactivates a paused ad. The parent campaign must be approved.

**ad\_id** `string` *required*

The UUID of the ad to activate.

```bash cURL theme={null}
curl -X POST https://api.thrad.ai/v1/ads/4a9b6c1d-7e2f-4b83-9c15-6d0e8f7a2b91/activate \
  -H "Authorization: Bearer $THRAD_ADS_API_KEY"
```

```json 200 OK theme={null}
{
  "id": "4a9b6c1d-7e2f-4b83-9c15-6d0e8f7a2b91",
  "ad_group_id": "9a1f2c3d-4e5f-6789-abcd-ef0123456789",
  "name": "Premium Running Shoes",
  "status": "active",
  "review_status": "approved",
  "asset_id": "3d9e7b21-5c4a-4f8e-b1d6-7a2c9e0f4b83",
  "creative": {
    "type": "card",
    "title": "Premium Running Shoes",
    "body": "Lightweight and built for marathon training.",
    "cta": "Shop Now",
    "image_url": "https://cdn.thrad.ai/uploads/2026/04/pro-racer.png",
    "target_url": "https://acme.example.com/products/pro-racer"
  }
}
```

## Pause an ad

`POST /v1/ads/{ad_id}/pause`

Deactivates the ad.

**ad\_id** `string` *required*

The UUID of the ad to pause.

```bash cURL theme={null}
curl -X POST https://api.thrad.ai/v1/ads/4a9b6c1d-7e2f-4b83-9c15-6d0e8f7a2b91/pause \
  -H "Authorization: Bearer $THRAD_ADS_API_KEY"
```

```json 200 OK theme={null}
{
  "id": "4a9b6c1d-7e2f-4b83-9c15-6d0e8f7a2b91",
  "ad_group_id": "9a1f2c3d-4e5f-6789-abcd-ef0123456789",
  "name": "Premium Running Shoes",
  "status": "paused",
  "review_status": "approved",
  "asset_id": "3d9e7b21-5c4a-4f8e-b1d6-7a2c9e0f4b83",
  "creative": {
    "type": "card",
    "title": "Premium Running Shoes",
    "body": "Lightweight and built for marathon training.",
    "cta": "Shop Now",
    "image_url": "https://cdn.thrad.ai/uploads/2026/04/pro-racer.png",
    "target_url": "https://acme.example.com/products/pro-racer"
  }
}
```

## Archive an ad

`POST /v1/ads/{ad_id}/archive`

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

**ad\_id** `string` *required*

The UUID of the ad to archive.

```bash cURL theme={null}
curl -X POST https://api.thrad.ai/v1/ads/4a9b6c1d-7e2f-4b83-9c15-6d0e8f7a2b91/archive \
  -H "Authorization: Bearer $THRAD_ADS_API_KEY"
```

```json 200 OK theme={null}
{
  "id": "4a9b6c1d-7e2f-4b83-9c15-6d0e8f7a2b91",
  "ad_group_id": "9a1f2c3d-4e5f-6789-abcd-ef0123456789",
  "name": "Premium Running Shoes",
  "status": "paused",
  "review_status": "approved",
  "asset_id": "3d9e7b21-5c4a-4f8e-b1d6-7a2c9e0f4b83",
  "creative": {
    "type": "card",
    "title": "Premium Running Shoes",
    "body": "Lightweight and built for marathon training.",
    "cta": "Shop Now",
    "image_url": "https://cdn.thrad.ai/uploads/2026/04/pro-racer.png",
    "target_url": "https://acme.example.com/products/pro-racer"
  }
}
```

## Errors

Errors use the bare shape `{ "error": { "message", "type", "param", "code" } }`.

| Code | HTTP | When |
| - | - | - |
| `required` | 400 | A required field is missing — `asset_id`, card `title` (when not auto-generated), or poll `title`. |
| `invalid_value` | 400 | `creative.type` doesn't match the ad group's type, a missing or non-object `creative`, an unknown `creative.type` (including the rejected `chat_card`), poll `options` not a list of 2–4, an `asset_id` that isn't in this account, a poll whose asset has no `url`, or a `title` sent on an `auto_generate` card. |
| `use_status_action` | 400 | `status` was sent in the update body. Use `/activate`, `/pause`, or `/archive`. |
| `string_too_long` | 400 | Card `title` over 30 characters, or poll `title` over 120 characters. |
| `poll_cannot_be_ai` | 400 | `auto_generate` set on a `poll` creative — AI polls are not supported. |
| `campaign_pending_status_locked` | 400 | A status action was attempted while the parent campaign is still pending approval. |
| `campaign_terminal` | 400 | The parent campaign is completed or blocked, so its ads are archive-only. |
| `not_found` | 404 | The ad or ad group doesn't exist or belongs to another organization. |
| `rate_limit_exceeded` | 429 | Over the 1000-requests/hour per-key limit. |


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