Username Sweeps and the Rate-Limit Trap
Checking one handle across eight platforms is easy. The bug worth avoiding is treating every failed request as proof the account does not exist, which is how OSINT tools quietly lie to you.
Username Sweeps and the Rate-Limit Trap
TL;DR: Checking a handle across platforms is a few
fetch()calls, because several public APIs send permissive CORS headers. The part that goes wrong is error handling: if you treat every non-200 response as "account does not exist", a rate limit becomes a confident false negative. Our Username OSINT Tracker reports "unknown" instead, and this post explains why that matters more than it sounds.
A username is one of the more useful pivots in open-source intelligence. People reuse handles, often for a decade, across services they forgot they signed up for. Checking one handle across many platforms is a standard early step, and it is genuinely simple to build.
Ours checks 8 platforms: GitHub, GitLab, npm, Dev.to, Keybase, Docker Hub, Archive.org and Wikipedia. Each has a public endpoint that answers whether a handle is taken, and each sends CORS headers permissive enough for a browser to read the response. No backend, no API keys, no proxy.
Three shapes of "does this exist"
The APIs do not agree on how to answer, which is most of the implementation.
Status-code APIs. GitHub, npm, Dev.to, Docker Hub and Archive.org return 200 when the user exists and 404 when they do not. Read the status, done.
Empty-array APIs. GitLab's user search returns 200 with [] for a handle nobody owns. A naive status check reports every non-existent GitLab user as found. You have to look inside the body.
Envelope APIs. Keybase returns 200 with a status object, and Wikipedia returns a user list where a non-existent account still appears as an entry, just without a userid field. Both require checking a specific field rather than the response code or even the array length.
Miss any of these and the tool is confidently wrong in one direction on one platform, which is the worst kind of wrong because nothing looks broken.
The bug that matters
Here is the mistake we shipped and then fixed, because it is instructive.
The original implementation collapsed everything into a boolean: 200 meant the account exists, anything else meant it does not. Clean, readable, and wrong.
GitHub's API allows 60 requests per hour from an unauthenticated IP. Cross that and you get 403. Under a boolean model, 403 is not 200, so the tool renders "Not Registered" for a handle that may well have an active account. The interface shows the same confident grey X it shows for a genuine 404.
That is not a cosmetic problem. It is a tool telling you something false with no indication of uncertainty, in a context where you might act on it.
So checks now resolve to one of four states rather than two:
- found, the account exists
- not-found, a real
404, the account does not exist - rate-limited, a
403or429, meaning we do not know - error, a network failure or unexpected shape, also meaning we do not know
The interface distinguishes them. A rate-limited platform reads "Rate limited, unknown", with the HTTP status available on hover, and it is visually amber rather than the muted grey of a real absence.
We also keep concurrency deliberately low. Firing eight requests at once is faster and converts more real answers into rate-limit noise, which is a bad trade when the whole point is accuracy.
Why this compounds
The distinction matters far more once results are stored. If a rate-limited check is recorded as an absence, then the next time you run the same sweep, the comparison reports that the profile disappeared. A transient HTTP 403 becomes a permanent, plausible-looking piece of false history.
So inconclusive results are never recorded as absences. A rate limit is preserved as a rate limit, and a not-found produces no stored finding at all, because "this handle was free on Tuesday" is rarely the fact you are trying to keep.
What a hit actually tells you
A username existing on a platform is weak evidence on its own. Handles collide, people abandon accounts, and squatters register names in bulk.
Corroborate before concluding:
Compare avatars and bios. The same profile picture across three services is much stronger than the same string.
Check the timeline. An account created in 2013 and one created last month, sharing a name, are probably different people.
Look for self-links. Profiles that reference each other are the strongest signal available, and they are free to check.
Privacy Note: Every check goes from your browser directly to the platform's public API. Utilora never receives the handle you searched, because there is no backend to receive it. Those platforms do see the request and your IP, which is the honest trade for having no middleman.
The wider point
Reconnaissance tools are trusted more than they deserve. A clean interface reporting "not registered" feels like a fact, and users rarely interrogate whether the tool could actually tell.
Distinguishing "no" from "I could not find out" costs a few lines of error handling and one extra colour in the UI. It is the difference between a tool that informs you and one that occasionally misleads you with total confidence.
Try these tools
Search for profiles associated with a username across developer and tech platforms. Runs 100% locally in your browser for absolute privacy.
Build advanced search queries (Google Dorks, Twitter/X dates, Wayback Machine archives) visually. 100% offline and private.
Check your whole password vault against known breaches without uploading a single password. Uses HIBP k-anonymity for total privacy.