Guide

Your ad platform and the app store report different install counts

Open Ads Manager and the store console for the same week and you get two install counts that do not match, usually by a lot. The instinct is to decide which one is lying, or to split the difference. Both moves are wrong for the same reason: these are not two samples of one quantity. They are two different quantities, measured by parties with different information, and a cost per install is only meaningful once you have said out loud which of them is under the division bar.

What each number actually counts

  • the ad platform's own reported count

    Installs the platform is claiming as its own work. On iOS a large share of these are not observed events at all — they are SKAdNetwork postbacks and modelled conversions, which is why the same campaign can move up and down after the fact. It answers how much of this did we cause, and it is the only one of the three that knows which campaign and creative to hang a result on.

  • the app store's own count

    First-time downloads as counted by App Store Connect or Play Console. Nobody is claiming credit and nothing is modelled — it is the count of the thing itself. What it cannot tell you is where any of them came from. App Store Connect ships the campaign dimension only in its detailed reports, under a privacy threshold that erases small daily numbers entirely.

  • an MMP's click attribution

    A third party's reconciliation of clicks to installs. Useful as a referee between platforms that each claim the same user, and still an attribution model rather than a count. Lift is a separate question, answered by holding a geography out, not by a better model.

Read those three descriptions again and the arithmetic follows on its own. Adding them double-counts the same downloads. Averaging them produces a number that no party measured and that nothing in the world corresponds to. The only defensible move is to pick one, name it, and keep the others visible so the reader can see what was not used.

Worked examples

Invented inputs, real engine. Each block below is produced by handing a dataset to the same function the product runs and printing what it chose, so the wording here changes if the rule ever changes. The three cases are: both sources present and agreeing on which days they cover; the store total carrying no channel labels; and neither source covering every day that had spend.

Both connected, the store feed covers the window

12 days of spend · the platform claims 9/day · the store counted 20/day

Spend in the window$2,400
Divisor the engine chose240 installs, from the app store's own count
Divisor for the per-channel rows240 installs, from the app store's own count · reads $10 per install
Held, not added108 installs from the ad platform's own reported count — the same spend over that divisor would have read $22 per install

The install denominator is the app store's own count

CPI = spend / installs, and these 240 installs come from the app store's own count. We also hold the ad platform's own reported count (108) for this window and do not add them in: they are different measurements of the same thing, not extra samples.

The store total carries no channel, the platform's number does

12 days of spend · the store's count arrives unlabelled, as it does from App Store Connect

Spend in the window$2,400
Divisor the engine chose240 installs, from the app store's own count
Divisor for the per-channel rows108 installs, from the ad platform's own reported count · reads $22 per install
Held, not added108 installs from the ad platform's own reported count — the same spend over that divisor would have read $22 per install

The install denominator is the app store's own count

CPI = spend / installs, and these 240 installs come from the app store's own count. We also hold the ad platform's own reported count (108) for this window and do not add them in: they are different measurements of the same thing, not extra samples.

55% of installs are unattributed — per-channel numbers use a different divisor

The total of 240 installs comes from the app store's own count, which does not say which channel they came from. The per-channel, per-campaign and per-creative rows below are therefore built from the ad platform's own reported count, which accounts for 108 of them. The remaining 132 (55%) are unattributed: they happened, and no channel is claiming them. Do not read that gap as organic, and do not add the two divisors together. Per-channel CPI is a comparison between channels, not a share of the authoritative total.

Neither source has a number for every day you spent money

12 days of spend · the store feed covers the most recent 4 days, the platform's export covers the rest

Spend in the window$2,400
Divisor the engine chose80 installs, from the app store's own count
Divisor for the per-channel rows80 installs, from the app store's own count · reads $30 per install
Held, not added72 installs from the ad platform's own reported count — the same spend over that divisor would have read $33 per install

The install denominator does not cover every day that had spend

8 day(s) in this window had ad spend and install data, but none from the source we are using as the denominator. Spend is summed over the whole window while installs are counted only on the covered days, so CPI here is too HIGH, not too low. Finish the import for the missing days, or read a window that the source covers.

The install denominator is the app store's own count

CPI = spend / installs, and these 80 installs come from the app store's own count. We also hold the ad platform's own reported count (72) for this window and do not add them in: they are different measurements of the same thing, not extra samples.

The gap between the two divisors is not organic

The second example is the normal state of an iOS account, not an edge case. The store knows the total and not the source; the platforms know their own claims and not the total. So the headline number and the per-channel table are computed from different divisors, and the difference between them is a pile of installs that happened with nobody claiming them.

That pile gets labelled organic in most reporting, and the label is a guess wearing a category's clothes. It contains genuine organic discovery, and it also contains paid installs whose attribution window expired, installs from campaigns suppressed under a privacy threshold, and anything a modelled conversion failed to catch. Subtracting paid from total does not isolate organic; it just moves everything you cannot see into a column with a confident name. Reporting it as unattributed is not a weaker answer — it is the only one the data supports.

A half-imported store feed makes your CPI look worse, not better

The third example is the one that catches people, because the error runs in the direction nobody expects. Connect a store feed and the intuition is that the divisor gets bigger and the cost per install drops. But a feed that has only backfilled part of your window contributes installs for the days it covers while the numerator keeps summing spend across every day. Cost per install goes up. Nothing errors, nothing is empty, and the campaign you are about to pause looks expensive for a reason that lives entirely in your import job.

This is why coverage is checked before precedence here: a more authoritative source that does not span the days you spent on is not the better divisor, it is a broken one. When no source spans them, the honest output is the number plus the fact that it is inflated and by how many days — which is what the block above prints.

What to do on Monday

  1. Write down, for the report you actually circulate, which of the three counts is in the denominator. If nobody can answer that in one sentence, the cost per install in it means nothing yet.
  2. Check that whatever you named has a number for every day the window charged you for. Partial coverage is the most common way a divisor lies, and it lies upward.
  3. Keep the totals row and the per-channel rows visibly on different divisors. Per-channel cost per install is a comparison between channels; it is not a share of the authoritative total, and the two will not reconcile.
  4. Report the remainder as unattributed rather than as organic, and treat any decision that depends on calling it organic as a decision you cannot make with this data.

Start from the export you already have

Our CSV check reads an Ads Manager export in the browser you already have open and sends it nowhere — no sign-up, no account connection, no email address. It will tell you which of the columns above your export actually carries. If you are unsure which columns to ask for in the first place, that is the column-by-column guide; and once the divisor is settled, how much of it you need before the number can be read at all is the sufficiency guide.

Or run it on connected accounts instead of an export — start a free trial. No credit card to begin.