How humanloads estimates real installs
The rule of the project: we show an estimate with an honest margin of error, not an exact number of users. Nobody has the exact number, npm included.
What npm actually counts
The npm downloads API reports how many times a package's .tgz tarball was served successfully each day.
It does not look at uniqueness, IP address, user agent or client type. So the count includes:
| Source | How it shows up in the data |
|---|---|
| Registry mirrors (Artifactory, Nexus, npmmirror, Verdaccio proxies…) | A spike on the day a version is published, fading over 2–4 days |
| Security scanners and crawlers | A flat floor: every version gets about the same downloads each week |
| Other projects' CI | Steady downloads of one pinned version, often on weekdays |
| Developers | Uneven, mostly the latest version and versions pinned in lockfiles |
| Inflation | Direct requests to the tarball URL, indistinguishable from the rest |
What the bots look like in real data
On a small real package with a handful of users, each release day showed 114–147 downloads per new version, and the background between releases was 0–11 a day. In one week its six versions got exactly 3, 3, 3, 3, 3 and 5 downloads: a robot that fetches every version once in a while. Only 2 downloads of the latest version stood above that floor, so about 2 of 20 weekly downloads looked human. This matches Tenable's research of roughly 100–150 automated downloads for every new version.
Step A: cut the publish spikes
Inputs are public: daily downloads (api.npmjs.org/downloads/range), publish dates from the registry
(registry.npmjs.org/<pkg>) and last week's downloads per version (api.npmjs.org/versions/<pkg>/last-week).
- Every publish day opens a window of that day plus the next 3.
- For each day in a window, the background is the larger of two medians: the same weekday 1–2 weeks before, and the same weekday 1–2 weeks after. Zero days (npm API outages) and days inside other windows are skipped.
- A day counts as a spike only if it is more than 2× its background. Then it is cut down to the background; the difference goes to mirrors and scanners.
Comparing with the same weekday keeps the weekly rhythm; taking the larger of “before” and “after” never cuts real growth; the 2× threshold lets normal ups and downs through. Simpler baselines failed on real packages: one that publishes canaries daily had no quiet days to compare with, and one with a 2.5× weekday/weekend swing lost every weekday.
Step B: subtract the crawler floor
- Take last week's downloads of every version older than 14 days (newer ones are still in their spike). The crawler floor f is a low percentile of those counts.
- Human downloads ≈ the sum over versions of
max(0, downloads − f), minus the spike removed in step A for the same 7 days. - That week's share of human downloads is applied to the cleaned daily series to draw the chart.
- With fewer than 3 old versions the floor can't be measured. Then it comes from calibration (below) or a default of 3 per version, and the page says so.
Step C: a range, not a number
The result is always a range. Its edges come from different floor percentiles (10th for the best guess, 25th for the low end, the minimum for the high end), widened by counting noise: about ±2√n for a weekly count of n, roughly a 95% Poisson interval. For a package with 2 human installs a week that reads “≈ 0–5”; for a large package the band is negligible.
Confidence levels
- low: fewer than 10 human installs a week (noise is as big as the signal), or the floor could not be measured.
- high: 1,000 or more a week with a measured floor.
- medium: everything else.
Low becomes medium (with at least 10 a week) when there is independent evidence of use, or when weekday traffic is at least 1.3× weekend traffic over the last 8 weeks: people work in a weekly rhythm, robots don't. CI also runs on weekdays, so the rhythm never lifts a package to high. Missing evidence never lowers confidence.
Independent evidence of use
Downloads are not the only trace of use. Each package page also shows:
- Dependent packages and repositories (ecosyste.ms, deps.dev): published packages and public projects that list it.
- CDN hits (jsDelivr): websites loading it from a CDN.
- GitHub activity: stars, forks and issues from other people.
Thresholds for raising confidence: 5 dependent packages, 10 dependent repositories or 1,000 monthly CDN hits. Stars show interest, not installs, so they never count. All of these are lower bounds: private code is invisible.
Calibration
A canary package that nobody uses gets a new version regularly. Everything it downloads is pure bot noise, so its old versions measure the crawler floor directly. When a package's own floor can't be measured, the canary's floor is used instead of the default.
Known limits
- Private and corporate use behind a registry mirror is invisible in principle.
- Inflation by direct tarball requests can't be told apart from real traffic.
- For very popular packages the floor drowns in the volume, so the estimate barely changes the number. That is correct.
- npm data lags by 1–2 days.
FAQ
- Are npm download counts accurate?
- They are an accurate count of tarball fetches, not of people. Every fetch counts: registry mirrors, security scanners, CI pipelines and crawlers as well as developers. For small packages most of the number is robots.
- How many npm downloads are bots?
- It depends on the package. A brand-new version gets roughly 100–150 downloads from mirrors and scanners within days, and crawlers fetch every published version a few times a week. For a small package that can be 90% of the count; for a very popular one it is a rounding error.
- Can humanloads tell me the exact number of users?
- No, and nobody can, npm included: npm does not record who downloaded what. humanloads gives an estimate with a range and a confidence level and shows its working.
- Why is the estimate for my package so low?
- Usually because most of its downloads match bot patterns: spikes right after each release and the same flat count on every old version. Private and corporate use behind a registry mirror is invisible to everyone and cannot be counted.