티스토리 수익 글 보기

티스토리 수익 글 보기

docs(blog): download counts (#20476) · pypi/warehouse@8294504 · GitHub
Skip to content

Commit 8294504

Browse files
authored
docs(blog): download counts (#20476)
Signed-off-by: Mike Fiedler <miketheman@gmail.com>
1 parent 09cd2d2 commit 8294504

2 files changed

Lines changed: 133 additions & 0 deletions

File tree

143 KB
Loading
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
---
2+
title: "Metadata requests no longer tracked in PyPI download counts"
3+
description: PyPI stopped counting requests that were never artifact downloads. Historical counts were inflated, and comparisons across 2026-08-24 will not line up.
4+
authors:
5+
- miketheman
6+
date: 2026-08-31
7+
tags:
8+
- infrastructure
9+
- transparency
10+
meta:
11+
- name: fediverse:creator
12+
content: "@miketheman@hachyderm.io"
13+
---
14+
15+
On 2026-08-24 I [shipped a change to the configuration that emits PyPI's download logs](https://github.com/pypi/infra/pull/242)
16+
so that only requests for actual distribution artifacts are counted.
17+
A request has to end in `.whl`, `.tar.gz`, or `.zip` to produce a download record.
18+
19+
The counts are more accurate now,
20+
and existing systems based on [PyPI's BigQuery dataset](https://docs.pypi.org/api/bigquery/),
21+
like [pypistats.org](https://pypistats.org/)
22+
will display a different shape going forward.
23+
24+
<!-- more -->
25+
26+
If you maintain something that reads these numbers from the public dataset,
27+
the discontinuity at 2026-08-24 is expected and permanent.
28+
29+
This effort would not be possible without the continued support from [Alpha-Omega](https://alpha-omega.dev/).
30+
31+
## Background
32+
33+
The logs are generated by Fastly's Edge, shipped to an AWS S3 bucket,
34+
parsed and anonymized by our [`linehaul`](https://github.com/pypi/linehaul-cloud-function) functions in Google Cloud,
35+
and published to a Google-managed [public BigQuery dataset](https://docs.cloud.google.com/bigquery/public-data).
36+
37+
## Other objects
38+
39+
Everything PyPI serves for a release lives under the same `/packages/<xx>/<yy>/<hash>/` prefix.
40+
Matching on that prefix alone counted a fetch of any part of a release
41+
as a download of the distribution itself.
42+
43+
- [PEP 658](https://peps.python.org/pep-0658/) `.metadata` sidecars. (estimated at 40%)
44+
Installers may fetch these to read a wheel's metadata *without* downloading the wheel.
45+
Every metadata fetch was being logged as a distribution download.
46+
- `.asc` GPG signatures. PyPI [stopped accepting these in 2023](2023-05-23-removing-pgp.md),
47+
but the ones uploaded before then are still served, and no longer counted.
48+
- `.egg` uploads were separately [deprecated in 2023](2023-06-26-deprecate-egg-uploads.md).
49+
All of these remain downloadable, and none of them are counted.
50+
- Formats frozen for upload since 2016 under [PEP 527](https://peps.python.org/pep-0527/),
51+
such as `.exe`, `.msi`, and `.rpm`, are about 0.2% of the files on PyPI, and are no longer counted.
52+
53+
## The correction
54+
55+
![Daily downloads across all of PyPI, 60 days](../assets/2026-08-31-pypistats_all_60d.png)
56+
*Daily download quantity across all of PyPI, 60-day window ending 2026-08-31, from [pypistats.org](https://pypistats.org/).*
57+
58+
Before the change, daily totals cycled between roughly 4.5 billion on weekends
59+
and a little over 7 billion midweek.
60+
The curve steps down after 2026-08-24.
61+
The last few days of that window were still settling when the chart was captured,
62+
so read the shape of the change rather than the final data point.
63+
64+
## Impact on existing data
65+
66+
If you compare download counts across 2026-08-24, the numbers will not line up.
67+
The counts after that date are lower and more accurate.
68+
Nothing was lost from the historical record - the older rows in BigQuery are unchanged,
69+
they were just measuring something broader than "someone downloaded this package" -
70+
they measured "someone downloaded a given file from PyPI".
71+
Most consumers of this dataset today do not filter specific to filename extensions,
72+
leading to inflated download counts for a given package.
73+
74+
However, if you wanted to query the dataset yourself, you can write your own SQL queries
75+
to be able to distinguish a single project's distribution file downloads
76+
from others with a BigQuery statement that looks a bit like this:
77+
78+
```sql
79+
SELECT
80+
DATE(timestamp) AS download_date,
81+
COUNT(*) AS all_objects,
82+
COUNTIF(REGEXP_CONTAINS(file.filename, r'\.(whl|tar\.gz|zip)$')) AS distributions_only,
83+
COUNT(*) - COUNTIF(REGEXP_CONTAINS(file.filename, r'\.(whl|tar\.gz|zip)$')) AS others,
84+
ROUND(100 * (COUNT(*) - COUNTIF(REGEXP_CONTAINS(file.filename, r'\.(whl|tar\.gz|zip)$'))) / COUNT(*), 1) AS others_pct
85+
FROM `bigquery-public-data.pypi.file_downloads`
86+
WHERE project = 'urllib3'
87+
AND timestamp >= TIMESTAMP('2026-08-18')
88+
AND timestamp < TIMESTAMP('2026-08-26')
89+
GROUP BY download_date
90+
ORDER BY download_date
91+
```
92+
93+
Results:
94+
95+
| download_date | all_objects | distributions_only | others | others_pct |
96+
| ------------- | ----------- | ------------------ | ------ | ---------- |
97+
| 2026-08-18 | 77477790 | 47268787 | 30209003 | 39\.0 |
98+
| 2026-08-19 | 74742551 | 45808710 | 28933841 | 38\.7 |
99+
| 2026-08-20 | 73495894 | 45011207 | 28484687 | 38\.8 |
100+
| 2026-08-21 | 69652309 | 42467783 | 27184526 | 39\.0 |
101+
| 2026-08-22 | 49665300 | 30080582 | 19584718 | 39\.4 |
102+
| 2026-08-23 | 50965479 | 30344490 | 20620989 | 40\.5 |
103+
| 2026-08-24 | 64241532 | 44012651 | 20228881 | 31\.5 |
104+
| 2026-08-25 | 46619401 | 46619401 | 0 | 0\.0 |
105+
106+
For this particular query and time range, ~39% of the download counts are
107+
not the actual distribution files.
108+
109+
## Downloads are not a popularity metric
110+
111+
Download counts are tricky to get right,
112+
and should not be used as a proxy for criticality or popularity.
113+
Volumes are often driven by misconfiguration of proxies or caches, CI systems running wild,
114+
or any variety of problems that occur with software.
115+
There's even folks out there who drive these numbers up artificially.
116+
117+
This change removes one source of potential confusion and misinterpretation,
118+
but does not mean download counts are an accurate measure of how many people use a package.
119+
120+
## Future work: range requests
121+
122+
An HTTP response code `206 Partial Content` counts exactly the same as a `200 OK` right now.
123+
Reading two bytes out of a wheel to inspect its ZIP central directory
124+
is recorded as a download of the whole wheel,
125+
alongside a client that pulled all 30 MB.
126+
127+
We want to record the request type and the bytes actually transferred,
128+
so that analysis can distinguish bytes served from downloads counted.
129+
That work is not scheduled yet.
130+
Follow <https://github.com/pypi/linehaul-cloud-function/issues/232>
131+
and <https://github.com/pypi/linehaul-cloud-function/issues/252>
132+
for that.
133+

0 commit comments

Comments
 (0)