A username enumeration tool for OSINT.
Most people reuse the same username across the internet without realizing how much that connects. ThirdEye checks a single username across dozens of platforms at once — the same technique used in OSINT investigations, background checks, and self-audits — and shows you exactly where that identity shows up.
Built with Streamlit. No accounts, no API keys required to get started, no data stored anywhere but your own machine.
You give it a username. It checks that username against every platform it knows about and sorts the results into four honest buckets:
| Status | Meaning |
|---|---|
| Found | Strong signal the account exists (confirmed via a real API, or the username shows up directly on the page) |
| Unsure | No clear signal either way — usually because the site shows a login wall to every visitor, real account or not (LinkedIn, Instagram, and friends do this by design) |
| Not found | A real 404, or a page that clearly says the account doesn't exist |
| Error | Blocked, rate-limited, or timed out — not a verdict, just noise |
That "Unsure" bucket matters. A lot of username-checker tools will confidently tell you "not found" on a site that actually just showed a login page to everyone — that's a false negative dressed up as a fact. ThirdEye would rather say "we don't know" than guess wrong.
Found and Unsure platforms get rendered as cards — logo, whatever profile data could be extracted (bio, follower counts, avatar, recent posts, etc.), and a link to the profile. Not-found and error results are listed underneath, with error messages written in plain English instead of raw status codes.
Two tiers, depending on the platform:
-
Dedicated check — for platforms with a file in
platforms/, the check goes through that file directly. These are usually backed by a real public API (GitHub's REST API, Reddit'sabout.json, Dev.to's API, Medium's RSS feed) or a known reliable endpoint, so the answer is trustworthy and often comes with real profile data — bio, follower counts, avatar, the works. -
Generic check — for everything else,
detection.pyfetches the page once and looks at the title, headings, and error-flavored elements for signals: does the username show up? Does it look like a templated "not found" page? Is it redirecting to a login wall? From that same single request, it also opportunistically pulls Open Graph meta tags (title/description/image) for a bit of extra detail — no second request needed.
Either way, nothing gets marked "found" just because a page returned HTTP 200. A 200 with no real signal is reported as "unsure," not guessed into a false positive.
Checks every platform in platform_list.py (currently 135, curated across dev, social, media, gaming, security, AI, crypto, and more). Platforms with a dedicated file get the reliable check; everything else falls back to the generic page-based check.
streamlit run app.pyOnly checks platforms that have a dedicated file in platforms/ — no generic fallback at all. Fewer platforms, but every result comes from a real API or known-reliable method. The platform count here is always exactly "however many files are in platforms/."
streamlit run app_lite.pygit clone <your-repo-url>
cd thirdeye
pip install -r requirements.txt
streamlit run app.py # or app_lite.pyThat's it — no API keys required. (Optional: set a GITHUB_TOKEN environment variable to raise GitHub's rate limit from 60 requests/hour to 5,000/hour.)
thirdeye/
├── app.py # Main app — full mode (135 platforms)
├── detection.py # Decides found/not_found/unsure/error for every platform
├── progress_ui.py # Runs the scan, drives the progress bar + rotating quotes
├── helper.py # Shared headers and small utility functions
├── card_renderer.py # Turns a result into a styled card (or a list entry)
├── platform_list.py # The master list: name, URL pattern, category, domain
│
├── platforms/ # One file per platform with a dedicated, reliable check
│ ├── github.py # via GitHub's public API
│ ├── X.py # via X endpoint
│ ├── devto.py # via Dev.to's public API
│ ├── medium.py # via each user's public RSS feed
│ ├── hackerrank.py # via HackerRank's frontend JSON endpoint
│ ├── facebook.py # best-effort (same story)
│ ├── .....
│
├── requirements.txt
└── .streamlit/
└── config.toml # theme config
This is the whole point of the platforms/ folder — it's a plugin system, not a config file you have to remember to update in five places.
Drop a new file in platforms/, following this shape:
# platforms/x.py
PLATFORM = "X (Twitter)"
CATEGORY = "social"
DOMAIN = "x.com"
URL_TEMPLATE = "https://x.com/{}"
def check(username, url=None, session=None):
"""Must return {"status": "found"|"not_found"|"unsure"|"error",
"detail": str or None,
"extra": dict or None}"""
...That's it. Both app.py and app_lite.py pick it up automatically the next time they run — no imports to add, no registry to update, nothing else to touch. If the platform has a real API, use it and return rich extra data. If it doesn't, platforms/_common.py has a best_effort_check() helper that handles the generic page-scraping pattern (soft-404 detection, login-wall detection, Open Graph extraction) so you're not rewriting that logic from scratch.
- A "Found" result means there's real signal the account exists — it doesn't mean the profile content shown is necessarily accurate or current.
- An "Unsure" result is not a weak "probably not" — it genuinely means the check couldn't confirm anything either way, most often because the platform shows the same page to every visitor whether they're logged in or not.
- This tool only ever looks at what's publicly visible without logging in. It doesn't bypass authentication, doesn't access private data, and doesn't try to defeat CAPTCHAs or anti-bot systems.
Built for OSINT research, personal footprint audits, and understanding your own exposure across the platforms you've signed up for over the years. Use it on your own accounts, or in the context of legitimate, authorized research. Don't use it to harass, stalk, or de-anonymize people without a legitimate reason to.