This came up when @erlend.sh asked about the “60M+ identities” number on the sifa.id homepage. Turns out that number was wrong and I like to have a more accurate number, but depending on what you count, that 60M might be too high or too low….
I went down the rabbit hole, crawled the entire PLC directory, and figured I’d share what I found.
What’s out there
1. Jaz’s Bluesky Stats / theo.io counter
bsky.jazco.dev/stats (by @jaz.bsky.social) indexes the Bluesky firehose and queries the Bluesky API for registered accounts, updated every ~5 minutes. bsky-users.theo.io wraps this with interpolation for a smooth real-time ticker.
Counts: registered Bluesky accounts (DIDs known to Bluesky’s AppView). Currently ~43.3M.
Good: real-time, reliable, easy to consume via JSON API.
But: only Bluesky. If you’re on a non-Bluesky PDS that their AppView doesn’t index, you don’t exist in this number. And “registered” includes every abandoned signup. Bluesky’s own 2025 transparency report says ~41.4M registered at end of 2025.
2. PLC directory crawl (what I did)
The PLC directory at plc.directory is the canonical registry for all did:plc identities, and nearly every ATProto account uses did:plc (hello did:web
) I crawled the entire /export API: 85.7M operations, ~80K pages, counted genesis operations (prev: null), grouped by PDS endpoint.
Results are public at sifa.id/stats.
Counts: every did:plc ever registered, regardless of PDS or AppView.
Good: complete (covers the full ATmosphere, not just Bluesky), broken down by PDS provider so you can see ecosystem composition, and the export API is public so anyone can verify.
But: no way to distinguish real accounts from junk. We found 17.4M DIDs from pds.trump.com (every single one registers with the same 7 Trump family handles… none resolve) and 1.8M from plc.surge.sh/gallery. That’s 19M+ phantom entries just from those two. Crawling the full directory takes hours, and “reachable” (handle resolves) is a proxy for “exists,” not “active.”
3. Bluesky transparency report
Bluesky’s 2025 report states 41.41M registered users at end of 2025. They say this “includes accounts hosted on Bluesky’s infrastructure, as well as the thousands of Personal Data Servers operated by people across the federated AT Protocol network.” So they count third-party PDS users too, but only those visible to their AppView.
The numbers
| Metric | Count | Source |
|---|---|---|
Total did:plc registered |
79.8M | PLC directory crawl |
| Reachable (handle resolves) | ~59.3M | PLC crawl + sampling |
| Bluesky registered accounts | ~43.3M | theo.io / Jaz |
| Bluesky reported total (end 2025) | 41.4M | Transparency report |
| Bluesky DAU (end 2025) | ~5.2M | Third-party estimates |
The gap between 79.8M and 43.3M is mostly pds.trump.com DID spam (17.4M) and plc.surge.sh junk (1.8M). Non-Bluesky ATProto users that are actually real (Bridgy Fed, independent PDS hosts, etc.) are probably in the low hundreds of thousands.
What I got wrong
Our homepage showed “60M+ identities on the AT Protocol” by taking the theo.io Bluesky count and adding a 17M offset (a number I got from an original quick PLC directory crawl). The idea was that the PLC directory has more DIDs than Bluesky tracks, so the difference must be non-Bluesky ATProto users.
It wasn’t. It was almost entirely DID spam. ![]()
I’d love to fix that number but… to what? Any consensus on what should it should actually show?
-
~59M (PLC reachable): technically correct for “identities that resolve,” but probably still inflated by accounts that signed up and never came back
-
~80M (PLC total): the raw truth, but 20M+ of those are phantom DIDs that resolve to nothing
-
… something else?
I get that it makes no sense to include DIDs that don’t resolve. But on the other hand it feels weird for a “total ATproto account statistic to exclude certain groups. It’s a bit like saying “There’s 6.8 Billion people in the world! We know there are actually 7.3 billion people but that group of 1.5 billion we don’t like so we exclude them from the total number of people”. It’s a statistic, not a judgement call on who to exclude ![]()
Is there any consensus in the community on how to count ATmosphere users? Are there other methods I’m missing?
Also: The PLC directory not having a count endpoint makes this harder than it needs to be… if someone can make that change that would be sweet ![]()
Check sifa.id/stats for what I got so far. Happy to open-source the crawl script if anyone wants to verify or build on this.