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

# Campaigns

> Create, list, retrieve, update, and control the lifecycle of advertiser campaigns.

A campaign is the top of the advertiser object tree: it carries the budget, schedule, geo targeting, and pricing model, and it owns one or more ad groups, each of which owns one or more ads. Creating a campaign is a **single nested call** — you send the campaign plus its `ad_groups[]` (each with its `ads[]`) in one request, and Thrad creates the whole tree, charges a deposit, and submits it for review.

All paths on this page are relative to the base URL `https://api.thrad.ai/v1`. Every request must send `Authorization: Bearer ak_...` — see [Authentication](/advertisers/api-reference/authentication).

<Note>
  Money on campaign resources is expressed in **integer micros**, where `$1 = 1,000,000`. So `budget.lifetime_spend_limit_micros: 5000000` is `$5.00`. This is different from the [Insights](/advertisers/api-reference/insights) API, which returns dollar floats. State the unit you mean — micros here, dollars there.
</Note>

## The campaign object

<ResponseField name="id" type="string">
  Unique campaign identifier (UUID).
</ResponseField>

<ResponseField name="name" type="string">
  Campaign name (3–100 characters).
</ResponseField>

<ResponseField name="description" type="string | null">
  Free-text description, or `null` if none was set.
</ResponseField>

<ResponseField name="status" type="string">
  Lifecycle state: `active` | `paused` | `archived`. Change it only through the [`/activate`](#activate-a-campaign), [`/pause`](#pause-a-campaign), and [`/archive`](#archive-a-campaign) sub-actions — sending `status` in a campaign update body is rejected.
</ResponseField>

<ResponseField name="review_status" type="string">
  Read-only moderation state: `in_review` | `approved` | `rejected`. A freshly created campaign is `in_review`. See [Review status](#review-status).
</ResponseField>

<ResponseField name="start_time" type="integer">
  Campaign start, as a Unix timestamp in seconds. Always present — if you omit `start_time` on create, Thrad sets it to roughly two days in the future (the earliest allowed launch window), so a read response is never `null` here.
</ResponseField>

<ResponseField name="end_time" type="integer | null">
  Campaign end, as a Unix timestamp in seconds. `null` means open-ended — the campaign spends until the budget is exhausted.
</ResponseField>

<ResponseField name="budget" type="object">
  <Expandable title="budget properties">
    <ResponseField name="lifetime_spend_limit_micros" type="integer">
      Lifetime spend cap, in micros (`$1 = 1,000,000`).
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="recurring_frequency" type="string">
  Spend cadence: `daily` | `weekly` | `monthly` | `quarterly` | `yearly`.
</ResponseField>

<ResponseField name="pricing_model" type="string">
  Bidding basis: `cpm` (cost per thousand impressions) or `cpc` (cost per click).
</ResponseField>

<ResponseField name="targeting" type="object">
  <Expandable title="targeting properties">
    <ResponseField name="locations" type="object">
      Contains `include`, an array of location objects. Each location carries `id`, `type` (`country`), and `country_code` (ISO 3166-1 alpha-2, e.g. `"US"`).
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="created_at" type="integer">
  Creation time, as a Unix timestamp in seconds.
</ResponseField>

<ResponseField name="updated_at" type="integer">
  Last-modified time, as a Unix timestamp in seconds.
</ResponseField>

## Review status

Every campaign created through this API is submitted to Thrad for human review. The two status fields are independent:

* `status` is the lifecycle state you control: `active` | `paused` | `archived`.
* `review_status` is the moderation verdict you do **not** control: `in_review` | `approved` | `rejected`.

A newly created campaign always comes back as `status: "paused"`, `review_status: "in_review"`. It cannot serve or be activated until Thrad approves it. Once approved, `review_status` becomes `approved` and you can [activate](#activate-a-campaign) it. If it is rejected, `review_status` becomes `rejected`.

<Note>
  Approval (the `in_review → approved` transition) is performed by Thrad, not through this API. Until a campaign is approved, [`/activate`](#activate-a-campaign) returns `400 campaign_not_approved`.
</Note>

***

## List campaigns

Returns a paginated list of campaigns for your organization, newest first by default.

```
GET /v1/campaigns
```

<ParamField query="ad_account_id" type="string">
  Restrict the list to a single ad account (UUID). Omit to list across all ad accounts in your organization.
</ParamField>

<ParamField query="limit" type="integer" default="20">
  Number of campaigns 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/campaigns?limit=20 \
    -H "Authorization: Bearer $THRAD_ADS_API_KEY"
  ```

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

  resp = requests.get(
      "https://api.thrad.ai/v1/campaigns",
      headers={"Authorization": f"Bearer {os.environ['THRAD_ADS_API_KEY']}"},
      params={"limit": 20},
  )
  print(resp.json())
  ```
</RequestExample>

<ResponseExample>
  ```json 200 - List theme={null}
  {
    "object": "list",
    "data": [
      {
        "id": "8b1f0c2e-4d3a-4f1b-9a2c-1e2d3f4a5b6c",
        "name": "Spring running launch",
        "description": "Marathon season push",
        "status": "active",
        "review_status": "approved",
        "start_time": 1775088000,
        "end_time": null,
        "budget": { "lifetime_spend_limit_micros": 50000000 },
        "recurring_frequency": "daily",
        "pricing_model": "cpm",
        "targeting": {
          "locations": {
            "include": [
              { "id": "US", "type": "country", "country_code": "US" }
            ]
          }
        },
        "created_at": 1774915200,
        "updated_at": 1775001600
      }
    ],
    "count": 1,
    "first_id": "8b1f0c2e-4d3a-4f1b-9a2c-1e2d3f4a5b6c",
    "last_id": "8b1f0c2e-4d3a-4f1b-9a2c-1e2d3f4a5b6c",
    "has_more": false
  }
  ```
</ResponseExample>

***

## Create a campaign

Creates a campaign together with its ad groups and ads in a single nested request, then submits the campaign for review.

```
POST /v1/campaigns
```

This one call does several things atomically:

<Steps>
  <Step title="Validate billing">
    Your organization must have a **default card** on file. If it does not, the request fails with `400 billing_not_configured` and nothing is created.
  </Step>

  <Step title="Create the tree">
    The campaign, its `ad_groups[]`, and each ad group's `ads[]` are created together. Each ad's creative image becomes a stored asset.
  </Step>

  <Step title="Charge the deposit">
    A **\$10 deposit** is charged off-session on your default card. A card decline returns `402 card_declined` and the campaign is removed (no lingering draft); a provider outage returns `502 payment_provider_error`.
  </Step>

  <Step title="Submit for review">
    The campaign is submitted to Thrad. The response comes back with `status: "paused"` and `review_status: "in_review"`.
  </Step>
</Steps>

<ParamField header="Idempotency-Key" type="string">
  A client-generated key (e.g. a UUID) that makes retries safe. The same key replays the first response indefinitely (there is no expiry window), so a retried create never charges twice. Reusing the same key with a **different** body returns `409 idempotency_key_reused`.
</ParamField>

<ParamField body="name" type="string" required>
  Campaign name, 3–100 characters.
</ParamField>

<ParamField body="budget" type="object" required>
  <Expandable title="budget properties">
    <ParamField body="lifetime_spend_limit_micros" type="integer" required>
      Lifetime spend cap, in micros (`$1 = 1,000,000`). Minimum `990000` (\$0.99). There is no upper cap.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="ad_groups" type="array" required>
  One or more ad groups, each with at least one ad. All ads within a single ad group must share one creative type (all `chat_card` or all `poll`).

  <Expandable title="ad_group properties">
    <ParamField body="name" type="string">
      Ad group name.
    </ParamField>

    <ParamField body="context_hints" type="array">
      Array of strings describing the conversational contexts where this ad group should serve.
    </ParamField>

    <ParamField body="bidding_config" type="object">
      <Expandable title="bidding_config properties">
        <ParamField body="max_bid_micros" type="integer">
          Maximum bid, in micros. Range `1`–`100000000` ($0.000001–$100).
        </ParamField>
      </Expandable>
    </ParamField>

    <ParamField body="ads" type="array" required>
      One or more ads. See [Ads](/advertisers/api-reference/ads) for the full `creative` schema.

      <Expandable title="ad properties">
        <ParamField body="name" type="string">
          Ad name. Defaults to the creative headline or poll question if omitted.
        </ParamField>

        <ParamField body="creative" type="object" required>
          The creative. For a `chat_card`: `title`, `body`, optional `cta`, and an image via `file_id` or `image_url`. For a `poll`: `title`, `options[]`, `target_url`.
        </ParamField>
      </Expandable>
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="description" type="string">
  Free-text description.
</ParamField>

<ParamField body="start_time" type="integer">
  Campaign start, as a Unix timestamp in seconds. Omit to default to roughly two days in the future (the earliest allowed launch window) — the response then comes back with that computed timestamp, never `null`.
</ParamField>

<ParamField body="end_time" type="integer">
  Campaign end, as a Unix timestamp in seconds. Omit for an open-ended campaign that spends until the budget is exhausted.
</ParamField>

<ParamField body="targeting" type="object">
  <Expandable title="targeting properties">
    <ParamField body="locations" type="object">
      Contains `include`, an array of location objects. Each entry needs a `country_code` (ISO 3166-1 alpha-2, e.g. `"US"`) — or an `id` carrying the same code. See [Campaign targeting](/advertisers/api-reference/campaign-targeting).
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="recurring_frequency" type="string" default="daily">
  Spend cadence: `daily` | `weekly` | `monthly` | `quarterly` | `yearly`.
</ParamField>

<ParamField body="pricing_model" type="string" default="cpm">
  Bidding basis: `cpm` or `cpc`.
</ParamField>

<ParamField body="ad_account_id" type="string">
  The ad account to create the campaign under (UUID). Defaults to your organization's default ad account.
</ParamField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://api.thrad.ai/v1/campaigns \
    -H "Authorization: Bearer $THRAD_ADS_API_KEY" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: 6f9619ff-8b86-d011-b42d-00cf4fc964ff" \
    -d '{
      "name": "Spring running launch",
      "description": "Marathon season push",
      "budget": { "lifetime_spend_limit_micros": 50000000 },
      "start_time": 1775088000,
      "recurring_frequency": "daily",
      "pricing_model": "cpm",
      "targeting": {
        "locations": { "include": [ { "country_code": "US" } ] }
      },
      "ad_groups": [
        {
          "name": "Lightweight shoes",
          "context_hints": ["running", "marathon training"],
          "bidding_config": { "max_bid_micros": 8000000 },
          "ads": [
            {
              "name": "Pro Racer card",
              "creative": {
                "type": "chat_card",
                "title": "Premium Running Shoes",
                "body": "Lightweight and built for marathon pace.",
                "cta": "Shop now",
                "image_url": "https://cdn.example.com/shoes.jpg"
              }
            }
          ]
        }
      ]
    }'
  ```

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

  resp = requests.post(
      "https://api.thrad.ai/v1/campaigns",
      headers={
          "Authorization": f"Bearer {os.environ['THRAD_ADS_API_KEY']}",
          "Idempotency-Key": str(uuid.uuid4()),
      },
      json={
          "name": "Spring running launch",
          "budget": {"lifetime_spend_limit_micros": 50000000},
          "start_time": 1775088000,
          "targeting": {"locations": {"include": [{"country_code": "US"}]}},
          "ad_groups": [
              {
                  "name": "Lightweight shoes",
                  "context_hints": ["running", "marathon training"],
                  "bidding_config": {"max_bid_micros": 8000000},
                  "ads": [
                      {
                          "name": "Pro Racer card",
                          "creative": {
                              "type": "chat_card",
                              "title": "Premium Running Shoes",
                              "body": "Lightweight and built for marathon pace.",
                              "cta": "Shop now",
                              "image_url": "https://cdn.example.com/shoes.jpg",
                          },
                      }
                  ],
              }
          ],
      },
  )
  print(resp.json())
  ```
</RequestExample>

<ResponseExample>
  ```json 201 - Created (submitted for review) theme={null}
  {
    "id": "8b1f0c2e-4d3a-4f1b-9a2c-1e2d3f4a5b6c",
    "name": "Spring running launch",
    "description": "Marathon season push",
    "status": "paused",
    "review_status": "in_review",
    "start_time": 1775088000,
    "end_time": null,
    "budget": { "lifetime_spend_limit_micros": 50000000 },
    "recurring_frequency": "daily",
    "pricing_model": "cpm",
    "targeting": {
      "locations": {
        "include": [
          { "id": "US", "type": "country", "country_code": "US" }
        ]
      }
    },
    "created_at": 1774915200,
    "updated_at": 1774915200
  }
  ```

  ```json 400 - Billing not configured theme={null}
  {
    "error": {
      "message": "Set up a default card in billing settings before creating a campaign.",
      "type": "invalid_request_error",
      "param": null,
      "code": "billing_not_configured"
    }
  }
  ```

  ```json 402 - Card declined theme={null}
  {
    "error": {
      "message": "Your card was declined.",
      "type": "invalid_request_error",
      "param": null,
      "code": "card_declined"
    }
  }
  ```
</ResponseExample>

<Warning>
  The charging create endpoint is rate-limited to **60 requests/hour** per key (on top of the global 1000/hour limit). Always send an `Idempotency-Key` so a network retry replays the original result instead of charging again.
</Warning>

***

## Retrieve a campaign

Fetches a single campaign by ID.

```
GET /v1/campaigns/{campaign_id}
```

<ParamField path="campaign_id" type="string" required>
  The campaign UUID. An unknown or cross-organization ID returns `404`.
</ParamField>

<RequestExample>
  ```bash cURL theme={null}
  curl https://api.thrad.ai/v1/campaigns/8b1f0c2e-4d3a-4f1b-9a2c-1e2d3f4a5b6c \
    -H "Authorization: Bearer $THRAD_ADS_API_KEY"
  ```

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

  campaign_id = "8b1f0c2e-4d3a-4f1b-9a2c-1e2d3f4a5b6c"
  resp = requests.get(
      f"https://api.thrad.ai/v1/campaigns/{campaign_id}",
      headers={"Authorization": f"Bearer {os.environ['THRAD_ADS_API_KEY']}"},
  )
  print(resp.json())
  ```
</RequestExample>

<ResponseExample>
  ```json 200 - Campaign theme={null}
  {
    "id": "8b1f0c2e-4d3a-4f1b-9a2c-1e2d3f4a5b6c",
    "name": "Spring running launch",
    "description": "Marathon season push",
    "status": "active",
    "review_status": "approved",
    "start_time": 1775088000,
    "end_time": null,
    "budget": { "lifetime_spend_limit_micros": 50000000 },
    "recurring_frequency": "daily",
    "pricing_model": "cpm",
    "targeting": {
      "locations": {
        "include": [
          { "id": "US", "type": "country", "country_code": "US" }
        ]
      }
    },
    "created_at": 1774915200,
    "updated_at": 1775001600
  }
  ```
</ResponseExample>

***

## Update a campaign

Updates the mutable fields of a campaign. Only the fields you send are changed.

```
POST /v1/campaigns/{campaign_id}
```

Updatable fields are `name`, `description`, `budget`, `targeting`, `start_time`, and `end_time`. Numeric values are re-validated exactly as on create: `budget.lifetime_spend_limit_micros` must still be `>= 990000`, and any `start_time` / `end_time` must be an integer Unix timestamp. An out-of-range or non-integer value returns `400 invalid_value`.

<Warning>
  You cannot change `status` here. Sending `status` in the body returns `400 use_status_action` — use [`/activate`](#activate-a-campaign), [`/pause`](#pause-a-campaign), or [`/archive`](#archive-a-campaign) instead. `review_status`, `recurring_frequency`, and `pricing_model` are not updatable either.
</Warning>

<ParamField path="campaign_id" type="string" required>
  The campaign UUID.
</ParamField>

<ParamField body="name" type="string">
  New campaign name, 3–100 characters.
</ParamField>

<ParamField body="description" type="string">
  New description.
</ParamField>

<ParamField body="budget" type="object">
  <Expandable title="budget properties">
    <ParamField body="lifetime_spend_limit_micros" type="integer">
      New lifetime spend cap, in micros. Minimum `990000` (\$0.99).
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="targeting" type="object">
  New geo targeting. Same shape as on create — `locations.include[]` with `country_code` per entry.
</ParamField>

<ParamField body="start_time" type="integer">
  New start, as a Unix timestamp in seconds.
</ParamField>

<ParamField body="end_time" type="integer">
  New end, as a Unix timestamp in seconds.
</ParamField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://api.thrad.ai/v1/campaigns/8b1f0c2e-4d3a-4f1b-9a2c-1e2d3f4a5b6c \
    -H "Authorization: Bearer $THRAD_ADS_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Spring running launch (US + CA)",
      "budget": { "lifetime_spend_limit_micros": 75000000 },
      "targeting": {
        "locations": { "include": [ { "country_code": "US" }, { "country_code": "CA" } ] }
      }
    }'
  ```

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

  campaign_id = "8b1f0c2e-4d3a-4f1b-9a2c-1e2d3f4a5b6c"
  resp = requests.post(
      f"https://api.thrad.ai/v1/campaigns/{campaign_id}",
      headers={"Authorization": f"Bearer {os.environ['THRAD_ADS_API_KEY']}"},
      json={
          "name": "Spring running launch (US + CA)",
          "budget": {"lifetime_spend_limit_micros": 75000000},
          "targeting": {
              "locations": {
                  "include": [{"country_code": "US"}, {"country_code": "CA"}]
              }
          },
      },
  )
  print(resp.json())
  ```
</RequestExample>

<ResponseExample>
  ```json 200 - Updated theme={null}
  {
    "id": "8b1f0c2e-4d3a-4f1b-9a2c-1e2d3f4a5b6c",
    "name": "Spring running launch (US + CA)",
    "description": "Marathon season push",
    "status": "active",
    "review_status": "approved",
    "start_time": 1775088000,
    "end_time": null,
    "budget": { "lifetime_spend_limit_micros": 75000000 },
    "recurring_frequency": "daily",
    "pricing_model": "cpm",
    "targeting": {
      "locations": {
        "include": [
          { "id": "US", "type": "country", "country_code": "US" },
          { "id": "CA", "type": "country", "country_code": "CA" }
        ]
      }
    },
    "created_at": 1774915200,
    "updated_at": 1775088123
  }
  ```

  ```json 400 - Status sent in body theme={null}
  {
    "error": {
      "message": "Change status via /activate, /pause, or /archive.",
      "type": "invalid_request_error",
      "param": "status",
      "code": "use_status_action"
    }
  }
  ```
</ResponseExample>

***

## Activate a campaign

Resumes a previously paused campaign. The campaign must already be approved.

```
POST /v1/campaigns/{campaign_id}/activate
```

* If the campaign is **paused and approved**, it transitions to `active`.
* If the campaign is already **running**, this is a no-op and returns `200` with the unchanged campaign.
* If the campaign is **not yet approved** (still `in_review`, or rejected), it returns `400 campaign_not_approved`.

<ParamField path="campaign_id" type="string" required>
  The campaign UUID.
</ParamField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://api.thrad.ai/v1/campaigns/8b1f0c2e-4d3a-4f1b-9a2c-1e2d3f4a5b6c/activate \
    -H "Authorization: Bearer $THRAD_ADS_API_KEY"
  ```

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

  campaign_id = "8b1f0c2e-4d3a-4f1b-9a2c-1e2d3f4a5b6c"
  resp = requests.post(
      f"https://api.thrad.ai/v1/campaigns/{campaign_id}/activate",
      headers={"Authorization": f"Bearer {os.environ['THRAD_ADS_API_KEY']}"},
  )
  print(resp.json())
  ```
</RequestExample>

<ResponseExample>
  ```json 200 - Activated theme={null}
  {
    "id": "8b1f0c2e-4d3a-4f1b-9a2c-1e2d3f4a5b6c",
    "name": "Spring running launch",
    "description": "Marathon season push",
    "status": "active",
    "review_status": "approved",
    "start_time": 1775088000,
    "end_time": null,
    "budget": { "lifetime_spend_limit_micros": 50000000 },
    "recurring_frequency": "daily",
    "pricing_model": "cpm",
    "targeting": {
      "locations": {
        "include": [
          { "id": "US", "type": "country", "country_code": "US" }
        ]
      }
    },
    "created_at": 1774915200,
    "updated_at": 1775090000
  }
  ```

  ```json 400 - Not approved theme={null}
  {
    "error": {
      "message": "Only an approved, paused campaign can be activated.",
      "type": "invalid_request_error",
      "param": null,
      "code": "campaign_not_approved"
    }
  }
  ```
</ResponseExample>

***

## Pause a campaign

Pauses a running campaign so it stops serving.

```
POST /v1/campaigns/{campaign_id}/pause
```

Only a **running** (`active`) campaign can be paused. Any other state returns `400 campaign_not_running`.

<ParamField path="campaign_id" type="string" required>
  The campaign UUID.
</ParamField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://api.thrad.ai/v1/campaigns/8b1f0c2e-4d3a-4f1b-9a2c-1e2d3f4a5b6c/pause \
    -H "Authorization: Bearer $THRAD_ADS_API_KEY"
  ```

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

  campaign_id = "8b1f0c2e-4d3a-4f1b-9a2c-1e2d3f4a5b6c"
  resp = requests.post(
      f"https://api.thrad.ai/v1/campaigns/{campaign_id}/pause",
      headers={"Authorization": f"Bearer {os.environ['THRAD_ADS_API_KEY']}"},
  )
  print(resp.json())
  ```
</RequestExample>

<ResponseExample>
  ```json 200 - Paused theme={null}
  {
    "id": "8b1f0c2e-4d3a-4f1b-9a2c-1e2d3f4a5b6c",
    "name": "Spring running launch",
    "description": "Marathon season push",
    "status": "paused",
    "review_status": "approved",
    "start_time": 1775088000,
    "end_time": null,
    "budget": { "lifetime_spend_limit_micros": 50000000 },
    "recurring_frequency": "daily",
    "pricing_model": "cpm",
    "targeting": {
      "locations": {
        "include": [
          { "id": "US", "type": "country", "country_code": "US" }
        ]
      }
    },
    "created_at": 1774915200,
    "updated_at": 1775093000
  }
  ```

  ```json 400 - Not running theme={null}
  {
    "error": {
      "message": "Only a running campaign can be paused.",
      "type": "invalid_request_error",
      "param": null,
      "code": "campaign_not_running"
    }
  }
  ```
</ResponseExample>

***

## Archive a campaign

Ends a campaign permanently. An archived campaign cannot be reactivated.

```
POST /v1/campaigns/{campaign_id}/archive
```

Only a **running** or **paused** campaign can be archived. Any other state returns `400 cannot_archive`. Archiving sets `status` to `archived`.

<ParamField path="campaign_id" type="string" required>
  The campaign UUID.
</ParamField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://api.thrad.ai/v1/campaigns/8b1f0c2e-4d3a-4f1b-9a2c-1e2d3f4a5b6c/archive \
    -H "Authorization: Bearer $THRAD_ADS_API_KEY"
  ```

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

  campaign_id = "8b1f0c2e-4d3a-4f1b-9a2c-1e2d3f4a5b6c"
  resp = requests.post(
      f"https://api.thrad.ai/v1/campaigns/{campaign_id}/archive",
      headers={"Authorization": f"Bearer {os.environ['THRAD_ADS_API_KEY']}"},
  )
  print(resp.json())
  ```
</RequestExample>

<ResponseExample>
  ```json 200 - Archived theme={null}
  {
    "id": "8b1f0c2e-4d3a-4f1b-9a2c-1e2d3f4a5b6c",
    "name": "Spring running launch",
    "description": "Marathon season push",
    "status": "archived",
    "review_status": "approved",
    "start_time": 1775088000,
    "end_time": null,
    "budget": { "lifetime_spend_limit_micros": 50000000 },
    "recurring_frequency": "daily",
    "pricing_model": "cpm",
    "targeting": {
      "locations": {
        "include": [
          { "id": "US", "type": "country", "country_code": "US" }
        ]
      }
    },
    "created_at": 1774915200,
    "updated_at": 1775260800
  }
  ```

  ```json 400 - Cannot archive theme={null}
  {
    "error": {
      "message": "Only a running or paused campaign can be archived.",
      "type": "invalid_request_error",
      "param": null,
      "code": "cannot_archive"
    }
  }
  ```
</ResponseExample>

***

## Error codes

| HTTP  | `code`                   | When                                                                                                                                                                                |
| ----- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400` | `required`               | A required field (`name`, `budget.lifetime_spend_limit_micros`, `ad_groups`, an ad group's `ads`) is missing.                                                                       |
| `400` | `invalid_value`          | A field is the wrong type or out of range — e.g. budget below `990000`, `max_bid_micros` outside `1`–`100000000`, a non-integer timestamp, or mixed creative types in one ad group. |
| `400` | `use_status_action`      | `status` was sent in an update body. Use `/activate`, `/pause`, or `/archive`.                                                                                                      |
| `400` | `billing_not_configured` | The organization has no default card; set one up before creating a campaign.                                                                                                        |
| `400` | `campaign_not_approved`  | `/activate` was called on a campaign that is not approved and paused.                                                                                                               |
| `400` | `campaign_not_running`   | `/pause` was called on a campaign that is not running.                                                                                                                              |
| `400` | `cannot_archive`         | `/archive` was called on a campaign that is not running or paused.                                                                                                                  |
| `400` | `no_ad_account`          | No `ad_account_id` was supplied **and** the organization has zero ad accounts. A supplied-but-unknown `ad_account_id` returns `404 not_found` instead.                              |
| `401` | `auth_required`          | The `Authorization` header is missing entirely.                                                                                                                                     |
| `401` | `invalid_api_key`        | The key is malformed, unknown, or revoked.                                                                                                                                          |
| `402` | `card_declined`          | The \$10 deposit charge was declined. Nothing is created.                                                                                                                           |
| `404` | `not_found`              | The `campaign_id` is unknown or belongs to another organization.                                                                                                                    |
| `409` | `idempotency_key_reused` | The same `Idempotency-Key` was reused with a different request body.                                                                                                                |
| `409` | `name_conflict`          | A campaign with that name already exists.                                                                                                                                           |
| `429` | `rate_limit_exceeded`    | The 1000/hour key limit (or the 60/hour create limit) was exceeded.                                                                                                                 |
| `502` | `payment_provider_error` | The payment provider was unavailable. Nothing is created; retry later.                                                                                                              |

Every error uses the bare shape:

```json theme={null}
{
  "error": {
    "message": "A human-readable description.",
    "type": "invalid_request_error",
    "param": "budget.lifetime_spend_limit_micros",
    "code": "invalid_value"
  }
}
```

<Tip>
  Once a campaign exists, manage its ad groups and ads through [Ad groups](/advertisers/api-reference/ad-groups) and [Ads](/advertisers/api-reference/ads), and track delivery with [Insights](/advertisers/api-reference/insights).
</Tip>
