What Is Holehe? A Deep Dive Into the Open-Source OSINT Email Investigation Tool
Holehe is a Python-based open-source OSINT utility that checks whether an email address is registered on more than 120 online services by querying their password-recovery or registration endpoints.
Holehe excels at discovering digital footprints tied to email addresses without requiring authentication or API keys. Investigators, security researchers, and bug bounty hunters use it to map account associations across social media, software platforms, forums, and payment services. The tool operates entirely through asynchronous HTTP requests, making it fast and scalable for batch operations.
How Holehe Works Under the Hood
The tool's architecture prioritizes extensibility, speed, and stealth. Rather than scraping profile pages, each service module exploits password-reset flows or registration validation endpoints—techniques that typically don't trigger rate limits or authentication barriers.
Core Engine in holehe/core.py
The central orchestration happens in holehe/core.py. The main() function serves as the CLI entry point, immediately delegating to trio.run(maincore) on line 32-34. This establishes the asynchronous runtime before any network activity begins.
The maincore() function handles argument parsing through argparse.ArgumentParser (lines 80-96), supporting flags like:
--only-used— Filter to registered accounts only--no-color— Disable ANSI color codes--csv— Export results to CSV format--timeout— Configure per-request timeouts
Dynamic Module Discovery System
Holehe's plugin architecture allows new services to be added without modifying core code. The import_submodules() function (lines 37-47) walks the holehe/modules/* package tree using pkgutil, while get_functions() (lines 50-63) filters imported objects into callable "website" functions matching the expected async signature.
# From holehe/core.py - dynamic module loading
modules = import_submodules("holehe.modules")
websites = get_functions(modules) # Returns list of async service checkers
Concurrent Execution with Trio and HTTPX
Performance comes from structured concurrency via the Trio library and httpx.AsyncClient. The launch_module() function (lines 66-71) spawns each service check in a nursery, enabling parallel execution across dozens of endpoints.
A custom TrioProgress instrument (defined in holehe/instruments.py) attaches to the runtime to render a live progress bar, then detaches after completion.
What Information Holehe Extracts
Each service module returns a standardized dictionary with these fields:
name— Human-readable service identifierdomain— Base domain of the serviceexists— Boolean indicating account registrationemailrecovery— Partially obfuscated recovery email (if exposed)phoneNumber— Masked recovery phone (if exposed)others— Additional metadata per service
The print_result() function (lines 22-49) aggregates these into color-coded terminal output, with export_csv() (lines 54-64) handling structured data export when requested.
Practical Usage Examples
Command-Line Scanning
# Basic scan against all supported services
holehe target@example.com
# Show only confirmed registrations
holehe target@example.com --only-used
# Export to CSV for further analysis
holehe target@example.com --csv results.csv
# Disable colors for piping to other tools
holehe target@example.com --no-color
Single Service Module (Programmatic)
import trio
import httpx
from holehe.modules.social_media.instagram import instagram
async def check_instagram():
email = "target@example.com"
out = []
async with httpx.AsyncClient() as client:
await instagram(email, client, out)
# out contains: [{'name': 'instagram', 'domain': 'instagram.com',
# 'exists': True/False, 'emailrecovery': 't***@gmail.com', ...}]
return out
result = trio.run(check_instagram)
Bulk Custom Scanning
import trio
import httpx
from holehe.core import import_submodules, get_functions, launch_module
async def comprehensive_scan(email: str):
"""Scan all discovered modules concurrently."""
modules = import_submodules("holehe.modules")
websites = get_functions(modules)
client = httpx.AsyncClient()
results = []
async with trio.open_nursery() as nursery:
for site_func in websites:
nursery.start_soon(launch_module, site_func, email, client, results)
await client.aclose()
return [r for r in results if r.get("exists")] # Filter to hits only
# Execute
matches = trio.run(lambda: comprehensive_scan("target@example.com"))
print(f"Found {len(matches)} registered accounts")
Anti-Detection Measures
Holehe implements several evasion techniques:
| Mechanism | Implementation | Source |
|---|---|---|
| Rotating User-Agents | Randomized browser strings from holehe/localuseragent.py |
holehe/core.py lines 21-22 |
| Async I/O | Non-blocking requests prevent sequential timing patterns | Trio + HTTPX throughout |
| Timeout Configuration | Per-request limits avoid hanging on slow endpoints | --timeout flag |
The localuseragent.py module generates plausible browser fingerprints, while the asynchronous design distributes traffic patterns across time windows rather than burst sequences.
Extending Holehe with New Services
Creating a new module requires only three elements:
- A Python file in the appropriate
holehe/modules/subdirectory - An async function following the signature:
async def servicename(email, client, out) - Appending a result dictionary to the
outlist
Example module structure from holehe/modules/software/adobe.py:
import httpx
async def adobe(email: str, client: httpx.AsyncClient, out: list):
try:
# Service-specific password-recovery request
response = await client.post(
"https://auth.services.adobe.com/signin/v2",
data={"username": email}
)
# Parse response for account indicators
exists = "password" in response.text # Simplified logic
out.append({
"name": "adobe",
"domain": "adobe.com",
"exists": exists,
"emailrecovery": None,
"phoneNumber": None,
"others": {}
})
except Exception:
out.append({
"name": "adobe",
"domain": "adobe.com",
"exists": None, # Error state
"emailrecovery": None,
"phoneNumber": None,
"others": {}
})
Self-Update Mechanism
The check_update() function (lines 65-85 in an earlier version, consolidated in current releases) queries the PyPI JSON API for version comparison. When outdated, it executes pip install --upgrade holehe automatically—ensuring investigators access the latest service modules without manual intervention.
Summary
- Holehe is an async Python OSINT tool that maps email addresses to accounts across 120+ services via password-recovery endpoint analysis
- Core architecture in
holehe/core.pyuses Trio for structured concurrency and HTTPX for HTTP/2-capable async requests - Dynamic module loading via
pkgutilenables plugin-style service additions without core modifications - Standardized output format supports both human-readable terminal display and CSV export for pipeline integration
- Built-in stealth features include rotating User-Agents and configurable timeouts
- GPL-3.0 licensed for educational and authorized security research use
Frequently Asked Questions
How does Holehe differ from commercial OSINT email tools?
Holehe operates without API keys, rate-limited subscriptions, or authenticated access. It exploits publicly accessible password-recovery flows that return account-existence indicators—making it free and unrestricted, though potentially more fragile to endpoint changes than paid services with official APIs.
Can Holehe access private account information or passwords?
No. Holehe only determines whether an account exists and may recover partially masked recovery contacts (like t***@gmail.com). It cannot retrieve passwords, private messages, or profile content. The tool respects rate limits and is designed for passive reconnaissance.
Why does Holehe use Trio instead of asyncio?
Trio provides structured concurrency guarantees that prevent common async bugs like orphaned tasks or uncaught exceptions. The trio.open_nursery() pattern ensures all service checks complete or fail cleanly, while the instrument system enables the progress bar without callback complexity.
How do I add support for a new website to Holehe?
Create a Python file in the appropriate holehe/modules/ category folder with an async function accepting (email, client, out) parameters. The function should append a result dictionary to out and handle its own exception catching. No registration with core code is required—discovery happens automatically at runtime.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →