getInstalledRelatedApps: What Sites Can Learn About Your Installed Apps

12 min read

navigator.getInstalledRelatedApps() lets a website ask the browser whether that website's own app is installed: its Android app, its Windows Store app, or its own progressive web app. It was built so a site can stop asking you to install an app you already have. It is also a small piece of information about your machine that a site can read without a prompt.

Most fingerprinting checklists skip it, and its reach is narrow. But if you keep separate browser profiles for privacy, client work or QA, it is worth knowing which of its answers stay inside a profile and which come from the machine underneath.

Check your own fingerprint first. We built a public checker at justbrowser.app/fingerprint-checker, client-side with no signup. It compares the WebRTC candidates against your public IP, and it checks whether the IP's location has a UTC offset that agrees with your browser's timezone. Worth running before you read the rest of this, so the signals below have your own values attached.

What We're Building

By the end of this tutorial, you'll understand:

  1. How getInstalledRelatedApps works and what it exposes
  2. Which answers every profile on a machine shares, and which stay with one profile
  3. How to test it on a site you control
  4. How to limit what it reveals
  5. What per-profile separation looks like in a multi-account browser

It takes about 20 minutes to work through, and it's worth it if you need client or test profiles kept apart from each other and from your own browsing.

Prerequisites

  • Basic familiarity with browser fingerprinting concepts
  • A Chromium-based browser (we'll use JustBrowser profiles in the examples, but the concepts apply broadly)
  • An HTTPS test site you control, where you can edit the web app manifest
  • Access to Chrome DevTools or similar

If you haven't worked through the basics of browser fingerprinting yet, the WebGL fingerprinting deep-dive gives good background. Not required, but helps.

Step 1: Understand What the API Exposes

The getInstalledRelatedApps() API was designed with good intentions. It lets a website check whether you've installed its native app or PWA, so it can stop showing you "Install our app!" banners when you already have it.

Here's the basic call:

const relatedApps = await navigator.getInstalledRelatedApps();
console.log(relatedApps);
// Returns installed related apps, e.g. [{ platform: "webapp", url: "https://example.com/manifest.json", id: "..." }]

How it actually works:

  1. A website lists its related apps in the related_applications member of its web manifest
  2. Each app confirms the relationship from its side: an Android app through Digital Asset Links, a Windows app through URI handlers, a PWA through its own manifest
  3. When a page calls the API, the browser reports which of those listed apps are installed
  4. Only the first three apps in the manifest are considered, and the call only works in a top-level HTTPS page, not inside an iframe

Those limits matter. A site can only ask about apps it has declared and that declare it back, and only three of them. A third-party script can't use the API to sweep your machine for other companies' apps: an embedded iframe can't call it at all, and the answers are always about the calling page's own apps.

So what does it reveal? Whether you have installed one particular company's app, and sometimes which version. That's small. It matters mainly because one part of the answer survives things you might expect to reset it.

Step 2: Know Where Each Answer Comes From

The API gives two kinds of answer, and they live in different places.

Native apps live on the operating system. An Android app or a Windows Store app is installed on the device, not inside a browser profile. If a site's Windows app is installed on your machine, that site can see it from any profile in a supporting browser on that machine, including a brand-new profile with no cookies.

PWAs live in a browser profile. In Chromium, installing a progressive web app adds it to the profile you installed it from. A check for a PWA reports whether it is installed in the profile making the call, and a call from outside the PWA's scope returns an empty array.

Support also varies by platform. Android has had these checks the longest; desktop Chrome and Edge gained the PWA check later. Chrome's documentation lists which app types each platform supports.

First, confirm the API exists in the browser you're testing. Open DevTools (F12), go to the Console tab and paste:

// Check if the API exists
if ('getInstalledRelatedApps' in navigator) {
  console.log('API available');
  // Note: this returns an empty array unless called from a page
  // whose manifest declares related_applications
} else {
  console.log('API not available');
}

If you're on a Chromium-based browser, it'll say "API available." Firefox and Safari don't implement it.

Step 3: Test It on a Site You Control

You can't paste a script into someone else's site and read your installed apps, because each site only gets answers about its own. So test with a page you own. Chrome's documentation shows a self-referencing entry for a PWA checking whether it is installed in its own scope:

"scope": "/",
"start_url": "/?utm_source=home_screen",
"related_applications": [
  {
    "platform": "webapp",
    "url": "/manifest.json",
    "id": "https://example.com/?utm_source=home_screen"
  }
],

Adapt the id to your own start URL, then:

  1. Open a JustBrowser profile, visit your test site and install it as an app
  2. In that profile, run navigator.getInstalledRelatedApps().then(apps => console.log(apps)) on a page inside the PWA's scope. You should see your app
  3. Open a second profile, visit the same page and run it again. You should see an empty array

Each JustBrowser profile is its own browser with its own data, so a PWA installed in one profile isn't visible to another, and the PWAs in your everyday browser aren't part of any profile. The test above is how you confirm that for yourself rather than taking it on trust.

Native apps are different, and no browser profile changes that. If your test site has a Windows app that declares it through URI handlers, every profile on that machine reports the same answer, because the answer comes from the operating system.

Common fingerprint test pages (see our TLS fingerprinting explainer for another layer they cover) don't check this API, so you can only find out by testing it yourself.

Step 4: Limiting What It Reveals

There are three ways to handle it, depending on which kind of answer you care about.

Option A: Remove Native Apps You Don't Use

Uninstall native apps you don't need from the machine you use for separate profiles.

Pros: An app that isn't installed can't be reported to any profile.

Cons: Inconvenient on your everyday machine, if you actually use those apps.

Option B: Use a Separate OS Account or a VM

A virtual machine, or an operating system account without those apps installed, gives you a separate set of installed apps underneath the browser.

Pros: Separation at the operating-system level, which covers native apps too.

Cons: Operational overhead. Switching users is friction, and VMs are heavier still. If you're managing 50+ browser profiles, you're not spinning up 50 VMs.

It suits high-security research work. It's overkill for most agencies and QA teams.

Option C: Keep PWA State Per Profile

For PWAs, the browser does the work, as long as each profile really has its own data. In a multi-account browser like JustBrowser, a PWA you install in one profile stays in that profile. This covers PWAs only: it doesn't change what the operating system reports about native apps.

For how extension-based tools differ from native ones on this kind of separation, the multi-account browser myths post covers the limits.

Step 5: Verify Your Setup

After choosing an approach, check it on your test site.

Test procedure:

  1. Install your test PWA in one profile only
  2. Open a different profile
  3. Run the API check on a page inside the PWA's scope
  4. Expected result: an empty array
// Run this on a page inside your test PWA's scope
navigator.getInstalledRelatedApps()
  .then(apps => {
    if (apps.length === 0) {
      console.log('No related apps reported in this profile');
    } else {
      console.log('Related apps reported:', apps);
    }
  });

Run it across several profiles. The results should match what you installed where: your PWA in the profile you installed it in, nothing in the others, and the same native-app answer everywhere on that machine.

If a PWA shows up in a profile you never installed it in, that tool isn't keeping profile data separate.

Common Errors and How to Fix Them

"getInstalledRelatedApps is not a function"

Cause: You're testing in Firefox or Safari. The API is Chromium-only.

Fix: Not an error. This browser simply doesn't support the API. (Firefox has its own fingerprinting surfaces, so don't read this as "nothing to check".)

