Firefox add-on analytics: what AMO shows and how to see the rest
7 min readModerok team
What Firefox add-on analytics you get from the AMO statistics dashboard, why its numbers run low, and how to measure the rest from inside the add-on.
Firefox add-on analytics comes in two layers. The first is the statistics dashboard on addons.mozilla.org (AMO): private to an add-on's authors and owners, aggregated from Firefox telemetry, and reporting usage alongside install and download statistics (each sourced below, as of October 2026). The second is whatever you instrument inside the add-on. The AMO layer answers "how many, roughly" and stops. It cannot tell you which features people use, whether an install cohort survived its first week, or what threw an error in your background script last night.
TL;DR: AMO gives you telemetry-derived usage, counted only over enabled installs in profiles that send telemetry, plus install counts that come only from your listing page. Feature usage, retention by install date and runtime errors appear nowhere in Mozilla's documentation of it, and events sent from inside the add-on are how you get them. Four Firefox specifics decide whether that works: the background is an event page rather than a service worker, the browser-update install reason has a different name,
storage.syncneeds a Gecko add-on ID, and new listings must declare what they collect.
What the AMO statistics dashboard actually reports
Sign in to AMO, open Manage My Submissions, and a Statistics link sits under each add-on name. Mozilla's Monitoring extension usage statistics page says, as of October 2026, that the dashboard "is private and can only be accessed by authors and owners of the extension", that "These stats are aggregated from Firefox telemetry data and do not include any personally identifiable user data", and that it "provides developers with more information about user adoption, general demographics, and other insights".
That is a description of output, not a metric list, and the page does not enumerate the dashboard's charts, so read the slices you get off your own dashboard. The nearest documented figures are two API fields: average_daily_users, "The average number of users for the add-on (updated daily)", and weekly_downloads, "The number of downloads for the add-on in the last week" (addons-server API reference, as of October 2026).
One limit applies on the install side. That Extension Workshop page says "The statistics dashboard will only count installs from the AMO listing page," and, under "Tracking external sources", that "Installs from other sources, such as .xpi downloads from a website or blog, are not counted on the dashboard, even if they link to an XPI hosted on AMO." The attribution it does document is by source: append UTM parameters (utm_source, utm_medium, utm_content, utm_campaign) to links pointing at your listing, and the dashboard "includes a view that breaks down downloads by each of these parameters", within "a limit of 40 characters for each UTM parameter value".
Why the AMO user number runs lower than you expect
The current numbers are telemetry numbers. When Mozilla moved the stats pipeline off daily update pings, the announcement was explicit: "For most add-ons you should expect usage totals to be lower, but usage trends and fluctuations should be nearly identical." Three of its sentences explain the gap.
The counting unit changed. "Our current stats system counts distinct users by IP address, while telemetry uses a per-profile ID." Addresses and profiles are not the same unit, so comparisons straddling that cutover mean little.
Disabled installs vanish. "The dashboards will only show data for enabled installs." Someone who disables your add-on in about:addons has not uninstalled, but drops out of the count, and nothing separates them from people who removed you.
Telemetry is optional. "Firefox users can opt out of sending telemetry data, and the way they are counted is different." Those profiles run your add-on and never appear in the usage figure. Treat the dashboard the way Mozilla frames it: a read on direction, not a census.
One factor pushes the other way: the same post notes that "some add-ons will have higher usage numbers, since the average will be calculated including both AMO-hosted and self-hosted versions." Usage is a telemetry figure, so it does not care where the add-on came from, while the install count is limited to your listing page. The two are not the same population.
The Firefox add-on analytics AMO cannot give you
Three product questions appear nowhere in Mozilla's documentation of the dashboard, and all three are about your code rather than the browser.
Which features people use. No store statistic knows your export button fires once a week and your keyboard shortcut never fires at all.
Retention by install date. A daily usage average cannot be split into "people who installed in week 1" and asked whether they were still there on day 30.
Errors. A background script that throws on startup for one OS is invisible in a usage count. You find out from reviews.
All three need events sent from inside the add-on, which brings in the Firefox specifics.
Instrumenting a Firefox add-on: four things that bite
Your background is an event page, not a service worker. Firefox MV3 runs background.scripts, not background.service_worker. The consequences (one manifest for both stores, no DOM in a shared file, top-level listeners, chrome.alarms instead of setTimeout) are covered in Firefox Manifest V3 uses an event page, not a service worker. Analytics code must survive that background unloading between events.
The browser-update reason has a different name. MDN's runtime.OnInstalledReason documents "browser_update" or, for Chrome, "chrome_update" as "The browser was updated to a new version.", alongside "install" and "update". A handler checking only for "chrome_update" mis-buckets a Firefox browser upgrade. runtime.onInstalled also hands you temporary, documented as true "if the add-on was installed temporarily", giving the about:debugging page and web-ext run as its examples. Without that guard, every web-ext run records an install.
chrome.runtime.onInstalled.addListener((details) => {
if (details.temporary) return; // about:debugging or web-ext run
if (details.reason === "install") {
// genuinely new profile
} else if (details.reason === "update") {
// details.previousVersion is present only here
}
// "browser_update" (Chrome: "chrome_update") is neither; ignore it
});
storage.sync needs a Gecko ID. Persisting an analytics ID in sync storage, so a profile reset does not mint a phantom new user, has a documented prerequisite in Firefox. MDN's storage.sync page states: "The implementation of storage.sync in Firefox relies on the Add-on ID. If you use storage.sync, you must set an ID for your extension using the browser_specific_settings manifest.json key." It also documents the ceilings: 102,400 bytes total, 8,192 per item, 512 items, with set() rejected beyond them. One identity record fits; an event buffer does not.
{
"browser_specific_settings": {
"gecko": {
"id": "addon@example.com"
}
}
}
You have to declare what you collect. Extension Workshop's built-in data consent page says "Firefox supports built-in consent for data collection and transmission in Firefox for desktop 140 and later" (Android 142), and that "From November 3, 2025, all new extensions must adopt the Firefox built-in data collection consent system." Usage metrics land in the technicalAndInteraction category, which that page notes "cannot be required" and "must be optional". Those floors are an obligation, not a formality: "If your extension collects data and a user installs it on Firefox for desktop 139 or earlier" (or Android 141 or earlier) "it must display a custom data collection experience", and the page gives three ways out, one of which is setting strict_min_version to 140 and 142. The lists, the none exclusivity rule and each value's meaning are worked through in Firefox extension data collection permissions.
What to send
Keep the event list short enough to read in one screen: install, update, a once-per-day activity ping, one event per user intention (not per click), and errors. Keep properties flat, with string, number or boolean values, and keep identifying content out of them: what you put in a property, not its type, decides what you are actually collecting.
Moderok's SDK sends the lifecycle part on its own: its automatic events are __install, __update, __first_open, __daily_ping and __error. The getting started guide documents Manifest V3 on Chromium or Firefox 109 and later with the storage permission, and the manifest page says you do not add host_permissions. It calls the chrome.* namespace Firefox aliases, parses the Firefox user agent to tag each event's browser, and treats moz-extension: pages as extension surfaces. It also mirrors its anonymous ID and last daily-ping date to chrome.storage.sync, the write that Gecko ID exists for; that write is best effort, so an add-on without the ID still runs, it just loses the recovery. The SDK is 5.7 kB gzipped with zero runtime dependencies.
If AMO has taken you as far as a usage curve and you want custom events, uninstalls by tenure and error logs beside it, see what the Moderok dashboard reports, then follow the getting started guide in your Firefox build.