{"id":1273,"date":"2026-05-31T13:00:00","date_gmt":"2026-05-31T18:00:00","guid":{"rendered":"https:\/\/tolinku.com\/blog\/?p=1273"},"modified":"2026-03-07T03:49:03","modified_gmt":"2026-03-07T08:49:03","slug":"android-link-verification-delays","status":"publish","type":"post","link":"https:\/\/tolinku.com\/blog\/android-link-verification-delays\/","title":{"rendered":"Android Link Verification Delays and How to Handle Them"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">You&#39;ve set up your <code>assetlinks.json<\/code>, configured your intent filters, and published your app. A user installs it. They tap a link to your domain. Instead of your app opening directly, they see the disambiguation dialog: &quot;Open with Chrome or YourApp?&quot;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The App Links verification hasn&#39;t completed yet. It can take anywhere from seconds to hours after installation, depending on network conditions, server response times, and the Android version. During this window, your App Links behave like unverified deep links.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This guide covers why verification delays happen, what the user experience looks like during the delay, and strategies to minimize the impact. For the verification process itself, see <a href=\"https:\/\/tolinku.com\/blog\/android-app-link-verification-process\/\">how Android App Link verification works<\/a>. For debugging verification failures, see the <a href=\"https:\/\/tolinku.com\/blog\/debugging-android-intent-resolution\/\">debugging intent resolution guide<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why Verification Takes Time<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">The Verification Flow<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When an app is installed (or updated), Android&#39;s verification service:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Parses the app&#39;s manifest for intent filters with <code>android:autoVerify=&quot;true&quot;<\/code>.<\/li>\n<li>Extracts all declared <code>android:host<\/code> values.<\/li>\n<li>Makes HTTPS requests to <code>https:\/\/{host}\/.well-known\/assetlinks.json<\/code> for each host.<\/li>\n<li>Validates the response: correct JSON structure, matching package name, matching SHA-256 fingerprint.<\/li>\n<li>Records the verification status for each domain.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Each of these steps can introduce delays.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Common Delay Causes<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Network conditions.<\/strong> The verification request is made by the Android system service, which may be queued behind other tasks. On slow or metered connections, the request may be deprioritized or time out.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Server response time.<\/strong> If your <code>assetlinks.json<\/code> takes more than 5 seconds to respond, Android may treat it as a failure. Slow CDNs, cold starts on serverless endpoints, or overloaded servers all contribute.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Batch processing.<\/strong> Android doesn&#39;t verify each app immediately on install. Verification requests are batched and processed periodically. On some devices, this batch runs every few minutes; on others, it may be deferred to a maintenance window.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Multiple domains.<\/strong> Each domain is verified with a separate HTTP request. If you have 5 domains in your intent filters, that&#39;s 5 requests. A timeout on any one can delay the entire verification.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Device state.<\/strong> If the device is in Doze mode, battery saver, or has restricted background data, verification requests may be deferred.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What Happens During the Delay<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Before Verification Completes<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Android 12+:<\/strong> Unverified App Links open in the browser by default. The user doesn&#39;t see a disambiguation dialog; the link just opens in Chrome. Your app is effectively invisible for those links.<\/li>\n<li><strong>Android 11 and below:<\/strong> Unverified links show the &quot;Open with&quot; disambiguation dialog. The user can choose your app or the browser.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">After Verification Completes<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Links open directly in your app with no dialog or browser involvement.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Verification Persistence<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Once verified, the status persists until:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The app is uninstalled and reinstalled.<\/li>\n<li>The app is updated (triggers re-verification on some Android versions).<\/li>\n<li>The user manually resets App Link preferences in settings.<\/li>\n<li>The system periodically re-verifies (Android 14+).<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Strategies for Handling Delays<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Strategy 1: Optimize Server Response Time<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The fastest fix. Make your <code>assetlinks.json<\/code> response as fast as possible:<\/p>\n\n\n\n<pre><code class=\"language-nginx\"># Nginx: serve assetlinks.json from memory with long cache\nlocation = \/.well-known\/assetlinks.json {\n    alias \/etc\/assetlinks\/assetlinks.json;\n    default_type application\/json;\n    add_header Cache-Control &quot;public, max-age=3600&quot;;\n\n    # Enable gzip for faster transfer\n    gzip on;\n    gzip_types application\/json;\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Target: under 200ms response time. The file is tiny (typically under 500 bytes), so the bottleneck is usually connection establishment, not transfer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>CDN caching:<\/strong> Serve the file through a CDN (Cloudflare, CloudFront) with edge caching enabled. This puts the file physically closer to the Android verification service.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>No redirects:<\/strong> The request to <code>\/.well-known\/assetlinks.json<\/code> must return a direct 200 response. Redirects (301, 302) may not be followed by the verification service on all Android versions.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Strategy 2: Use a Web Fallback That Redirects to the App<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For the critical first-open experience, don&#39;t rely solely on App Links. Use a web page that detects the app and redirects:<\/p>\n\n\n\n<pre><code class=\"language-html\">&lt;!-- Your landing page at links.yourapp.com\/welcome --&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta http-equiv=&quot;refresh&quot; content=&quot;2;url=https:\/\/play.google.com\/store\/apps\/details?id=com.example.app&quot;&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;script&gt;\n        \/\/ Try to open the app via intent URL\n        const intentUrl = &#39;intent:\/\/welcome#Intent;&#39; +\n            &#39;scheme=https;&#39; +\n            &#39;package=com.example.app;&#39; +\n            &#39;S.browser_fallback_url=&#39; + encodeURIComponent(window.location.href) + &#39;;&#39; +\n            &#39;end&#39;;\n\n        window.location.href = intentUrl;\n    &lt;\/script&gt;\n    &lt;p&gt;Opening app... &lt;a href=&quot;https:\/\/play.google.com\/store\/apps\/details?id=com.example.app&quot;&gt;\n        Download from Play Store\n    &lt;\/a&gt;&lt;\/p&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If verification hasn&#39;t completed and the link opens in the browser, the page attempts to open the app via an intent URL (which doesn&#39;t require verification). If the app isn&#39;t installed, it falls back to the Play Store.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Strategy 3: Use Tolinku Smart Links<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/tolinku.com\/features\/deep-linking\">Tolinku deep links<\/a> handle verification delays transparently. When a user taps a Tolinku link:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>If App Links verification is complete, the app opens directly.<\/li>\n<li>If verification is pending, the Tolinku redirect chain detects the platform and attempts to open the app through alternative mechanisms (intent URLs, JavaScript redirect).<\/li>\n<li>If the app isn&#39;t installed, the user is sent to the app store or a web fallback.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">This three-layer approach ensures the link works regardless of verification status.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Strategy 4: Test Verification Before Relying on It<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In your app&#39;s first-run experience, check whether App Links are verified:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">\/\/ Check if App Links are verified for your domain\nfun checkAppLinkStatus(context: Context) {\n    if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.S) {\n        val manager = context.getSystemService(DomainVerificationManager::class.java)\n        val userState = manager.getDomainVerificationUserState(context.packageName)\n\n        val verifiedDomains = userState?.hostToStateMap\n            ?.filter { it.value == DomainVerificationUserState.DOMAIN_STATE_VERIFIED }\n            ?.keys ?: emptySet()\n\n        val unverifiedDomains = userState?.hostToStateMap\n            ?.filter { it.value != DomainVerificationUserState.DOMAIN_STATE_VERIFIED }\n            ?.keys ?: emptySet()\n\n        if (unverifiedDomains.isNotEmpty()) {\n            \/\/ Verification pending or failed for some domains\n            \/\/ Consider prompting user to set default app manually\n            Log.w(&quot;AppLinks&quot;, &quot;Unverified domains: $unverifiedDomains&quot;)\n        }\n    }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Strategy 5: Prompt Users to Set Default App<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">If verification fails or is delayed, you can guide users to manually approve your app as the default handler:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">\/\/ Android 12+: Open the app&#39;s default links settings\nfun promptDefaultApp(context: Context) {\n    if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.S) {\n        val intent = Intent(\n            Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS,\n            Uri.parse(&quot;package:${context.packageName}&quot;)\n        )\n        context.startActivity(intent)\n    }\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use this as a last resort. Most users won&#39;t navigate system settings, but power users and testers will appreciate the option.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Monitoring Verification in Production<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Server-Side Monitoring<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Monitor requests to your <code>assetlinks.json<\/code> endpoint:<\/p>\n\n\n\n<pre><code># Expected pattern: requests from Android verification service\nUser-Agent: Mozilla\/5.0 (Linux; Android *)\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Track:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Request volume:<\/strong> Spikes after app releases indicate re-verification.<\/li>\n<li><strong>Response codes:<\/strong> Any 4xx or 5xx responses mean failed verifications.<\/li>\n<li><strong>Response time:<\/strong> Responses over 5 seconds may timeout.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Client-Side Monitoring<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">On Android 12+, use the <code>DomainVerificationManager<\/code> API to check verification status and report it to your analytics:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">fun reportVerificationStatus(context: Context) {\n    if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.S) {\n        val manager = context.getSystemService(DomainVerificationManager::class.java)\n        val userState = manager.getDomainVerificationUserState(context.packageName)\n\n        userState?.hostToStateMap?.forEach { (domain, state) -&gt;\n            val statusName = when (state) {\n                DomainVerificationUserState.DOMAIN_STATE_VERIFIED -&gt; &quot;verified&quot;\n                DomainVerificationUserState.DOMAIN_STATE_SELECTED -&gt; &quot;selected&quot;\n                DomainVerificationUserState.DOMAIN_STATE_NONE -&gt; &quot;none&quot;\n                else -&gt; &quot;unknown&quot;\n            }\n            analytics.trackEvent(&quot;app_link_status&quot;, mapOf(\n                &quot;domain&quot; to domain,\n                &quot;status&quot; to statusName\n            ))\n        }\n    }\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This gives you visibility into what percentage of your user base has verified App Links.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Timing Expectations<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Scenario<\/th>\n<th>Typical Verification Time<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td>Fresh install, good network<\/td>\n<td>10-60 seconds<\/td>\n<\/tr>\n<tr>\n<td>Fresh install, slow network<\/td>\n<td>1-5 minutes<\/td>\n<\/tr>\n<tr>\n<td>Fresh install, server timeout<\/td>\n<td>Never (fails)<\/td>\n<\/tr>\n<tr>\n<td>App update<\/td>\n<td>10-60 seconds (re-verification)<\/td>\n<\/tr>\n<tr>\n<td>Device reboot<\/td>\n<td>May re-verify within minutes<\/td>\n<\/tr>\n<tr>\n<td>Android 14 periodic re-check<\/td>\n<td>Every few days<\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The bottom line: don&#39;t assume verification is instant. Design your link flows to work even when verification hasn&#39;t completed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For the full App Links setup, see the <a href=\"https:\/\/tolinku.com\/blog\/android-app-links-complete-guide\/\">Android App Links complete guide<\/a>. For <a href=\"https:\/\/tolinku.com\/docs\/developer\/app-links\/\">Tolinku&#39;s<\/a> approach to handling verification across domains, the platform manages asset links files with optimized caching and CDN delivery.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Deal with Android App Link verification delays. Understand why verification takes time, what happens while it&#8217;s pending, and strategies to ensure links work reliably.<\/p>\n","protected":false},"author":2,"featured_media":1272,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"Android Link Verification Delays and How to Handle Them","rank_math_description":"Deal with Android App Link verification delays. Understand why verification takes time and strategies to ensure links work while pending.","rank_math_focus_keyword":"android link verification delays","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-android-link-verification-delays.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-android-link-verification-delays.png","footnotes":""},"categories":[10],"tags":[25,23,20,35,69,87,91],"class_list":["post-1273","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-android","tag-android","tag-app-links","tag-deep-linking","tag-digital-asset-links","tag-mobile-development","tag-troubleshooting","tag-verification"],"_links":{"self":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1273","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=1273"}],"version-history":[{"count":3,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1273\/revisions"}],"predecessor-version":[{"id":2575,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1273\/revisions\/2575"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media\/1272"}],"wp:attachment":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media?parent=1273"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/categories?post=1273"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/tags?post=1273"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}