> ## Documentation Index
> Fetch the complete documentation index at: https://docs.thrads.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](/advertisers/api-reference/campaigns)), 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](/advertisers/api-reference/authentication) for how to create your key.
</Note>

## The ad group object

<ResponseField name="id" type="string">
  Unique identifier for the ad group (a UUID).
</ResponseField>

<ResponseField name="campaign_id" type="string">
  The parent campaign's `id`.
</ResponseField>

<ResponseField name="name" type="string">
  Display name for the ad group.
</ResponseField>

<ResponseField name="context_hints" type="string[]">
  The hints that tell Thrad which conversations this ad group's ads are relevant to. Returned as an array of strings.
</ResponseField>

<ResponseField name="status" type="string">
  One of `active`, `paused`, or `archived`. Ad groups have no distinct archived state on Thrad, so an archived ad group reads back as `paused`. Change status with the dedicated `/activate`, `/pause`, and `/archive` actions, or by sending `status` in an update body.
</ResponseField>

<ResponseField name="bidding_config" type="object">
  Bidding settings for the ad group.

  <Expandable title="bidding_config properties">
    <ResponseField name="billing_event_type" type="string">
      The event you are billed on. Always `impression`.
    </ResponseField>

    <ResponseField name="max_bid_micros" type="integer">
      The maximum bid in **micros**, where `$1 = 1,000,000`. For example, `700000` is \$0.70. `null` when no max bid is set.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="created_at" type="integer">
  Unix timestamp (seconds) when the ad group was created.
</ResponseField>

<ResponseField name="updated_at" type="integer">
  Unix timestamp (seconds) when the ad group was last updated.
</ResponseField>

<ResponseExample>
  ```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": 1775088000,
    "updated_at": 1775174400
  }
  ```
</ResponseExample>

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

<ParamField query="campaign_id" type="string" required>
  The campaign whose ad groups to list.
</ParamField>

<ParamField query="limit" type="integer" default="20">
  Number of ad groups to return, `1`–`500`.
</ParamField>

<ParamField query="order" type="string" default="desc">
  Sort direction by creation time: `asc` or `desc`.
</ParamField>

<ParamField query="before" type="string">
  Cursor for the previous page. Pass a prior response's `first_id`. Mutually exclusive with `after`.
</ParamField>

<ParamField query="after" type="string">
  Cursor for the next page. Pass a prior response's `last_id`. Mutually exclusive with `before`.
</ParamField>

<RequestExample>
  ```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())
  ```
</RequestExample>

<ResponseExample>
  ```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": 1775088000,
        "updated_at": 1775174400
      }
    ],
    "count": 1,
    "first_id": "9a1f2c3d-4e5f-6789-abcd-ef0123456789",
    "last_id": "9a1f2c3d-4e5f-6789-abcd-ef0123456789",
    "has_more": false
  }
  ```
</ResponseExample>

## 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 `chat_card`, or all `poll`).

<ParamField body="campaign_id" type="string" required>
  The campaign to add this ad group to.
</ParamField>

<ParamField body="name" type="string" required>
  Display name for the ad group. Max 255 characters.
</ParamField>

<ParamField body="context_hints" type="string[]" required>
  Hints that steer where the ad group's ads serve. Each array entry is one hint. Must be a non-empty array.
</ParamField>

