🪐 Icarus

Follower counts for the AT Protocol. Icarus reads every app.bsky.graph.follow record on the network and keeps only what a follower count needs. 🚀

Ask it how many accounts follow someone, and how many that someone follows:

curl 'https://icarus.atverkackt.de/xrpc/de.atverkackt.icarus.getProfile?actor=pfrazee.com'
{
  "did": "did:plc:ragtjsm2j2vknwkz3zp4oxrd",
  "handle": "pfrazee.com",
  "followersCount": 191055,
  "followsCount": 683
}

That is the whole API. One query, four fields. The service holds no posts, no likes, no profile text — only the shape of the graph.

🌍 Two colonies, one lexicon

The same query runs on two hosts. They read the network by different routes and answer the identical lexicon, so a client can ask either one.

HostReadsCharacter
icarus.atverkackt.de The Jetstream event archive and the live tail Current to the second. A lost event stays lost.
picon.atverkackt.de Whole repository snapshots from hubble Self-correcting. A snapshot repairs whatever drifted.

Icarus learns the graph from a stream of changes, so it is immediate but can miss an event it never received. Picon folds the complete state of a repository, so a count it reports is the count the repository actually holds. Where the two disagree, believe picon.

GET /xrpc/de.atverkackt.icarus.getProfile

Return the follower count and the follow count of one account.

Parameters

NameTypeRequiredMeaning
actor at-identifier yes The DID or the handle of the account.

Prefer a DID on a hot path. A query by DID never leaves the process. A query by handle makes an outbound DNS and HTTPS request whenever the handle is not already cached.

Response

FieldTypeMeaning
did did The authoritative identifier of the account. Always present.
handle handle The handle, or handle.invalid when the service has not seen an identity event for the account. See below.
followersCount integer Follow records that point at this account.
followsCount integer Follow records in the repository of this account.

Errors

NameMeaning
AccountNotFound The service holds no record of the account.

Try it


GET /health

Answers 200 with the body ok while the process serves. It reports liveness only, and says nothing about how far the backfill has reached.

🌗 Definitions

The counts are records, not accounts

Both numbers count follow records. The Bluesky AppView counts distinct accounts, so it ignores a second follow record naming an account the author already follows. A repository that holds duplicate follow records therefore reads higher here than it does on Bluesky. Neither number is wrong; they answer different questions.

handle.invalid

An identity event only fires when a handle changes, so the service has never seen a handle for most accounts and reports handle.invalid for them. A query that names a handle returns that verified handle. A query that names a DID returns whatever handle the account claims, unverified.

Treat the DID as the identity and the handle as a label. The did field is always authoritative.

A count of zero

A followsCount of zero can mean the account follows nobody, or that the backfill has not reached its repository yet. The follower count fills in gradually as the sweep folds other repositories; the follow count arrives all at once, when that account's own repository is read.

🌘 Using it kindly