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.
Money on campaign resources is expressed in integer micros, where
$1 = 1,000,000. So budget.amount_micros: 5000000 is $5.00. Timestamps are ISO 8601 strings in UTC (2026-04-01T00:00:00Z), never Unix integers.The campaign object
string
Unique campaign identifier (UUID).
string
Campaign name (3–100 characters).
string | null
Free-text description, or
null if none was set.string
string
Read-only moderation state:
in_review | approved | rejected. A freshly created campaign is in_review. See Review status.string
Campaign start, as an ISO 8601 timestamp in UTC. 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.string | null
Campaign end, as an ISO 8601 timestamp in UTC.
null means the campaign has no wall-clock stop. end_time is orthogonal to budget.period — all four combinations are legal.object
The campaign budget, stated as an amount plus the window it applies to.
string
Bidding basis:
cpm (cost per thousand impressions) or cpc (cost per click).string | null
ISO 8601 timestamp of Thrad’s reversible archive — a list-visibility flag with no effect on delivery — or
null. This is a different concept from the /archive action, which is the terminal transition to status: "archived". A campaign can read status: "archived" while archived_at is still null, or vice versa. /v1/ lists always include reversibly-archived campaigns.object
string
Creation time, as an ISO 8601 timestamp in UTC.
string
Last-modified time, as an ISO 8601 timestamp in UTC.
Budget: amount and period
A Thrad budget is self-describing — you always send both the amount and the window it applies to, and the two together determine how spend is enforced.budget.period is required on create and is locked once the campaign launches — /v1 campaigns launch on create, and the billing schedule locks with them. A later update that changes it returns 400 recurrence_locked_after_launch on budget.period; restating the current period is a no-op.end_time is a wall-clock stop, independent of budget.period. A lifetime budget with an end_time is a bounded one-off (the cap survives the stop); a periodic budget without one is an open-ended subscription.
Review status
Every campaign created through this API is submitted to Thrad for human review. The two status fields are independent:statusis the lifecycle state you control:active|paused|archived.review_statusis the moderation verdict you do not control:in_review|approved|rejected.
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 it. If it is rejected, review_status becomes rejected.
Approval (the
in_review → approved transition) is performed by Thrad, not through this API. Until a campaign is approved, /activate returns 400 campaign_not_approved.List campaigns
Returns a paginated list of campaigns for your organization, newest first by default.string
Restrict the list to a single ad account (UUID). Omit to list across all ad accounts in your organization.
integer
default:"20"
Number of campaigns to return,
1–500.string
default:"desc"
Sort direction by creation time:
asc or desc.string
Cursor for the previous page — pass a prior response’s
first_id. Mutually exclusive with after.string
Cursor for the next page — pass a prior response’s
last_id. Mutually exclusive with before.Create a campaign
Creates a campaign together with its ad groups and ads in a single nested request, then submits the campaign for review.1
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.2
Create the tree
The campaign, its
ad_groups[], and each ad group’s ads[] are created together. Every ad must reference an existing asset by asset_id — browse the catalog with GET /v1/assets.3
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.4
Submit for review
The campaign is submitted to Thrad. The response comes back with
status: "paused" and review_status: "in_review".The whole tree comes back paused. The ad groups and ads created by this call also read
status: "paused" while the campaign awaits review, so nothing can serve before approval. They return to active automatically when the campaign is approved — do not call /activate on them. (Children added later with POST /v1/ad_groups or POST /v1/ads are created active.)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.string
required
Campaign name, 3–100 characters.
object
required
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
card or all poll).string
Free-text description.
string
Campaign start, as an ISO 8601 timestamp with a UTC offset or trailing
Z (e.g. 2026-04-02T00:00:00Z). 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. A bare wall-clock time (no offset) is rejected with 400 invalid_value.string
Campaign end, as an ISO 8601 timestamp with a UTC offset or trailing
Z. Omit for a campaign with no wall-clock stop. This is independent of budget.period — see Budget: amount and period.object
string
default:"cpm"
Bidding basis:
cpm or cpc.string
The ad account to create the campaign under (UUID). Defaults to your organization’s default ad account.
Retrieve a campaign
Fetches a single campaign by ID.string
required
The campaign UUID. An unknown or cross-organization ID returns
404.Update a campaign
Updates the mutable fields of a campaign. Only the fields you send are changed.name, description, budget.amount_micros, targeting, start_time, and end_time. Every value is re-validated exactly as on create: budget.amount_micros must still be >= 990000, and any start_time / end_time must be an ISO 8601 timestamp carrying a UTC offset or trailing Z. An out-of-range value or a malformed timestamp returns 400 invalid_value (never a 500).
Sending budget.amount_micros alone keeps the current period — there is no read-before-write. Budget edits also respect a floor rule: an amount cannot be lowered below spend already charged.
string
required
The campaign UUID.
string
New campaign name, 3–100 characters.
string
New description.
object
object
New geo targeting. Same shape as on create —
locations.include[] with country_code per entry.string
New start, as an ISO 8601 timestamp with a UTC offset or trailing
Z. Send null to clear it.string
New end, as an ISO 8601 timestamp with a UTC offset or trailing
Z. Send null to remove the wall-clock stop. This never changes the recurrence shape.Activate a campaign
Resumes a previously paused campaign. The campaign must already be approved.- If the campaign is paused and approved, it transitions to
active. - If the campaign is already running, this is a no-op and returns
200with the unchanged campaign. - If the campaign is not yet approved (still
in_review, or rejected), it returns400 campaign_not_approved.
string
required
The campaign UUID.
Pause a campaign
Pauses a running campaign so it stops serving.active) campaign can be paused. Any other state returns 400 campaign_not_running.
string
required
The campaign UUID.
Archive a campaign
Ends a campaign permanently. An archived campaign cannot be reactivated.400 cannot_archive. Archiving sets status to archived.
string
required
The campaign UUID.
Error codes
Every error uses the bare shape:
