Base URL
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 — 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 withads[]) that auto-launches on submit. - Ad Group —
context_hintsand abidding_config.max_bid_micros. Add ad groups to an existing campaign. - Ad — the creative (
chat_cardorpoll), with title, body, target URL, and image.
Two surfaces
The API is split into two surfaces that share authentication but differ in shape and purpose.Resources
Write surface. CRUD over campaigns, ad groups, ads, and the ad account, plus
/activate /pause /archive lifecycle actions and /upload for creative images. Returns bare list/object shapes. Resource money is in integer micros.Insights
Read-only analytics across the four resource levels. Combines Thrad + ChatGPT supply by default. Returns a bare list of metric rows where
spend, cpc, and cpm are dollar floats and ctr is a fraction.Endpoints at a glance
| Resource | Operation | Method & path |
|---|---|---|
| Ad account | Get | GET /ad_account |
| Upload | Create image | POST /upload |
| 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 theAuthorization header.
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.401 — see Authentication.
Response shapes
Resource and insights responses use bare OpenAI-style shapes, not the platform{ success, data, error, meta } envelope.
A list response wraps its rows and carries "object": "list". Single-object responses (a campaign, ad group, ad, ad account, or upload) are returned bare — they do not include an object field.
error object:
type is one of invalid_request_error, authentication_error, rate_limit_error, or server_error.
Money — two scales
- Resource bodies use integer micros, where
$1 = 1,000,000.budget.lifetime_spend_limit_microshas a minimum of990000($0.99) and no upper cap.bidding_config.max_bid_microsranges1–100000000($0.000001–$100). - Insights responses use dollar floats.
spend,cpc, andcpmare already divided into dollars (e.g.0.70means $0.70), andctris a fraction (e.g.0.10means 10%) — these are not micros.
Timestamps
All/v1/ timestamps — start_time, end_time, created_at, updated_at — are Unix seconds as integers. (The dashboard-side key-management endpoints under /api/organizations/{org}/api-keys/ are JWT/envelope endpoints and instead return ISO 8601 string timestamps.)
Status and review
Each campaign, ad group, and ad exposes a writablestatus and a read-only review_status.
| Field | Values |
|---|---|
status | active · paused · archived |
review_status (read-only) | approved · in_review · rejected |
/activate, /pause, and /archive sub-actions. For campaigns, sending status in an update body is rejected with 400 use_status_action.
Ad groups and ads accept
status in the update body. Sending status: active | paused | archived on an ad-group or ad update is accepted: active keeps the child active, while paused or archived deactivate it. Thrad children have no separate archived state, so a deactivated child reads back as paused — the same result as the /pause or /archive sub-actions. This is a deliberate divergence from OpenAI.Pagination
List endpoints are cursor-paginated. Thelimit 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.
Number of items per page. Resource lists:
1–500. Insights lists: 1–2000.Return items before this cursor. Echo a prior response’s
first_id. Mutually exclusive with after.Return items after this cursor. Echo a prior response’s
last_id. Mutually exclusive with before.Sort direction:
asc or desc.Rate limits
The API is throttled to 1000 requests per hour per key. The chargingPOST /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.
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.Next steps
Quickstart
Mint a key and create, launch, and read insights for your first campaign with cURL.
Authentication
How the Bearer key works, where to create it, and how to rotate or revoke it.
