npm hub

npm Downloads API

Download totals or a row per day for any package, and the registry-wide total when you leave the name out.

GET /v1/npm/package/downloads

1 credit Failed requests are free

About this endpoint

GET /v1/npm/package/downloads?name=express&period=last-week returns data.total with data.start and data.end for the window. Set granularity=daily to get data.days, one { day, downloads } row per day, with data.total: null. Periods are last-day, last-week, last-month and last-year; last-week is the default.

Give from and to as calendar days (YYYY-MM-DD) instead of a period for an explicit range. A range spans up to 18 months; a longer range keeps its newest 18 months and sets data.clamped. Download data starts on 2015-01-10, and a range wholly before that day is refused. Omit name for the whole registry total, which is only available as a total. The newest day usually reads 0 because npm has not counted it yet, so use data.end as the last complete day. npm's counts also have gaps for some packages, so a month or year total can be lower than the sum of the daily rows.

npm Downloads API FAQ

What is the difference between total and daily?

granularity=total gives one number in data.total plus the start and end days. granularity=daily gives data.days, one row per day, and leaves total null. Daily is only available for a named package; the registry total is a single number.

How do I request a custom date range?

Pass from and to together as YYYY-MM-DD and leave period out. The route counts both ends. A range spans up to 18 months; a longer range keeps its newest 18 months and sets data.clamped. A range wholly before 2015-01-10 is refused.

Why does the newest day read zero?

npm has not finished counting it. The response reports data.end as the last complete day, so sum up to that day instead of assuming the final rows are real zeros. Some packages also have gaps in their daily counts, so a period total can be lower than the sum of the daily rows.

What happens for a package npm does not have?

The response is HTTP 200 with data.lookupStatus not_found. A package with real downloads returns found and the total. The registry total uses name: null and is found when the registry-wide total is available.

What does the request cost?

Pricing is documented on the operation in the API registry (shown on this page). Confirm on every response with meta.creditsCharged: that field is the billing source of truth.