Developers · HTTP API
Buy AI ads through an API.
The Admux API buys ads in ChatGPT, Google Search including AI Overviews and AI Mode, and the Gravity network from one prepaid USD balance. Your code or your agent can upload creatives, draft and validate campaigns, get a price, reserve budget, launch and read reports, with a key whose scopes and spending limits you set.
What a key can do
| Area | Endpoints |
|---|
Keys can’t top up, refund, manage billing documents, members or other keys: those stay with people in the web app. Reading these docs grants nothing; Admux enforces each key’s scopes and limits on every request.
Quickstart
A ChatGPT ads campaign from upload to submission. The same steps work for Google and Gravity: add them to channels. Admux tests this script against its own API (scripts/examples-api.sh).
export ADMUX_API_KEY=amx_... # created in the web app, shown once
API=https://api.admux.ai
AUTH="Authorization: Bearer $ADMUX_API_KEY"
curl -s "$API/v1/channels" -H "$AUTH" | jq '.channels[] | {id, minBudgetCents}'
IMG=$(curl -s -X POST "$API/v1/assets/images" -H "$AUTH" -H 'content-type: application/json' \
-d "{\"name\":\"hero.png\",\"contentBase64\":\"$(base64 < hero.png | tr -d '\n')\"}" | jq -r '.version.id')
END=$(date -u -v+7d +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -d '+7 days' +%Y-%m-%dT%H:%M:%SZ)
DRAFT=$(curl -s -X POST "$API/v1/campaigns" -H "$AUTH" -H 'content-type: application/json' -d "{
\"name\": \"Spring launch\",
\"draft\": {
\"destinationUrl\": \"https://example.com/launch\", \"brandName\": \"Acme Analytics\",
\"shared\": { \"headline\": \"Find where users drop off\", \"body\": \"Funnels and retention for product teams.\", \"imageVersionId\": \"$IMG\" },
\"channels\": [\"chatgpt\"], \"budgetCents\": 50000,
\"targeting\": { \"countries\": [\"US\"], \"languages\": [], \"keywords\": [], \"negativeKeywords\": [], \"contextHints\": [\"product analytics\"], \"platforms\": [] },
\"schedule\": { \"startMode\": \"asap\", \"startAt\": null, \"endAt\": \"$END\" }
}
}")
CAMPAIGN=$(echo "$DRAFT" | jq -r '.id'); VERSION=$(echo "$DRAFT" | jq -r '.draftVersion')
curl -s -X POST "$API/v1/campaigns/$CAMPAIGN/validate" -H "$AUTH" | jq # {"valid": true, "issues": []}
OFFER=$(curl -s -X POST "$API/v1/campaigns/$CAMPAIGN/offers" -H "$AUTH" | jq -r '.id')
OP=$(curl -s -X POST "$API/v1/campaigns/$CAMPAIGN/submit" -H "$AUTH" -H 'content-type: application/json' \
-H "Idempotency-Key: submit-$CAMPAIGN" \
-d "{\"offerId\":\"$OFFER\",\"expectedDraftVersion\":$VERSION,\"launchMode\":\"review\"}" | jq -r '.operationId')
curl -s "$API/v1/operations/$OP" -H "$AUTH" | jq '{status}'
curl -s "$API/v1/campaigns/$CAMPAIGN" -H "$AUTH" | jq '.legs[] | {channel, display: .display.label}'
curl -s "$API/v1/balance" -H "$AUTH" | jq
Submitting reserves the full budget and sends each channel leg to review. With launchMode: "review" (the default), approved legs wait for you: call POST /v1/campaigns/{id}/launch with a key that has campaigns:launch, or launch from the web app.
Scopes and presets
Each key gets a preset or a custom set of scopes. An agent sees and can call only what its key allows.
| Scope | Allows | In presets |
|---|---|---|
campaigns:read | Inspect campaigns, review and delivery states, channel capabilities and customer pricing | Read, Draft, Full |
campaigns:draft | Create and edit unfunded drafts; no live changes or spending commitments | Draft, Full |
campaigns:validate | Validate drafts and request quotes without launching | Draft, Full |
campaigns:reserve | Reserve prepaid funds for review within workspace and key limits (no launch authority) | Full |
campaigns:launch | Launch reviewed legs or authorize automatic launch of the submitted revision | Full |
campaigns:modify_live | Apply supported live changes (additional commitments also need reserve) | Full |
campaigns:pause | Pause delivery | Full |
campaigns:resume | Resume paused delivery | Full |
campaigns:cancel | Cancel campaigns or remove authorized legs | Full |
creatives:read | Read supplied assets and copy | Read, Draft, Full |
creatives:write | Upload supplied assets and create creative versions | Draft, Full |
reports:read | Read performance reports | Read, Draft, Full |
balance:read | Read operational available/reserved funds (no billing or payment data) | Read, Draft, Full |
operations:read | Read status of operations on resources you can access | Read, Draft, Full |
Spending limits
A key can carry daily, weekly, monthly and lifetime limits in USD. They cap new budget authorizations, such as a submission or a budget increase, per UTC period. They don’t pace delivery, which each channel controls.
A request over the limit fails with KEY_ALLOWANCE_EXCEEDED and reserves nothing. Expired or revoked keys are rejected on every request, pause and cancel included.
Retries and errors
Every request that moves money or changes delivery needs an Idempotency-Key header. Repeating the same key with the same body returns the original result; a different body with that key returns 409.
Errors look like {"error":{"code","message","details"}}, with codes such as OFFER_EXPIRED, VERSION_CONFLICT, INSUFFICIENT_FUNDS, AUTHORITY_DENIED and REVIEW_REQUIRED. Lists use cursor pagination.
Questions developers ask
Is there an API to buy ChatGPT ads?
OpenAI has its own Advertiser API for its ad accounts. The Admux API buys ChatGPT ads together with Google Search (including AI placements) and Gravity, from one prepaid balance and one key, without opening an account with each provider.
Can my agent spend money without me?
Only within its key. Submitting reserves budget and needs campaigns:reserve, launching needs campaigns:launch, and the key’s spending limits cap new budget authorizations. By default approved campaigns wait for an explicit launch.
How do I get an API key?
A workspace owner or campaign manager creates it in the Admux web app, picks a preset (Read, Draft, Full) or custom scopes, and sets optional spending limits. The key is shown once.
Is there an OpenAPI document?
Yes: https://api.admux.ai/openapi.json. The reference at /docs is generated from the same document.