How Holehe Checks if an Email Exists on Websites Without Sending Notifications

Holehe determines if an email address is registered on hundreds of online services by orchestrating concurrent, passive HTTP requests through site-specific async checkers that parse API responses and HTML without triggering email notifications.

Holehe is an open-source OSINT tool maintained by megadose/holehe that discovers where an email address is registered across the internet. Unlike services that send verification emails to the target address, this tool performs passive reconnaissance by analyzing how websites respond to authentication API endpoints and registration forms. The implementation relies on dynamically importing over 250 site-specific modules and executing them concurrently using Python's Trio async framework.

The Core Architecture of Holehe

The tool operates through a modular pipeline defined in holehe/core.py. This architecture separates the orchestration logic from the site-specific detection heuristics, allowing the framework to scale across hundreds of services.

Dynamic Module Discovery

Holehe begins by automatically discovering available checkers without hardcoding site lists. The import_submodules("holehe.modules") function traverses the holehe/modules directory tree and imports every Python file implementing a site-specific async function.

According to the source code in holehe/core.py:37-48, this dynamic loading mechanism ensures that new sites added to the modules folder are immediately available without modifying the core engine. Once imported, get_functions() (lines 50-63) extracts the callable objects and stores them in a list called websites, preparing them for concurrent execution.

Concurrent Execution with Trio

To check hundreds of sites rapidly without blocking, Holehe uses Trio for structured concurrency. The system creates a single httpx.AsyncClient instance with configurable timeouts (as implemented in holehe/core.py:11-14), enabling connection pooling and TLS session reuse across all requests.

A Trio nursery spawns a launch_module task for each site checker (holehe/core.py:24-30). Each task invokes the site-specific async function with three arguments: the target email, the shared HTTP client, and a thread-safe results list (out).

Site-Specific Detection Logic

Every module follows the standardized signature async def <site>(email, client, out). Inside each function, the checker:

  1. Sends one or more HTTP requests (GET/POST) to the target service's registration or authentication endpoints
  2. Parses the response (JSON, HTML, cookies, or status codes)
  3. Determines one of three outcomes: exists = True (email taken), exists = False (email available), or rateLimit = True (request throttled)

The result dictionary is appended to the shared out list for final aggregation.

How Individual Modules Detect Email Registration

To understand how Holehe checks if an email exists without sending notifications, examine the Instagram implementation in holehe/modules/social_media/instagram.py. This module demonstrates the passive reconnaissance technique used across all 250+ checkers.

Extraction Logic and CSRF Handling

The Instagram checker first retrieves a CSRF token via a GET request to the signup page. It then sends a POST request to accounts/web_create_ajax/attempt/ with a random username and the target email address. This endpoint is used during Instagram's registration flow to validate availability before account creation, meaning the target receives no email notification.

Response Classification

The module inspects the JSON response for specific error strings. According to the source code, if check["status"] != "fail" and the error payload contains "email_is_taken" or "email_sharing_limit", the checker records exists=True. If the response indicates the email is available or returns a different error structure, it records exists=False. Network failures or explicit rate-limit responses trigger rateLimit=True.

This pattern—querying registration validation endpoints and parsing error messages—repeats across all modules, from mails/google.py to forum/thevapingforum.py, ensuring the target email address never receives an actual notification while the tool determines registration status.

Usage Examples

Holehe supports both command-line operation and programmatic integration via its Python API.

Command Line Interface

Run Holehe against a single email address to check all supported sites:


# Check a single address against all supported sites

holehe email@example.com

# Show only sites where the email is registered

holehe email@example.com --only-used

# Export the full result set to CSV

holehe email@example.com --csv

Python API Integration

Import Holehe's core functions to embed email checking into custom workflows:

import asyncio
import httpx
from holehe.core import import_submodules, get_functions, launch_module

async def check_email(email: str):
    # Load all checkers dynamically

    modules = import_submodules("holehe.modules")
    sites = get_functions(modules)
    
    # Shared async client for connection pooling

    client = httpx.AsyncClient(timeout=10)
    results = []
    
    # Execute with controlled concurrency

    async with asyncio.Semaphore(20):
        for site in sites:
            await launch_module(site, email, client, results)
    
    await client.aclose()
    return results

# Example execution

if __name__ == "__main__":
    email = "test@example.com"
    res = asyncio.run(check_email(email))
    for r in res:
        print(r["domain"], "exists?", r["exists"])

Summary

  • Passive Detection: Holehe queries registration validation endpoints and parses responses (JSON, HTML, cookies) to determine if an email exists without sending actual emails or notifications to the target.
  • Modular Architecture: The tool dynamically imports 250+ site-specific checkers from holehe/modules using import_submodules() and get_functions() in holehe/core.py.
  • Concurrent Execution: Uses Trio and a shared httpx.AsyncClient to run launch_module tasks in parallel, maximizing speed while maintaining connection efficiency.
  • Standardized Interface: Every module implements async def <site>(email, client, out), returning structured results indicating exists, not found, or rateLimit states.
  • Flexible Deployment: Supports CLI usage with CSV export and programmatic integration via the core Python API.

Frequently Asked Questions

Does Holehe send emails or notifications to the target address?

No. Holehe performs passive reconnaissance by sending HTTP requests to registration and authentication endpoints (such as Instagram's accounts/web_create_ajax/attempt/). It analyzes the server's response—looking for strings like "email_is_taken"—to infer registration status. The tool never completes account creation or triggers email verification flows.

How does Holehe avoid triggering rate limits?

Each site-specific module includes logic to detect rate limiting (returning rateLimit=True when services return HTTP 429 status codes or specific error messages). The core engine uses a shared httpx.AsyncClient with configurable timeouts and respects the concurrency limits of the underlying Trio nursery. Users can also implement asyncio.Semaphore when using the Python API to further throttle requests.

Can I add custom websites to Holehe?

Yes. Create a new Python file in the appropriate subdirectory under holehe/modules/ implementing the standard signature async def <sitename>(email, client, out). The import_submodules("holehe.modules") function in holehe/core.py automatically discovers and loads your module on the next execution without requiring changes to the core engine.

What is the difference between Holehe and traditional email verification services?

Traditional email verification often uses SMTP handshakes or sends confirmation emails, which can alert the target or be blocked by mail servers. Holehe instead uses web-based enumeration, querying the same APIs that websites use to check username/email availability during signup. This method, implemented across 250+ services in the megadose/holehe repository, discovers account correlations without network noise in SMTP logs.

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 →