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

# Ad Groups

> Group ads under a campaign with shared context hints and a max bid. Create, list, retrieve, update, and change status.

Ad groups live under a campaign. Each ad group carries the **context hints** that steer where its ads serve and the **max bid** the auction may pay, and it holds one or more ads that all share a single creative type.

You can create ad groups inline when you create a campaign (see [Campaigns](https://docs.thrad.ai/advertisers/api-reference/campaigns.md)), or add them to an existing campaign with the endpoints below.

> [!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) for how to create your key.

## The ad group object

**id** `string`

Unique identifier for the ad group (a UUID).

**campaign\_id** `string`

The parent campaign's `id`.

**name** `string`

Display name for the ad group.

**context\_hints** `string[]`

The hints that tell Thrad which conversations this ad group's ads are relevant to. Returned as an array of strings.

**status** `string`

One of `active` or `paused`. Ad groups have no distinct archived state on Thrad, so `/archive` deactivates the ad group and it reads back as `paused`. Change status **only** with the dedicated `/activate`, `/pause`, and `/archive` actions — `status` sent in an update body is rejected.

**bidding\_config** `object`

Bidding settings for the ad group.

**bidding\_config properties**

**billing\_event\_type** `string`

The event you are billed on. Always `impression`.

**max\_bid\_micros** `integer`

The maximum bid in **micros**, where `$1 = 1,000,000`. For example, `700000` is \$0.70.

**created\_at** `string`

ISO 8601 timestamp (UTC) when the ad group was created.

**updated\_at** `string`

ISO 8601 timestamp (UTC) when the ad group was last updated.

```json The ad group object theme={null}
{
  "id": "9a1f2c3d-4e5f-6789-abcd-ef0123456789",
  "campaign_id": "1b2c3d4e-5f60-7182-93a4-b5c6d7e8f901",
  "name": "Runners — focus playlists",
  "context_hints": [
    "marathon training",
    "running shoes",
    "endurance"
  ],
  "status": "active",
  "bidding_config": {
    "billing_event_type": "impression",
    "max_bid_micros": 700000
  },
  "created_at": "2026-04-02T00:00:00.000000Z",
  "updated_at": "2026-04-03T00:00:00.000000Z"
}
```

## List ad groups

`GET /v1/ad_groups`

Returns the ad groups for one campaign, most recent first. `campaign_id` is **required** — listing across all campaigns is not supported.

**campaign\_id** `string` *required*

The campaign whose ad groups to list.

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

Number of ad groups to return, `1`–`500`.

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

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

**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`.

```bash cURL theme={null}
curl "https://api.thrad.ai/v1/ad_groups?campaign_id=1b2c3d4e-5f60-7182-93a4-b5c6d7e8f901&limit=20" \
  -H "Authorization: Bearer $THRAD_ADS_API_KEY"
```

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

resp = requests.get(
    "https://api.thrad.ai/v1/ad_groups",
    headers={"Authorization": f"Bearer {os.environ['THRAD_ADS_API_KEY']}"},
    params={"campaign_id": "1b2c3d4e-5f60-7182-93a4-b5c6d7e8f901", "limit": 20},
)
resp.raise_for_status()
print(resp.json())
```

```json 200 OK theme={null}
{
  "object": "list",
  "data": [
    {
      "id": "9a1f2c3d-4e5f-6789-abcd-ef0123456789",
      "campaign_id": "1b2c3d4e-5f60-7182-93a4-b5c6d7e8f901",
      "name": "Runners — focus playlists",
      "context_hints": ["marathon training", "running shoes"],
      "status": "active",
      "bidding_config": {
        "billing_event_type": "impression",
        "max_bid_micros": 700000
      },
      "created_at": "2026-04-02T00:00:00.000000Z",
      "updated_at": "2026-04-03T00:00:00.000000Z"
    }
  ],
  "count": 1,
  "first_id": "9a1f2c3d-4e5f-6789-abcd-ef0123456789",
  "last_id": "9a1f2c3d-4e5f-6789-abcd-ef0123456789",
  "has_more": false
}
```

## Create an ad group

`POST /v1/ad_groups`

Adds an ad group to an existing campaign. The ad group must include at least one ad, and all ads in the ad group must share one creative type (all `card`, or all `poll`).

**campaign\_id** `string` *required*

The campaign to add this ad group to.

**name** `string` *required*

Display name for the ad group. Max 255 characters.

**context\_hints** `string[]` *required*

Hints that steer where the ad group's ads serve. Each array entry is one hint. Must be a non-empty array.

**bidding\_config** `object` *required*

Bidding settings for the ad group.

**bidding\_config properties**

**max\_bid\_micros** `integer` *required*

Maximum bid in **micros** (`$1 = 1,000,000`). Must be between `100` and `100000000` ($0.0001–$100). Every ad group needs a max bid — omitting it is rejected.

> [!NOTE]
>
> A per-country **bid floor** also applies. If the bid is below the highest floor across the ad group's targeted countries, the request is rejected — raise the bid or narrow the geo.

**ads** `array` *required*

The ads in this ad group. At least one is required, and all ads must share one creative type.

**ad properties**

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

The UUID of an existing [asset](https://docs.thrad.ai/advertisers/api-reference/assets.md), which supplies the ad's image and destination URL.

**name** `string`

Display name for the ad. Falls back to the creative's title when omitted.

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

Cards only. When `true`, Thrad writes the card headline per impression, so `creative.title` must be **omitted**. Not available for polls.

**creative** `object` *required*

The ad copy. See [Ads](https://docs.thrad.ai/advertisers/api-reference/ads.md) for the full creative schema.

**creative properties**

**type** `string` *required*

`card` (a standard card ad) or `poll` (a Thrad-only format). AI-generated polls are not allowed.

**title** `string`

For `card`: the headline (max 30 characters; must be omitted when `auto_generate` is `true`). For `poll`: the poll question (required, max 120 characters).

**body** `string`

`card` only. The card description.

**cta** `string`

`card` only. Call-to-action text. Defaults to `"Learn More"` when omitted.

**options** `string[]`

`poll` only. The 2–4 answer options.

```bash cURL theme={null}
curl -X POST "https://api.thrad.ai/v1/ad_groups" \
  -H "Authorization: Bearer $THRAD_ADS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaign_id": "1b2c3d4e-5f60-7182-93a4-b5c6d7e8f901",
    "name": "Runners — focus playlists",
    "context_hints": ["marathon training", "running shoes"],
    "bidding_config": { "max_bid_micros": 700000 },
    "ads": [
      {
        "name": "Marathon shoes card",
        "asset_id": "3d9e7b21-5c4a-4f8e-b1d6-7a2c9e0f4b83",
        "creative": {
          "type": "card",
          "title": "Premium Running Shoes",
          "body": "Lightweight and built for marathon training.",
          "cta": "Shop Now"
        }
      }
    ]
  }'
```

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

resp = requests.post(
    "https://api.thrad.ai/v1/ad_groups",
    headers={"Authorization": f"Bearer {os.environ['THRAD_ADS_API_KEY']}"},
    json={
        "campaign_id": "1b2c3d4e-5f60-7182-93a4-b5c6d7e8f901",
        "name": "Runners — focus playlists",
        "context_hints": ["marathon training", "running shoes"],
        "bidding_config": {"max_bid_micros": 700000},
        "ads": [
            {
                "name": "Marathon shoes card",
                "asset_id": "3d9e7b21-5c4a-4f8e-b1d6-7a2c9e0f4b83",
                "creative": {
                    "type": "card",
                    "title": "Premium Running Shoes",
                    "body": "Lightweight and built for marathon training.",
                    "cta": "Shop Now",
                },
            }
        ],
    },
)
resp.raise_for_status()
print(resp.json())
```

```json 201 Created theme={null}
{
  "id": "9a1f2c3d-4e5f-6789-abcd-ef0123456789",
  "campaign_id": "1b2c3d4e-5f60-7182-93a4-b5c6d7e8f901",
  "name": "Runners — focus playlists",
  "context_hints": ["marathon training", "running shoes"],
  "status": "active",
  "bidding_config": {
    "billing_event_type": "impression",
    "max_bid_micros": 700000
  },
  "created_at": "2026-04-02T00:00:00.000000Z",
  "updated_at": "2026-04-02T00:00:00.000000Z"
}
```

## Retrieve an ad group

`GET /v1/ad_groups/{id}`

**id** `string` *required*

The ad group's `id`.

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

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

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

```json 200 OK theme={null}
{
  "id": "9a1f2c3d-4e5f-6789-abcd-ef0123456789",
  "campaign_id": "1b2c3d4e-5f60-7182-93a4-b5c6d7e8f901",
  "name": "Runners — focus playlists",
  "context_hints": ["marathon training", "running shoes"],
  "status": "active",
  "bidding_config": {
    "billing_event_type": "impression",
    "max_bid_micros": 700000
  },
  "created_at": "2026-04-02T00:00:00.000000Z",
  "updated_at": "2026-04-03T00:00:00.000000Z"
}
```

## Update an ad group

`POST /v1/ad_groups/{id}`

Patch the mutable fields. Only the fields you send are changed.

**id** `string` *required*

The ad group's `id`.

**name** `string`

New display name.

**context\_hints** `string[]`

Replacement set of context hints.

**bidding\_config** `object`

Bidding settings. Send `max_bid_micros` to change the max bid.

**bidding\_config properties**

**max\_bid\_micros** `integer`

New maximum bid in **micros** (`$1 = 1,000,000`). Re-validated to `100`–`100000000` on update.

> [!WARNING]
>
> `status` is **not** accepted in the update body. Sending it returns `400 use_status_action` — use the `/activate`, `/pause`, and `/archive` actions below.

```bash cURL theme={null}
curl -X POST "https://api.thrad.ai/v1/ad_groups/9a1f2c3d-4e5f-6789-abcd-ef0123456789" \
  -H "Authorization: Bearer $THRAD_ADS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Runners — focus playlists (v2)",
    "bidding_config": { "max_bid_micros": 850000 }
  }'
```

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

ad_group_id = "9a1f2c3d-4e5f-6789-abcd-ef0123456789"
resp = requests.post(
    f"https://api.thrad.ai/v1/ad_groups/{ad_group_id}",
    headers={"Authorization": f"Bearer {os.environ['THRAD_ADS_API_KEY']}"},
    json={
        "name": "Runners — focus playlists (v2)",
        "bidding_config": {"max_bid_micros": 850000},
    },
)
resp.raise_for_status()
print(resp.json())
```

```json 200 OK theme={null}
{
  "id": "9a1f2c3d-4e5f-6789-abcd-ef0123456789",
  "campaign_id": "1b2c3d4e-5f60-7182-93a4-b5c6d7e8f901",
  "name": "Runners — focus playlists (v2)",
  "context_hints": ["marathon training", "running shoes"],
  "status": "active",
  "bidding_config": {
    "billing_event_type": "impression",
    "max_bid_micros": 850000
  },
  "created_at": "2026-04-02T00:00:00.000000Z",
  "updated_at": "2026-04-04T00:00:00.000000Z"
}
```

## Activate, pause, or archive an ad group

```
POST /v1/ad_groups/{id}/activate
POST /v1/ad_groups/{id}/pause
POST /v1/ad_groups/{id}/archive
```

These are the **only** way to change an ad group's status. Each returns the updated ad group object.

**id** `string` *required*

The ad group's `id`.

> [!NOTE]
>
> Status actions require the parent campaign to be **approved**. If the parent campaign is still pending approval, the action returns `400 campaign_pending_status_locked`; if it has been completed or blocked, it returns `400 campaign_terminal`.

> [!WARNING]
>
> Thrad ad groups have no separate archived state, so `/archive` deactivates the ad group and it reads back with `status: "paused"` — the same result as `/pause`. Use `/activate` to bring it back.

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

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

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

```json 200 OK theme={null}
{
  "id": "9a1f2c3d-4e5f-6789-abcd-ef0123456789",
  "campaign_id": "1b2c3d4e-5f60-7182-93a4-b5c6d7e8f901",
  "name": "Runners — focus playlists",
  "context_hints": ["marathon training", "running shoes"],
  "status": "paused",
  "bidding_config": {
    "billing_event_type": "impression",
    "max_bid_micros": 700000
  },
  "created_at": "2026-04-02T00:00:00.000000Z",
  "updated_at": "2026-04-05T00:00:00.000000Z"
}
```

## Errors

Errors use the bare error shape:

```json theme={null}
{
  "error": {
    "message": "`bidding_config.max_bid_micros` exceeds the allowed maximum.",
    "type": "invalid_request_error",
    "param": "bidding_config.max_bid_micros",
    "code": "invalid_value"
  }
}
```

| Status | `code` | When |
| - | - | - |
| `400` | `required` | `campaign_id`, `name`, `context_hints`, `ads`, an ad's `asset_id`, or another required field is missing or empty |
| `400` | `invalid_value` | `max_bid_micros` is non-integer, outside `100`–`100000000`, or below the per-country bid floor; the ads do not share one creative type; a creative is missing, not an object, or uses an unknown `type`; or an `asset_id` is not in this account |
| `400` | `use_status_action` | `status` was sent in the update body. Use `/activate`, `/pause`, or `/archive` |
| `400` | `string_too_long` | A creative `title` exceeds its limit (30 for cards, 120 for polls) |
| `400` | `poll_cannot_be_ai` | A poll ad was sent with `auto_generate: true` |
| `400` | `campaign_pending_status_locked` | A status action was attempted while the parent campaign is still pending approval |
| `400` | `campaign_terminal` | A status action was attempted while the parent campaign is completed or blocked |
| `401` | `auth_required` | The `Authorization` header is missing |
| `401` | `invalid_api_key` | The `Authorization` header is malformed, unknown, or revoked |
| `404` | `not_found` | The ad group or its campaign does not exist or belongs to another organization |
| `429` | `rate_limit_exceeded` | Per-key rate limit (1000/hour) exceeded |


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