API Reference
Read and optimize Apple Ads accounts over HTTP.
The REST API exposes the same capabilities as the MCP server over
plain HTTP, for the callers MCP can't reach: cron jobs, backend services,
automation platforms, and curl.
Base URL: /api/v1. Authenticate with a key from Workspace → Settings → API
keys.
curl https://your-app.example.com/api/v1/accounts \
-H "Authorization: Bearer tsk_your_key"Four things that will surprise you
Read these before you write any code against this API. Each one is a real property of Apple Ads, not a quirk of ours.
1. Every write is a proposal, not a mutation
A POST to an action endpoint does not change your account. It creates a
decision row, and the safe-apply policy
then either applies it immediately or queues it for a human:
{
"actionId": "9f2c…",
"kind": "budget_change",
"mode": "queued",
"riskTier": "review",
"policyReasons": ["Budget raises above 30% are review-tier."]
}Both auto_applied and queued are successes. And mode is a snapshot,
not an outcome — the apply worker re-runs the policy before touching Apple and
can still demote an auto-approved action back to proposed. Poll
GET /accounts/{projectId}/actions for terminal state.
See Policy states.
2. We only see what we sync
Apple has no change-events API and no webhooks. Everything this API returns comes from our warehouse, refreshed by a nightly full pull and a six-hourly incremental. So:
- Changes made in the Apple Ads UI are invisible here until the next sync.
- The action ledger is our record of our own changes, not an account history.
GET /accounts/{projectId}/syncsshould be the first call in any automation.
Account-scoped reads carry a dataFreshness object. If it has a warning,
relay it — don't act through it.
3. Apple's vocabulary, not Google's
Taps, not clicks. Installs, not conversions. TTR, CPT, CPA. There is no ROAS, quality score, impression share, ad copy, audience or experiment anywhere in this API, because Apple doesn't have them.
Status enums split by entity: campaigns, ad groups and ads are
ENABLED|PAUSED, but keywords are ACTIVE|PAUSED. Filtering keywords on
ENABLED returns zero rows with no error — a silent bug worth knowing about.
Keywords are EXACT or BROAD only; there is no PHRASE. A storefront is a
country.
4. projectId is not Apple's org id
Every account-scoped path takes {projectId} — the project UUID from
GET /accounts. Apple's own asaOrgId is returned alongside it but is not
addressable. With an account-scoped key you can write the literal current
instead:
curl .../api/v1/accounts/current/campaigns -H "Authorization: Bearer tsk_…"Money is in micros
Any field whose name ends in Micros is in micros: 1,000,000 micros = 1 unit
of the account currency, rounded to Apple's 10,000-micro minimum. Every
response that carries money also carries the account currency.
What this API can't do
Stated plainly so you don't go looking:
- Connect an account. There is no Apple Ads OAuth flow and no consent
screen. Credentials must be pasted into the dashboard by a person.
GET /connectiontells you whether that's been done. - Mint API keys. A key cannot create keys.
- Dry-run a change. Apple has no
validate_onlyendpoint and no sandbox. Validation runs locally at propose time and rejects with422. - Bid modifiers. Targeting is set membership; to bid differently on a segment, split it into its own ad group.