Apple Search Ads Attribution: Why 3 Dashboards Disagree

Your Apple Ads console, App Analytics, and MMP disagree by design, not by defect. Apple counts an App Store download — redownloads included — inside a fixed 30-day tap window, while your MMP counts first-open with cross-network deduplication. Trust the console for spend and paid campaign performance, trust the MMP for post-install value and last-touch, and stop trying to reconcile install-for-install. That, in three sentences, is the whole of Apple Search Ads attribution once you stop expecting the numbers to match.

The discrepancy that got escalated as fraud

A UA team I worked with pulled me into a Slack thread flagged red. Their Apple Ads console showed roughly 900 basis points more installs than their MMP for the same Search Ads campaign, same week, same country. The working theory on the thread was click injection, a fraud vector where a malicious app listens for install broadcasts and fires a last-second click to steal attribution credit.

It was a reasonable guess. A 9% gap is the kind of number that funds a chargeback request, and someone had already drafted an email to the network asking for a refund estimate in the low four figures.

The problem: there was no network to refund. This was Apple Search Ads, attributed through Apple's own AdServices API, and there is no third-party click to inject against on that rail. The "fraud" turned out to be two definitional mismatches stacked on top of each other. Apple was counting App Store downloads, including redownloads from lapsed users, while the MMP was counting first-opens of net-new devices only. Nobody stole anything. Both dashboards measured different events and labeled both "install."

We closed the ticket. No refund, because there was nothing to refund. But the team had burned a day chasing a ghost, and that day is why this article exists. If you can name the causes of a gap, you stop escalating them as defects.

Two rails, not one: AdServices vs AdAttributionKit

People say "Apple Search Ads attribution" as if it's one system. It's two Apple-owned mechanisms, and conflating them is where the confusion starts.

The first is the AdServices attribution API. It's a first-party token your app requests at open. Apple hands back campaign, ad-group, and keyword-level data for every download, regardless of the user's App Tracking Transparency choice. This is the rail your MMP has been ingesting for years.

The second is AdAttributionKit. Per Apple's own documentation, Apple Ads registered with AdAttributionKit on April 10, 2025 "to provide a more comprehensive picture for its developer customers." It's positioned as the official last-click across all registered ad platforms, Apple's answer to cross-network winner selection.

Two rails, two purposes. AdServices tells you which Apple keyword drove a download. AdAttributionKit adjudicates whether Apple or another platform gets the last-click credit. When you read a discrepancy, know which rail produced the number in front of you. Most console-vs-MMP gaps live on the AdServices rail.

What actually moves at app-open

Here's the plumbing, in words.

Your app launches. Your SDK, or your own code, calls the AdServices API and requests an attribution token. That token goes to Apple. Apple returns an attribution payload with the campaign ID, ad group ID, keyword ID, and a couple of date fields. Your MMP ingests that payload, matches it to the device's first-open, and writes an install row.

Now the detail that breaks reconciliations. Apple's documentation describes two payload forms, and the only difference between them is the clickDate or impressionDate field, "which indicates the date that the ad was clicked on or viewed." That field is returned solely when the user grants ATT permission.

Read that again. An ATT opt-out install arrives with no engagement timestamp. The detailed payload, from opt-in users, tells you when the tap happened. The standard payload, from opt-out users, does not.

The downstream consequence isn't cosmetic. If you don't know when the ad was tapped, you can't check whether that tap falls inside a 30-day window. Adjust states this plainly: "It is not possible for Adjust to discard standard response engagements that happen outside of the 30-day Apple attribution window." So the MMP defaults them in. Apple, working inside its own ecosystem with the timestamp it never exposed to you, may window them differently. Same install, two verdicts, neither wrong.

Why three dashboards can't agree

Every gap you'll ever see between these three dashboards maps to a discrete, nameable cause. There aren't a hundred of them. There are five that matter. Once you can label a delta, it stops being a mystery and becomes a line item.

Delta observed Cause Which dashboards it splits Deterministic or modeled Verdict — trust which
Console shows more installs than MMP Apple counts App Store download following a tap/view; MMP counts first-open Console vs MMP Deterministic Console for download volume; MMP for real net-new users
Console install count includes users the MMP calls returning Apple counts redownloads/reinstalls as installs; MMPs label these REINSTALLS or exclude them Console vs MMP Deterministic MMP for new-user acquisition; select the New Downloads column to align
Installs appear in console but drop from MMP paid Apple's fixed 30-day tap / 1-day view window vs MMP configurable cross-network windows Console vs MMP Deterministic Depends on window policy; document yours
Opt-out installs land in wrong window bucket Standard (ATT opt-out) payload has no clickDate, so the MMP can't window-filter and defaults them in Console vs MMP Modeled/defaulted Neither is fully correct; flag as blind spot
Same conversion claimed twice AdServices uses only Apple first-party data and is unaware of third-party clicks Console vs third-party network vs MMP Deterministic conflict MMP last-touch dedup resolves the winner

