crates.io download count: 80% of ours arrived with a new version
Originally published at tensorust.com. TL;DR: On October 17, 2026 I pulled every daily download number crates.io has for my 69 Rust crates. Over 89 days they logged 3,676 downloads, and 80.1% of them landed on the day a new version went up or the day after. A new version collects about 13 downloads in its first 24 hours no matter what is in it, then flatlines. Outside crates that depend on any of the 69: zero. The number that actually tells you whether people use your crate is the quiet-day baseline, and for us it was 339 downloads, about 3.8 a day. Short answer for the search. The crates.io download count is the number of times the crate's .crate file was fetched from the crates.io CDN, counted from the CDN's request logs (crates.io moved to log-based counting in March 2024). Since October 29, 2025 only requests that look like they came from cargo (or send no user agent at all) are counted. It is not a count of users, installs or projects. One developer's CI that runs cargo build on a clean machine 30 times a day adds 30 downloads a day; one person who installs your crate once and keeps it in their local cache adds one, ever. So what is actually inside a small crate's number? Below is a fresh measurement on a real portfolio, the script that pulls it (copy it and run it on your own crates), and how to read your own chart without lying to yourself in either direction. Every daily download for all 69 crates, colored by kind of day. crates.io API, pulled Oct 17 2026, 7:10 AM ET. Bar height is square-root scaled so quiet days stay visible. Where the 69 crates came from Some context, because the shape of the data comes straight from how these crates were published. I maintain a pile of Rust libraries and tools: fin-primitives (trading building blocks), tokio-prompt-orchestrator (an LLM proxy with dedup and a circuit breaker), proof-engine (an OpenGL game engine), and CoreScout, an MCP server split into a workspace of 33 crates. They went out in three bursts: 51 versions in March 2026, 116 in September and 68 in October, 235 versions in total. The October burst was one big release run on October 4, after a pass where AI coding agents upgraded every crate and I published everything at the end in one go. That single day matters a lot later. All-time total when I pulled it at 7:10 AM ET on October 17: 4,815 downloads. The crates.io "recent downloads" figure (last 90 days) was 3,677. My goal for these crates is 100,000, so the honest question is how much of the 4,815 is people. Pull the numbers yourself in 40 lines crates.io has a public JSON API. It isn't formally versioned, but it's what the website itself calls, and the data access policy asks for two things: at most one request per second, and a User-Agent that says who you are. Three endpoints give you everything: /api/v1/crates?user_id=N: every crate a user owns, with all-time and 90-day totals. /api/v1/crates//downloads: daily downloads for the last 90 days, split by version (older versions are lumped into extra_downloads). /api/v1/crates//reverse_dependencies: which published crates depend on this one. This is the script I ran this morning (pull.py, trimmed only of the JSON field picking). It stops dead on a 429 instead of retrying, because a polite scraper doesn't argue with a rate limit: UA = "tensorust-deepdive-research (contact: hello@tensorust.com)" last = [0.0] def get(path): wait = 1.05 - (time.time() - last[0]) # stay under 1 request/second if wait > 0: time.sleep(wait) last[0] = time.time() req = urllib.request.Request("https://crates.io" + path, headers={"User-Agent": UA}) try: with urllib.request.urlopen(req, timeout=30) as r: return json.load(r) except urllib.error.HTTPError as e: if e.code == 429: print("429 - stopping"); sys.exit(2) raise user = get("/api/v1/users/mattbusel")["user"] crates = get(f"/api/v1/crates?user_id={user['id']}&per_page=100")["crates"] for c in crates: n = c["name"] dl = get(f"/api/v1/crates/{n}/downloads") vers = get(f"/api/v1/crates/{n}/versions?per_page=100") rev = get(f"/api/v1/crates/{n}/reverse_dependencies?per_page=100") # ... store dl["version_downloads"], dl["meta"]["extra_downloads"], versions, rev 69 crates times three calls, one per second, took about three and a half minutes. Then a second pass of 69 calls to map version ids to version numbers and publish times, because the downloads endpoint labels each row with a numeric version id, not "2.15.0". Real output from pull.py and the analysis scripts, Oct 17 2026, 7:10 to 7:16 AM ET. If you have more than a few hundred crates (or you want every crate on the registry), don't hammer the API: crates.io publishes a database dump every 24 hours (static.crates.io/db-dump.tar.gz) that includes the per-day, per-version download table for the last 90 days. What the crates.io page shows you Here's the bottom of the fin-primitives page, which is my most-downloaded crate. Look at the chart before reading the numbers: a low wiggle of 0 to 3 a day through the summer, then tall spikes in the last week of September and the first week of October, each spike a different color. Each color is a version. The real crates.io page for fin-primitives, captured Oct 17 2026. Each colored spike is a new version. That chart is honest, but it's per crate. With 69 crates you can't eyeball 69 charts, so I classified every crate-day in the 90-day window into three buckets. The three kinds of download day The rule is simple and fully mechanical: Publish window: that crate published a version on this UTC day or the day before. Sweep day: no publish, but at least half of all my crates that existed got downloaded on the same day. Quiet: everything else. The core of the classifier, from analyze.py: for n, c in C.items(): for v in c['downloads']['version_downloads'] + c['downloads']['meta']['extra_downloads']: d = dt.date.fromisoformat(v['date']) if any(0
This is a summary aggregated from Dev.to. Read the complete article on the original site:
Read full article at Dev.to