Google Play Install Referrer: The Signal That Still Works
A payments app I looked at last year was buying installs at what looked like a great CPI. Then I pulled the click-to-install times. A suspicious slice of "clicks" landed a second or two after the download had already started. That's not a user tapping an ad. That's a fraud SDK on the device watching for a new install and firing a click into the gap to steal the credit. The tell was sitting right there in the install referrer, in two timestamps most people never open. Reading them turned a chunk of the monthly invoice from "paid" into "refunded."
So here's the short answer. The Google Play Install Referrer is the one attribution signal on Android that Google Play hands you directly, deterministically, per install. It carries the campaign string from the click plus two timestamps, and because it comes from the Play Store rather than a broadcast any app can spoof, it survives as a trustworthy signal even as SKAdNetwork and the Privacy Sandbox chip away at everything else. If you ship an Android app and you care about where installs actually came from, this is the API you build on first.
What the referrer actually is
When someone clicks a tracked link and lands on your Play Store listing, Google Play stashes a small campaign string against that install. Your app reads it back once, on first launch, through the Play Install Referrer Library.
You get three things back. The Android Developers docs are blunt about it: the referrer URL, the referrer click timestamp, and the install-begin timestamp. That's the whole payload. Play version 8.3.73 and later has the API, which is effectively every live device, and the data stays available for 90 days or until the app is reinstalled.
The referrer URL is the interesting part. It's whatever campaign string rode along on the click. For a plain UTM-tagged link that's a query string you'll recognize instantly:
utm_source=facebook&utm_medium=cpc&utm_campaign=summer_sale&utm_content=video_a
For traffic routed through a measurement partner it's usually an opaque token the MMP knows how to decode into network, campaign, ad set and click ID. Singular's reference notes the same thing: the referrer is either your utm parameters or an MMP-encoded value, and you should call the API exactly once on the first run after install. Call it more and you're just burning battery.
The fields, plainly
Here's what each piece is and how much you can trust it.
| Field | What it is | Set by | Trust it? |
|---|---|---|---|
installReferrer |
Campaign string from the click (utm params or MMP token) | Whoever built the link | Yes, but sanitize it |
referrerClickTimestampSeconds |
When the ad click happened, in seconds | Google Play | High |
installBeginTimestampSeconds |
When the download started, in seconds | Google Play | High |
referrerClickTimestampServerSeconds |
Server-side click time (Play-attested) | Google Play | Highest |
installBeginTimestampServerSeconds |
Server-side install-begin time | Google Play | Highest |
googlePlayInstantParam |
Whether the user came through an instant experience | Google Play | Informational |
The two client-side timestamps are what most tutorials show. The server-side pair matters more for fraud work, because they're attested by Play rather than derived on a device you don't control. If you only wire up the client timestamps, you're leaving your strongest evidence on the table.
Parsing the string without getting burned
Mechanically the read is small. You build an InstallReferrerClient, call startConnection(), and because that call is asynchronous you wait for the onInstallReferrerSetupFinished callback before touching anything. On OK you pull the ReferrerDetails, grab the string, and disconnect.
Two things bite people here.
First, the referrer string is URL-encoded. utm_campaign=summer%20sale needs decoding before summer sale is readable, and if you're pattern-matching campaign names against a decoded value you'll silently miss half of them. Decode once, then parse.
Second, treat the string as untrusted input. It's user-supplied in the sense that anyone building a link controls it. I've watched a raw referrer value get written straight into an analytics event and then into a SQL string. Don't do that. Cap the length, allowlist the parameter names you expect, and drop the rest. A referrer field is a lovely place to smuggle an injection payload precisely because everyone assumes Google sanitized it. Google did not.
Honestly, the official docs don't spell out how aggressive to be with sanitization, so testing is your friend. Feed it a deliberately nasty string in a debug build and see what your pipeline does with it.
The two timestamps are the fraud detector
This is the part people skip, and it's the part I care about most.
Subtract the click timestamp from the install-begin timestamp. That difference is the click-to-install time, CTIT, and it's the single cheapest fraud signal on Android. Kochava's assessment of the API lays out the logic cleanly: fraud detection works by comparing an engagement's time to the install-begin timestamp, and if the click arrives after the install already began, it's almost certainly click injection.
Think about the physics for a second. A real person sees an ad, taps it, then downloads and opens your app. Click first, always. The gap is usually minutes, sometimes hours. When the gap is a fraction of a second, or worse, negative, no human did that. A fraud SDK on the device detected the install broadcast and injected a click to claim an organic user it never touched.
The other side of the same coin is click spam, where the CTIT is implausibly long, days of it, because the "click" was fired blind across thousands of devices hoping one of them installs later. Both patterns fall out of the same two numbers.
In basis points, this is where the money is. On the payments app I mentioned, injected installs were running around 300 basis points of paid volume. Small as a percentage. Not small as a monthly refund when the CPI is in the single-digit dollars and the install count has six figures. The referrer timestamps were the evidence that got that money back.
The encrypted referrer, and why it exists
One wrinkle you'll hit with certain networks. For Meta campaigns the campaign metadata isn't sitting in plaintext utm params. Branch's documentation on the Meta install referrer describes it: the payload is appended in an encrypted format and you need a Meta-provided decryption key, tied to your app, to read it. Under the hood it's AES-256-GCM, which is why you can't just eyeball the string and read off the campaign.
Two practical notes. You store that key server-side and decrypt server-side; shipping a decryption key inside your APK is handing it to anyone with apktool. And the encryption is about the advertiser's data, not yours, so the flow is deliberately opinionated about where the plaintext is allowed to exist. If your MMP handles Meta for you, this is invisible. If you're rolling attribution yourself, budget a day for the key exchange and the decrypt path.
Why it survives when SKAN and ARA don't
Here's the strategic reason this old, boring API deserves a spot at the center of your Android measurement.
Deterministic, device-level attribution is genuinely disappearing everywhere else. On iOS, SKAdNetwork gives you aggregated, delayed, privacy-thresholded postbacks and not much else, and adoption is still thin. AdAction's 2025 breakdown puts industry SKAN adoption around 30%, with SKAN4 in the single digits years after launch. The postbacks scatter across individual networks instead of landing in one place, and down-funnel metrics like ROAS get crushed by the aggregation. Android's Privacy Sandbox and its Attribution Reporting API are heading toward the same aggregated, privacy-preserving shape, with the same expected signal loss versus the device-level precision teams grew up on.
The install referrer is a different animal. It's first-party by construction: Google Play is telling your app where its own install came from. There's no cross-app identifier, no probabilistic fingerprint, nothing for a privacy framework to deprecate. It's per-install, deterministic, and delivered from the Play Store rather than a spoofable broadcast. That's exactly why it keeps working while the modeled and aggregated approaches wobble.
I'm not claiming it does everything. It won't give you a user-level graph across networks, it won't handle iOS, and it says nothing about what the user does after install. But for the specific question "which campaign produced this Android install, and can I trust it," nothing else on the platform answers as cleanly. Most attribution SDKs, Kixo's Android SDK included, read the Play Install Referrer to power deferred deep links and mobile attribution, precisely because it's the one deterministic anchor left.
If you're deciding where to spend engineering time on Android measurement, spend it here before you spend it modeling the gaps SKAN leaves behind.
A few questions people actually ask
Do I need an MMP to use the install referrer? No. The API is free and native to the Play Store. An MMP mostly saves you the decode logic, the Meta key dance, and the CTIT fraud rules. Plenty of teams read the referrer directly and do fine.
How long do I have to read it? The value persists for 90 days or until reinstall, per Google's docs. But you should read it on the very first launch and cache the result, not lazily weeks later.
Why is my referrer string empty? Usually an organic install with no campaign attached, or a sideload that never touched the Play Store. Empty is a valid, common answer, not a bug.
Can the timestamps be faked? The client timestamps live on a device you don't control, so treat them as evidence rather than gospel. The server-side pair is Play-attested and is what you should lean on when you're actually accusing a network of injection.
The install referrer isn't glamorous. It's a query string and two numbers. But those two numbers have clawed back real refund dollars for teams I've worked with, and the signal keeps working while flashier frameworks degrade. On Android, that makes it the first thing I'd wire up and the last thing I'd rip out.