"InvalidStateError" when calling the API

Cause: The call came from a page embedded in an iframe. The API only works in a top-level page.

Fix: Call it from the top-level page of your test site.

API returns empty but you definitely have the app installed

Cause: The page isn't inside the PWA's scope, the app isn't declared in the manifest, the app doesn't confirm the relationship from its side, or it isn't among the first three declared apps.

Fix: Check the manifest against Chrome's documentation and call the API from a page inside the scope.

A brand-new profile reports a native app as installed

Cause: Expected. Native apps are installed on the operating system, so every profile on the machine sees them.

Fix: None in the browser. Use Option A or B above if that answer matters to you.

Why This Matters for Agencies, QA and Research Teams

If you manage ad accounts for clients with permission, use each platform's own business tools: each of them only allows accounts to be held and shared in the ways its terms describe, and a separate profile doesn't change what is allowed. What a profile does give you is clean separation between client workspaces on your team. The same goes for agencies running calling campaigns via VeloCalls for several clients: keep each client's profiles separate.

For privacy-minded users and research teams who keep separate profiles, this API is a reminder that some answers come from the machine, not the profile. Native apps installed long ago still count.

Combined with other values, an installed-app answer is one more data point a site can use to relate visits. On its own it is weak evidence, but data points add up.

For ad verification teams checking whether campaigns render correctly across geos, it's worth knowing. For click fraud detection workflows using ClickzProtect, where your monitoring profiles need a consistent, separate setup, it's worth knowing too. Teams running analytics across multiple domains need clean profile separation as well. And for anyone who assumes separate profiles share nothing, it's a reminder that the fingerprinting surface is larger than the usual suspects.

