Skip to main content
Apple Ads is a closed beta. Accounts without it receive 404 from every tool on this page — the same answer as a nonexistent endpoint, by design. Ask support to enable it before wiring an assistant to it.
The MCP server exposes the Apple Ads section of the dashboard as four tools: three that read and one family that changes live campaigns. This page covers what each one answers, and — more importantly — the handful of ways their output is easy to misread.

The tools

Reading the report

get_apple_ads_report returns one row per entity at the requested level, each carrying a qonversion block (cohort outcomes), an apple block (spend and delivery), and derived ratios.
avg_cpa is not CAC. apple.avg_cpa is Apple’s cost per tap-through install. The cost of acquiring a paying user is windows.<code>.cac, and the two differ by the entire install→payer funnel. Quoting one as the other overstates efficiency by an order of magnitude on most accounts.

Closed maturation windows

Row-level revenue/roas are cumulative to date: revenue keeps accruing while spend is bounded by the period, so they cannot compare cohorts of different ages. Request windows (d0, d7, d30, … lifetime) and every row gains a block computed only over install dates that have fully matured.
  • is_closed: false means the numbers cover a closed subrange[closed_from, closed_through], not [from, to]. They are provisional, and the ratios can move in either direction. Say the window is still maturing and name closed_through.
  • d0 means through the end of the install UTC day, not the first 24 hours from the install moment.
  • lifetime never closes, by definition.
  • null is not zero. A missing ratio means “not available yet”; a spend_closed: 0 is a measured zero.
  • A date is only final once it is past both the window length plus the buffer and the restatement floor (meta.windows.spend_snapshot.rewrite_days_effective) — the store rewrites its most recent days, and no date is called final while that can still happen.

Markers to check before quoting anything

partial_sources (the Apple side degraded — affected rows have apple: null), meta.partial_scope (how many campaigns lack spend), meta.is_partial_period (the range includes today), meta.windows.spend_snapshot (three distinct states: normal, still backfilling, read failed), and meta.request.filters_applied — which lists only the filters that were recognised, so check it before assuming a filter took effect.

Reading the series

get_apple_ads_series plots one metric per UTC day.
Do not loop the report to build a time series. The report’s cohort metrics are cumulative to date from each install date; a series point is the value attributed to that day. The two answer different questions and return different numbers.
  • value: null is unknown (outside the spend snapshot’s coverage, missing from it, or an empty denominator), never zero.
  • is_cohort_metric: true means the point belongs to the cohort acquired that day — revenue on 3 March is “what the people acquired on 3 March have paid to date”, not revenue collected on 3 March.
  • filters_ignored lists filters that were accepted and not applied (the entity-state filters have no meaning on a time axis). Non-empty means your numbers answer a wider question than you asked.

Checking the connection

get_apple_ads_connection_status exists because an empty report and a refused change look identical whether the cause is “no data yet”, “the Apple authorization expired” or “this Apple role cannot write”. Call it before concluding anything from either. It returns two independent readiness axes:
  • reporting — whether performance data can flow.
  • campaign_management — whether changes are allowed, with can_write, the per-orgs[] detail, and the exact Apple role names.
connected therefore does not imply write access; the ordinary case is a perfectly working integration whose Apple role only reads. Other states worth naming precisely: The response never contains a private key, client secret, token, authorization code or OAuth state — and the tool cannot start the sign-in flow. It returns dashboard_url instead, which is where a human completes it. Never paste a .p8 key or a client secret into a chat.

Changing campaigns

The write tools require the separate apple_ads:write scope and a dashboard user who is allowed to manage campaigns. A project Secret Key on its own is refused: that key authorizes the whole public API and tends to live in CI variables, while the right to spend money is granted per member in the dashboard.
These tools change a live Apple Ads account and spend real money. Read the report first: you need the entity ids and the current value, because the current value is mandatory.

The rules

  • before_* is required. It is the optimistic lock (a 409 means the value changed under you — re-read the report, do not retry), the source an undo would use, and the sign of the spend change that the daily caps count.
  • One action per request: a status, a bid, or a daily budget — never two.
  • Confirmation is asymmetric. Pausing an entity or lowering a value applies immediately. Enabling one or raising a value is parked for human confirmation via confirm_destructive_action — the token is addressed to the operator, not a cue for the assistant to confirm its own request.
  • idempotency_key makes a retry the same attempt rather than a second change. Omit it and one is generated for this attempt; pass the same one back when retrying after an unknown outcome.
  • dry_run: true changes nothing and returns would_send, the exact payload Apple would have received. Use it before any first-time or unusual change.
  • Amounts are decimal strings in the advertising account’s currency — never the currency you requested a report in, and never converted.

Outcomes that must be handled differently

A success carries action_id (the audit row — name it when describing what you did). If it also carries baseline_unverified: true, there was no confirmed history for that entity, the ×3 raise ceiling could not be applied, and a human should look at the result.

The bid you read back may be stale

Bids and budgets in a report row come from Apple’s reporting metadata and can lag by hours — including behind a change you just made yourself. Confirm a write from the write’s own response, not by re-reading the report a second later.

Rate limits

Analytics tools are limited to 30 requests per minute, counted per project and per identical request. Over the limit you get a 429 with Retry-After — never a cached answer pretending to be fresh. Write actions have their own tighter limits plus daily caps on spend-increasing changes.