Bluesky Profile Stats Viewer – read an identifier before you trust it
Every Bluesky account answers to two different names, and confusing them is the source of most of the mistakes people make when they write an account down. A handle like alice.bsky.social is a domain name. A DID like did:plc:z72i7hdynmk6r22z27h6tvur is the account itself. This tool takes whichever one you have, shows you exactly what it is made of, and renders the profile card that goes with it.
Why the distinction matters
A handle is rented, not owned. Whoever holds it can change it whenever they like — moving from a default *.bsky.social address to a custom domain is a normal thing to do — and once a handle is released, somebody else can register it. So a handle answers the question “what is this account called right now?” and nothing more. A DID is assigned once, never changes, and is never reassigned to a different account.
What the breakdown shows you
Paste an identifier and it is normalised first: a leading @ is stripped, a https://bsky.app/profile/ prefix is reduced to the segment after it, invisible characters that survived the copy are removed, and a handle is lower-cased because handles are case-insensitive. Each change is reported rather than done silently, so you can see what was altered.
A handle is then checked as a domain name, because that is what it is. It must contain at least one dot — there is no such thing as a bare alice. Each part between dots may be 1–63 characters of letters, digits and hyphens, and may not start or end with a hyphen. The whole name is capped at 253 characters, the standard DNS limit. The final part cannot be all digits, and reserved suffixes such as .local, .invalid and .internal are rejected because they never resolve on the public internet. Each label is drawn as its own chip so you can see where a long handle actually breaks.
A DID is split into its three pieces: the literal did prefix, the method, and the identifier. Bluesky uses did:plc: and did:web: in practice; anything else parses but carries a warning. The identifier is left exactly as you typed it, because a DID identifier is case-sensitive — lower-casing one the way you lower-case a handle produces a different identifier entirely.
Three ways in, all of them offline
The breakdown never needs a lookup, and neither do the other two modes. Manual entry lets you type a display name, bio and counts and get the same card. Paste JSON takes a getProfile response you obtained yourself and reads it defensively — every field optional, every type checked, unknown keys ignored, and a malformed paste reported with the line and column rather than a crash.
Counts are counts
Followers, following and posts are shown exactly as the response reported them, or as a dash when it carried no usable number. Nothing is graded, ranked or benchmarked against other accounts, and no engagement rate, reach or growth figure is derived from them — the endpoint does not return those, and arithmetic does not invent data a source never sent. A follower count is not a measure of anybody’s worth.
Everything renders as ordinary text, so a bio containing markup shows as literal characters rather than executing, and any avatar or banner address is checked against an https-only allowlist before it is allowed to become an image. Nothing you type or paste leaves the page.