<ParamField body="bidding_config" type="object">
  Bidding settings for the ad group.

  <Expandable title="bidding_config properties">
    <ParamField body="max_bid_micros" type="integer">
      Maximum bid in **micros** (`$1 = 1,000,000`). Must be between `1` and `100000000` ($0.000001–$100). Omit to leave unset.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="ads" type="array" required>
  The ads in this ad group. At least one is required, and all ads must share one creative type.

  <Expandable title="ad properties">
    <ParamField body="name" type="string">
      Display name for the ad. Falls back to the creative's title when omitted.
    </ParamField>

    <ParamField body="auto_generate" type="boolean" default="false">
      When `true`, Thrad generates a `chat_card` title and body for you, so `creative.title` and `creative.body` become optional. Not available for polls.
    </ParamField>

    <ParamField body="creative" type="object" required>
      The ad creative. See [Ads](/advertisers/api-reference/ads) for the full creative schema.

      <Expandable title="creative properties">
        <ParamField body="type" type="string" required>
          `chat_card` (a standard card ad) or `poll` (a Thrad-only format). AI-generated polls are not allowed.
        </ParamField>

        <ParamField body="title" type="string">
          For `chat_card`: the headline (max 30 characters; optional when `auto_generate` is `true`). For `poll`: the poll question (required, max 120 characters).
        </ParamField>

        <ParamField body="body" type="string">
          `chat_card` only. The card description.
        </ParamField>

        <ParamField body="cta" type="string">
          `chat_card` only. Call-to-action text.
        </ParamField>

        <ParamField body="file_id" type="string">
          `chat_card` only. A `file_id` from `POST /v1/upload` to use as the card image.
        </ParamField>

        <ParamField body="image_url" type="string">
          `chat_card` only. A public image URL to fetch and store as the card image.
        </ParamField>

        <ParamField body="options" type="string[]">
          `poll` only. The 2–4 answer options.
        </ParamField>

        <ParamField body="target_url" type="string">
          `poll` only. The advertiser URL the poll links to. Required for polls.
        </ParamField>
      </Expandable>
    </ParamField>
  </Expandable>
</ParamField>

<RequestExample>
  ```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",
          "creative": {
            "type": "chat_card",
            "title": "Premium Running Shoes",
            "body": "Lightweight and built for marathon training.",
            "cta": "Shop Now",
            "image_url": "https://cdn.example.com/nike-shoes.jpg"
          }
        }
      ]
    }'
  ```

  ```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",
                  "creative": {
                      "type": "chat_card",
                      "title": "Premium Running Shoes",
                      "body": "Lightweight and built for marathon training.",
                      "cta": "Shop Now",
                      "image_url": "https://cdn.example.com/nike-shoes.jpg",
                  },
              }
          ],
      },
  )
  resp.raise_for_status()
  print(resp.json())
  ```
</RequestExample>

<ResponseExample>
  ```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": 1775088000,
    "updated_at": 1775088000
  }
  ```
</ResponseExample>

## Retrieve an ad group

`GET /v1/ad_groups/{id}`

<ParamField path="id" type="string" required>
  The ad group's `id`.
</ParamField>

<RequestExample>
  ```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())
  ```
</RequestExample>

<ResponseExample>
  ```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": 1775088000,
    "updated_at": 1775174400
  }
  ```
</ResponseExample>

## Update an ad group

`POST /v1/ad_groups/{id}`

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

<ParamField path="id" type="string" required>
  The ad group's `id`.
</ParamField>

<ParamField body="name" type="string">
  New display name.
</ParamField>

<ParamField body="context_hints" type="string[]">
  Replacement set of context hints.
</ParamField>

<ParamField body="bidding_config" type="object">
  Bidding settings. Send `max_bid_micros` to change the max bid.

  <Expandable title="bidding_config properties">
    <ParamField body="max_bid_micros" type="integer">
      New maximum bid in **micros** (`$1 = 1,000,000`). Re-validated to `1`–`100000000` on update.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="status" type="string">
  Optionally set `active`, `paused`, or `archived` directly. `archived` deactivates the ad group, so it reads back as `paused`. You can also use the dedicated `/activate`, `/pause`, and `/archive` actions below.
</ParamField>

<RequestExample>
  ```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())
  ```
</RequestExample>

<ResponseExample>
  ```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": 1775088000,
    "updated_at": 1775260800
  }
  ```
</ResponseExample>

## 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 recommended way to change an ad group's status. Each returns the updated ad group object.

<ParamField path="id" type="string" required>
  The ad group's `id`.
</ParamField>

<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`.
</Note>

<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 a PATCH with `status: "archived"`. Use `/activate` to bring it back.
</Warning>

<RequestExample>
  ```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())
  ```
</RequestExample>

<ResponseExample>
  ```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": 1775088000,
    "updated_at": 1775347200
  }
  ```
</ResponseExample>

## 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`, or another required field is missing or empty                                                  |
| `400`  | `invalid_value`                  | `max_bid_micros` is non-integer or outside `1`–`100000000`, the ads do not share one creative type, or a creative is missing or not an object |
| `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                                                                                                       |