Next Steps

Now that you understand this API:

  1. Audit your setup — Run the check on your test site from your most important profiles, and know which native apps are installed on the machine.

  2. Test any multi-account browser before committing — Per-profile PWA separation isn't a checkbox you'll find in marketing materials. Test it.

  3. Keep the host machine tidy — Don't install every app that offers itself, or use a dedicated machine for client work.

  4. Add this to your checks — Along with TLS fingerprinting and WebGL consistency checks, the installed-app check belongs in your verification workflow.

Fingerprinting keeps finding new surfaces. Canvas, WebGL, audio and fonts get the attention. Quiet APIs like getInstalledRelatedApps are easy to overlook precisely because few people look for them.

Frequently Asked Questions

What exactly does getInstalledRelatedApps reveal about me?

Whether the calling site's own related apps are installed: its Android app, its Windows Store app or its own PWA, and sometimes the app's version. The site has to declare the app in its web manifest, the app has to confirm the relationship from its side, and only the first three declared apps count. Native apps are installed on the operating system, so every browser profile on that machine gets the same answer for them. A PWA's install state belongs to the browser profile it was installed in.

Should I uninstall apps to avoid this signal?

Only the ones you don't use. The signal is narrow, because each site can only ask about its own apps. Removing a native app you don't need from the machine you use for separate profiles takes it out of every profile's answer. PWAs already stay with the profile they were installed in, so a multi-account browser that gives each profile its own data keeps them apart.

Do all websites have access to getInstalledRelatedApps?

Any HTTPS site can call it from a top-level page, but it only reports apps that the site has declared in its own web manifest and that confirm the relationship back. A page embedded in an iframe can't call it, so a third-party script can't use it to check for other companies' apps.

Is this API available in Firefox or Safari?

No. getInstalledRelatedApps is a Chromium feature, available in Chrome, Edge and other Chromium-based browsers, and Firefox and Safari don't implement it. Which app types it can report also depends on the platform, so check the compatibility notes in Chrome's documentation before relying on it.


Try JustBrowser

A multi-account browser for teams, built on a custom Chromium engine with native C++ integration (not extension-based). Each profile is its own browser with its own cookies, storage, proxy and 40+ identity parameters. REST API and CDP for Playwright, Puppeteer, Selenium. $9.99/month or $99.99/year. 7-day free trial, card required — cancel any time in the seven days and you are not charged. Unlimited profiles.

Get started → · How it differs from Multilogin / GoLogin / AdsPower

Tags
  • getinstalledrelatedapps
  • pwa-fingerprinting
  • browser-detection
  • cross-profile-linking
  • multi-account-browser
  • buildinpublic
  • saasstudio
  • aiworkforce
  • buildwithclaude

Ready to manage multiple accounts?

Seven days free, then $9.99/month — one plan, everything included.

We'd like to use Google Analytics, a Google service, to understand how our website is used. It sets two cookies in your browser and runs only if you click Accept. You can change your choice at any time with Cookie settings. Cookie Policy

Sign-in cookies and the cookie that remembers this choice are always on; the website needs them to work.

Google Analytics, a Google service, helps us understand how our website is used. It sets two cookies, _ga and _ga_TVZHQ99TZW. It is now onoff in this browser. If your browser sends a Global Privacy Control or Do Not Track signal, it stays off. Cookie Policy