That last row deserves a callout. Apple's documentation is candid that "both the third-party network and AdServices could claim the conversion in that instance," because AdServices sees only Apple's first-party data and has no idea a Meta or TikTok click ever happened. Your MMP's cross-network deduplication exists precisely to break that tie. The console can't, because it doesn't know the other click exists.

Worked reconciliation: 10,000 clicks, three numbers

Take a single Search Ads campaign. Ten thousand taps in a month. Here's how the same campaign produces three different install counts, and how you walk the gap down to zero unexplained causes, not zero delta.

Apple Ads console: 1,240 installs. This is App Store downloads following a tap or view, per Apple's install definition. Redownloads are baked in. Say 180 of those 1,240 are lapsed users who'd deleted the app and reinstalled. Apple counts all 1,240.

App Analytics: ~1,240 downloads, split into First-Time Downloads and Redownloads. Same underlying event, but Apple lets you see the split here: roughly 1,060 first-time and 180 redownloads. This is your Rosetta stone for the next number.

MMP: 1,060 installs. The MMP counts first-open of net-new devices. Its 180 redownloads sit in a REINSTALLS bucket, exactly as Branch describes: "Apple Ads' dashboard shows reinstalls as conversions in its default view, but Branch calls these installs REINSTALLS." So the raw console-vs-MMP gap is 180 on 1,240, about 1,450 basis points. Looks alarming. It isn't.

Walk it down. Row two of the taxonomy, redownloads, explains 180 of those, roughly 1,450 bps, fully accounted for by selecting the New Downloads column. That closes almost the entire gap on paper.

Now the residual. Suppose 40 of the MMP's 1,060 came in as ATT opt-out standard payloads with no clickDate, and 12 of those had taps just outside 30 days that the MMP defaulted in but Apple's internal windowing excluded. That's a ~113 bps residual you cannot resolve deterministically. It maps to taxonomy row four. You don't fix it. You label it and move on.

Final state: a 1,450 bps headline gap decomposed into one 1,450 bps definitional cause, which is closeable, and one ~113 bps window-and-timestamp cause, which isn't closeable but is at least named. Zero of it is fraud. If your fraud team is still curious about what real injection looks like, our click-injection taxonomy draws the line between a definitional gap and an actual attack.

Which number goes to whom

Stop routing all three dashboards to everyone. Route by job.

The console goes to finance and UA leads. It owns spend and paid campaign performance. Cost-per-tap, keyword bids, spend pacing: the console is authoritative because it's the system that actually charged the card. No MMP re-derives your Apple spend more accurately than Apple.

Your MMP goes to growth. It owns post-install value and cross-network deduplicated last-touch. When you need to know which network truly earned the install among competing claims, or what a cohort's day-30 revenue looks like, that's MMP territory. The console can't see the competing Meta click. The MMP can.

App Analytics is your sanity check. Use it to confirm the paid-vs-organic split and to read the First-Time vs Redownload breakdown that reconciles the other two. It's the referee, not a player.

The line I make every team repeat: never expect install-for-install reconciliation. If you're re-evaluating whether you even need an MMP in this setup, the SKAN-plus-warehouse tradeoff is worth reading before you cut the contract. And if you're mid-switch, the migration playbook covers keeping AdServices ingestion alive across the gap.

What you genuinely can't measure anymore

Here's the honest part.

Opt-out installs arrive without a click timestamp. The standard payload strips clickDate, so deterministic window enforcement on those installs is impossible on your side. You can't discard an out-of-window engagement you can't date, and Adjust says so directly.

So a slice of your Apple attribution is modeled-by-default, not measured. Small, usually. Real, always.

You can't close it with a better SDK. A warehouse pipeline won't close it either. The timestamp isn't withheld by your tooling; it's withheld by the OS on opt-out. The mature move is to size the slice and disclose it, not to pretend it's deterministic. If ATT consent is where this bites you, our mobile consent management guide covers raising opt-in rates, which is the only lever that actually shrinks this blind spot.

Who ingests the AdServices payload

The AdServices attribution payload isn't MMP-exclusive. Both MMPs and product-and-marketing analytics platforms consume it; among the latter is Kixo, which handles mobile attribution and deep links alongside product analytics. Whatever ingests it, the token flow and the opt-out timestamp gap stay the same. The payload doesn't change based on who reads it.

Takeaways

The core split is definitional: Apple counts an App Store download (Apple's own docs: "App Store verified install (download) following a tap or a view"), your MMP counts first-open. That single difference, plus redownloads, explains most of the gap before you look at anything else.

Treat disagreement as a taxonomy, not a defect. Five named causes cover nearly every delta: download-vs-first-open, redownloads, window mismatch, opt-out timestamp loss, and double-claim across networks. Label the cause, quantify it in basis points, and only escalate what's genuinely unexplained.

Route by job and never chase install-for-install parity. Console for spend and paid performance, MMP for post-install value and deduplicated last-touch, App Analytics as referee. The residual you can't close is the OS withholding a timestamp. Size it, disclose it, move on.