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 identifier
  • domain — Base domain of the service
  • exists — Boolean indicating account registration
  • emailrecovery — 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:

  1. A Python file in the appropriate holehe/modules/ subdirectory
  2. An async function following the signature: async def servicename(email, client, out)
  3. Appending a result dictionary to the out list

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.py uses Trio for structured concurrency and HTTPX for HTTP/2-capable async requests
  • Dynamic module loading via pkgutil enables 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →