{"id":592,"date":"2026-03-24T17:00:00","date_gmt":"2026-03-24T22:00:00","guid":{"rendered":"https:\/\/tolinku.com\/blog\/?p=592"},"modified":"2026-03-07T04:45:30","modified_gmt":"2026-03-07T09:45:30","slug":"google-play-install-referrer","status":"publish","type":"post","link":"https:\/\/tolinku.com\/blog\/google-play-install-referrer\/","title":{"rendered":"Google Play Install Referrer for Deferred Deep Links"},"content":{"rendered":"\n<p>The Google Play Install Referrer API is Android&#39;s built-in mechanism for passing data through an app install. When a user clicks a link that redirects to the Play Store, the referrer string you append to the Play Store URL is recorded by Google and made available to your app on first launch.<\/p>\n\n\n\n<p>This is fundamentally different from how iOS works. Apple has no equivalent mechanism. On Android, the referrer gives you a deterministic, first-party signal for install attribution without relying on advertising IDs or <a href=\"https:\/\/tolinku.com\/blog\/fingerprinting-vs-deterministic-matching\/\">probabilistic fingerprinting<\/a>. Understanding how it works, where it can fail, and how to validate the data it returns makes your deferred deep linking implementation significantly more reliable.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How the Referrer Gets There<\/h2>\n\n\n\n<p>The flow starts with a redirect URL. When a user clicks a Tolinku deep link before your app is installed, Tolinku redirects them to the Play Store with the referrer parameter included:<\/p>\n\n\n\n<pre><code>https:\/\/play.google.com\/store\/apps\/details\n  ?id=com.yourcompany.yourapp\n  &amp;referrer=utm_source%3Dtolinku%26utm_medium%3Dlink%26tolk_click_id%3Dabc123\n<\/code><\/pre>\n\n\n\n<p>The <code>referrer<\/code> value is URL-encoded. In decoded form it is:<\/p>\n\n\n\n<pre><code>utm_source=tolinku&amp;utm_medium=link&amp;tolk_click_id=abc123\n<\/code><\/pre>\n\n\n\n<p>Google Play records this string server-side when the user arrives at the listing. If the user installs the app, Google associates the referrer with that install. On first launch, your app can query the Install Referrer API to retrieve it.<\/p>\n\n\n\n<p>The official <a href=\"https:\/\/developer.android.com\/google\/play\/installreferrer\" rel=\"nofollow noopener\" target=\"_blank\">Play Install Referrer documentation<\/a> covers the complete API surface.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Setting Up InstallReferrerClient<\/h2>\n\n\n\n<p>Add the library to your <code>build.gradle.kts<\/code> (app module):<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">dependencies {\n    implementation(&quot;com.android.installreferrer:installreferrer:2.2&quot;)\n}\n<\/code><\/pre>\n\n\n\n<p>The library handles the connection to the Play Store service. The basic setup:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">import com.android.installreferrer.api.InstallReferrerClient\nimport com.android.installreferrer.api.InstallReferrerStateListener\nimport com.android.installreferrer.api.ReferrerDetails\n\nclass ReferrerManager(private val context: Context) {\n\n    private var client: InstallReferrerClient? = null\n\n    fun fetchReferrer(callback: (ReferrerDetails?) -&gt; Unit) {\n        client = InstallReferrerClient.newBuilder(context).build()\n\n        client?.startConnection(object : InstallReferrerStateListener {\n\n            override fun onInstallReferrerSetupFinished(responseCode: Int) {\n                when (responseCode) {\n                    InstallReferrerClient.InstallReferrerResponse.OK -&gt; {\n                        val details = try {\n                            client?.installReferrer\n                        } catch (e: RemoteException) {\n                            null\n                        }\n                        callback(details)\n                        client?.endConnection()\n                    }\n                    InstallReferrerClient.InstallReferrerResponse.FEATURE_NOT_SUPPORTED -&gt; {\n                        \/\/ Play Store does not support this API on this device\n                        callback(null)\n                    }\n                    InstallReferrerClient.InstallReferrerResponse.SERVICE_UNAVAILABLE -&gt; {\n                        \/\/ Play Store service is not available\n                        callback(null)\n                    }\n                }\n            }\n\n            override fun onInstallReferrerServiceDisconnected() {\n                \/\/ Service disconnected; connection will be retried automatically\n                \/\/ in most cases. Log this for debugging.\n            }\n        })\n    }\n}\n<\/code><\/pre>\n\n\n\n<p>Always call <code>endConnection()<\/code> after retrieving the data. Leaving the connection open consumes resources and may interfere with subsequent queries.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Parsing the Referrer String<\/h2>\n\n\n\n<p>The <code>installReferrer<\/code> property on <code>ReferrerDetails<\/code> returns the raw, URL-encoded referrer string. Parse it using Android&#39;s <code>Uri<\/code> class:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">import android.net.Uri\n\nfun parseReferrer(rawReferrer: String): Map&lt;String, String&gt; {\n    val uri = Uri.parse(&quot;https:\/\/example.com\/?$rawReferrer&quot;)\n    val params = mutableMapOf&lt;String, String&gt;()\n    uri.queryParameterNames.forEach { key -&gt;\n        uri.getQueryParameter(key)?.let { value -&gt;\n            params[key] = value\n        }\n    }\n    return params\n}\n<\/code><\/pre>\n\n\n\n<p>For a referrer of <code>utm_source=tolinku&amp;utm_medium=link&amp;tolk_click_id=abc123&amp;utm_campaign=spring<\/code>, this returns:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">mapOf(\n    &quot;utm_source&quot; to &quot;tolinku&quot;,\n    &quot;utm_medium&quot; to &quot;link&quot;,\n    &quot;tolk_click_id&quot; to &quot;abc123&quot;,\n    &quot;utm_campaign&quot; to &quot;spring&quot;\n)\n<\/code><\/pre>\n\n\n\n<p>Extract the Tolinku click ID (<code>tolk_click_id<\/code>) and send it to the attribution server to retrieve the original deep link data.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">UTM Parameters<\/h2>\n\n\n\n<p>Google Analytics and most analytics platforms recognize the standard <a href=\"https:\/\/support.google.com\/analytics\/answer\/1033863\" rel=\"nofollow noopener\" target=\"_blank\">UTM parameters<\/a>: <code>utm_source<\/code>, <code>utm_medium<\/code>, <code>utm_campaign<\/code>, <code>utm_content<\/code>, and <code>utm_term<\/code>. Tolinku populates these automatically when you create links with campaign parameters.<\/p>\n\n\n\n<p>Beyond UTM parameters, you can include any custom parameters in the referrer. Keep in mind the 4096-character length limit for the referrer string. In practice, standard campaign parameters plus a click ID are well within this limit.<\/p>\n\n\n\n<p>A full referrer string from a Tolinku link might look like:<\/p>\n\n\n\n<pre><code>utm_source=facebook_ads\n&amp;utm_medium=cpc\n&amp;utm_campaign=q1_retargeting\n&amp;utm_content=video_ad_v2\n&amp;tolk_click_id=clk_9f2a3b4c5d\n&amp;tolk_link_id=lnk_referral_spring\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Timing: When is the Referrer Available?<\/h2>\n\n\n\n<p>The referrer is available immediately on first launch in most cases, but there is a subtlety. The Play Install Referrer API connects to the Play Store&#39;s service process via IPC. If the Play Store app is updating or has not fully started, the connection may take a moment.<\/p>\n\n\n\n<p>The <code>onInstallReferrerSetupFinished<\/code> callback fires asynchronously. Do not block the main thread waiting for it. Your app should show a loading state or a default screen while the referrer query resolves, then route the user based on the result.<\/p>\n\n\n\n<p>Implement a timeout to avoid leaving users on a loading screen indefinitely if the service is slow:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">class ReferrerManagerWithTimeout(private val context: Context) {\n\n    fun fetchReferrer(\n        timeoutMs: Long = 3000,\n        callback: (ReferrerDetails?) -&gt; Unit\n    ) {\n        val handler = Handler(Looper.getMainLooper())\n        var called = false\n\n        val timeoutRunnable = Runnable {\n            if (!called) {\n                called = true\n                callback(null)\n            }\n        }\n\n        handler.postDelayed(timeoutRunnable, timeoutMs)\n\n        val manager = ReferrerManager(context)\n        manager.fetchReferrer { details -&gt;\n            handler.removeCallbacks(timeoutRunnable)\n            if (!called) {\n                called = true\n                callback(details)\n            }\n        }\n    }\n}\n<\/code><\/pre>\n\n\n\n<p>Three seconds is a reasonable default. Adjust based on your app&#39;s startup time constraints.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Additional Fields on ReferrerDetails<\/h2>\n\n\n\n<p><code>ReferrerDetails<\/code> provides more than just the referrer string:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">val details: ReferrerDetails = client.installReferrer\n\nval referrerString = details.installReferrer         \/\/ The URL-encoded referrer\nval clickTimestamp = details.referrerClickTimestampSeconds   \/\/ Unix timestamp of click\nval installTimestamp = details.installBeginTimestampSeconds  \/\/ Unix timestamp of install begin\nval googlePlayInstant = details.googlePlayInstantParam       \/\/ Whether opened via Instant App\n<\/code><\/pre>\n\n\n\n<p>The click and install timestamps are useful for server-side validation. If the gap between click and install exceeds your expected match window (for example, 30 days), you may want to treat the attribution as suspect and fall back to organic. Understanding the full <a href=\"https:\/\/tolinku.com\/blog\/install-attribution-flow\/\">install attribution flow<\/a> helps contextualize how these timestamps fit into the broader matching process.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Server-Side Validation<\/h2>\n\n\n\n<p>Never trust the referrer string without server-side verification. A user can modify the referrer string before it reaches your app by sideloading a modified APK or by other means.<\/p>\n\n\n\n<p>The validation flow:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Your app receives the referrer and extracts the <code>tolk_click_id<\/code>.<\/li>\n<li>Your app sends the click ID to Tolinku&#39;s attribution API.<\/li>\n<li>Tolinku&#39;s server looks up the click record by that ID, checks that the click is recent and not already attributed to another install, and returns the verified deep link data.<\/li>\n<li>Your app uses the returned data for routing.<\/li>\n<\/ol>\n\n\n\n<p>If the click ID is not found, has already been used, or was created outside the valid attribution window, the server returns an error and the install is treated as organic. This prevents click ID stuffing and referrer manipulation.<\/p>\n\n\n\n<p>Tolinku&#39;s <a href=\"https:\/\/tolinku.com\/docs\/developer\/sdks\/android\/\">Android SDK<\/a> handles this validation automatically when you use <code>resolveDeferred()<\/code>. The SDK fetches the referrer, extracts the click ID, validates it server-side, and returns the verified deep link data in a single call.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Reliability Considerations<\/h2>\n\n\n\n<p>The Play Install Referrer API is reliable in typical conditions, but several factors can affect it:<\/p>\n\n\n\n<p><strong>Non-Play Store installs.<\/strong> The referrer API only works for apps installed through Google Play. Sideloaded APKs, manufacturer pre-installs, and enterprise MDM deployments do not go through the Play Store and will not have a referrer.<\/p>\n\n\n\n<p><strong>Browser referrer stripping.<\/strong> Some Android browsers strip parameters from the Play Store redirect URL. This is more common with certain in-app browsers (some social media apps open links in an internal WebView that modifies outgoing URLs). The Tolinku link server handles some of this with redirect chain normalization, but it cannot overcome all browser-level stripping.<\/p>\n\n\n\n<p><strong>Device compatibility.<\/strong> The API requires Google Play Services. Devices that ship without Google Play (Huawei phones post-2019, Amazon Fire tablets, some Chinese OEM devices) do not support the API. Fall back to fingerprinting for these devices.<\/p>\n\n\n\n<p><strong>Referrer not found.<\/strong> If a user installs through a Play Store search (not through a link), the <code>installReferrer<\/code> value will be <code>&quot;organic&quot;<\/code> or an empty string. Handle this case and treat it as an organic install.<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">val rawReferrer = details.installReferrer\nif (rawReferrer.isNullOrBlank() || rawReferrer == &quot;organic&quot;) {\n    \/\/ Organic install, no deferred link to resolve\n    showDefaultOnboarding()\n    return\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Testing<\/h2>\n\n\n\n<p>Use ADB to simulate a referrer without going through the full Play Store install flow:<\/p>\n\n\n\n<pre><code class=\"language-bash\">adb shell am broadcast \\\n  -a com.android.vending.INSTALL_REFERRER \\\n  -n com.yourcompany.yourapp\/.ReferrerReceiver \\\n  --es &quot;referrer&quot; &quot;utm_source=test&amp;tolk_click_id=test_click_001&quot;\n<\/code><\/pre>\n\n\n\n<p>This approach works for development testing. For full integration testing, use the <a href=\"https:\/\/developer.android.com\/google\/play\/installreferrer\/igetinstallreferrerservice\" rel=\"nofollow noopener\" target=\"_blank\">Play Store&#39;s referrer testing tool<\/a> or test with real installs via internal testing tracks.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Putting It All Together<\/h2>\n\n\n\n<p>The Play Install Referrer API gives Android deferred deep linking a reliability advantage over iOS. Because the referrer travels through Google&#39;s infrastructure, it is not subject to the match-window uncertainty of fingerprinting or the permission walls of advertising IDs.<\/p>\n\n\n\n<p>For the full picture of how the referrer fits into the broader attribution flow, see <a href=\"https:\/\/tolinku.com\/blog\/install-attribution-flow\/\">Install Attribution Flow: From Ad Click to First Open<\/a>. For the complete Android SDK integration, see the <a href=\"https:\/\/tolinku.com\/blog\/deferred-deep-linking-android\/\">Android implementation guide<\/a> and the <a href=\"https:\/\/tolinku.com\/docs\/developer\/sdks\/android\/\">SDK documentation<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Google Play Install Referrer API is the most reliable way to implement deferred deep linking on Android. This guide covers InstallReferrerClient setup, referrer string parsing, UTM parameters, server-side validation, and reliability edge cases.<\/p>\n","protected":false},"author":2,"featured_media":591,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"Google Play Install Referrer for Deferred Deep Links","rank_math_description":"Learn how to use the Google Play Install Referrer API for deferred deep linking on Android. Covers setup, referrer parsing, UTM parameters, and validation.","rank_math_focus_keyword":"Google Play install referrer","rank_math_canonical_url":"","rank_math_facebook_title":"","rank_math_facebook_description":"","rank_math_facebook_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-google-play-install-referrer.png","rank_math_facebook_image_id":"","rank_math_twitter_title":"","rank_math_twitter_description":"","rank_math_twitter_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-google-play-install-referrer.png","footnotes":""},"categories":[11],"tags":[25,28,21,111,112,50],"class_list":["post-592","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-deep-linking","tag-android","tag-attribution","tag-deferred-deep-linking","tag-google-play","tag-install-referrer","tag-utm-parameters"],"_links":{"self":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/592","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/comments?post=592"}],"version-history":[{"count":2,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/592\/revisions"}],"predecessor-version":[{"id":2800,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/592\/revisions\/2800"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media\/591"}],"wp:attachment":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media?parent=592"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/categories?post=592"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/tags?post=592"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}