How to Add a New Website Module to Holehe: Step-by-Step Guide

Adding a website module to Holehe requires creating an async function with a specific signature in holehe/modules/ and populating a standard result dictionary—no registration step is needed.

Holehe uses a dynamic import system to discover and execute email checks across hundreds of websites. To extend its capabilities, you create a Python module in the appropriate category folder that follows the established pattern. This guide walks through the exact implementation details found in the megadose/holehe source code, from signature requirements to result formatting.

Understanding Holehe's Module Discovery System

Holehe discovers all available checks at runtime. In holehe/core.py, the function import_submodules("holehe.modules") recursively walks the package tree, imports every submodule, and extracts public functions using get_functions.

Because of this automatic discovery, no manual registration is required. Save your module file in the correct location and Holehe will include it in the next scan.

Step 1: Choose the Right Category Folder

Website modules are organized by category under holehe/modules/. Common categories include:

  • social_media — platforms like Twitter, Instagram, LinkedIn
  • forum — discussion boards and community sites
  • software — developer tools and SaaS platforms
  • ecommerce — online stores and marketplaces
  • entertainment — streaming and gaming services

Place your new module in the most appropriate existing folder, or create a new category if none fit.

Step 2: Create the Module File with Required Function Signature

Every Holehe module defines a single async function with this exact signature:

async def <site_name>(email, client, out):

The parameters are:

Parameter Type Purpose
email str The target email address to check
client httpx.AsyncClient Shared async HTTP client provided by the core
out list Result accumulator—append your dictionary here

Each site-specific check is a coroutine that performs one HTTP request (or sequence) to determine if the email is registered.

Step 3: Populate the Standard Result Dictionary

Every module appends a dictionary to out with exactly these keys:

out.append({
    "name": name,                      # Internal identifier (match function name)

    "domain": domain,                  # Human-readable domain, e.g., "example.com"

    "method": method,                  # Usually "register"

    "frequent_rate_limit": False,      # True if site often rate-limits

    "rateLimit": False,                # Set True when rate-limit detected in this request

    "exists": exists,                  # Boolean: True if email is registered

    "emailrecovery": None,             # Optional recovered email address

    "phoneNumber": None,               # Optional recovered phone number

    "others": None                     # Additional metadata (e.g., full name)

})

Critical: Use None for optional fields rather than omitting them. The core expects this exact structure.

Step 4: Implement Error Handling

Wrap your request logic in a generic exception handler. When errors occur, append a result with rateLimit: True (or follow the launch_module pattern with error: True):

try:
    # Your request logic here

    ...
except Exception:
    out.append({
        "name": name,
        "domain": domain,
        "method": method,
        "frequent_rate_limit": frequent_rate_limit,
        "rateLimit": True,              # Mark as rate-limited/errored

        "exists": False,
        "emailrecovery": None,
        "phoneNumber": None,
        "others": None,
    })

This ensures Holehe continues checking other sites even when one fails.

Complete Example: Adding a Module for example.com

Here's a production-ready template based on patterns from holehe/modules/social_media/twitter.py:


# File: holehe/modules/social_media/example.py

from holehe.core import *
from holehe.localuseragent import *

async def example(email, client, out):
    name = "example"
    domain = "example.com"
    method = "register"
    frequent_rate_limit = False

    try:
        # Replace with actual endpoint for checking email availability

        req = await client.get(
            "https://api.example.com/v1/users/email_available",
            params={"email": email},
            headers={"User-Agent": ua},
            timeout=5
        )
        
        # Parse response—logic varies by service

        data = req.json()
        exists = not data.get("available", True)

        out.append({
            "name": name,
            "domain": domain,
            "method": method,
            "frequent_rate_limit": frequent_rate_limit,
            "rateLimit": False,
            "exists": exists,
            "emailrecovery": None,
            "phoneNumber": None,
            "others": None,
        })
        
    except Exception:
        out.append({
            "name": name,
            "domain": domain,
            "method": method,
            "frequent_rate_limit": frequent_rate_limit,
            "rateLimit": True,
            "exists": False,
            "emailrecovery": None,
            "phoneNumber": None,
            "others": None,
        })

Save this file and run Holehe immediately:

holehe target@example.com

Your new check appears automatically in the output.

Optional: Update __init__.py for Clean Imports

Most category folders expose an __all__ list in their __init__.py. Adding your module name here enables direct imports:


# holehe/modules/social_media/__init__.py

__all__ = ["twitter", "instagram", "example"]  # Add your module

This step is optional—the dynamic importer works regardless.

Key Source Files to Reference

File Purpose
holehe/core.py Contains import_submodules() and orchestration logic
holehe/modules/social_media/twitter.py Canonical example of a well-implemented module
holehe/localuseragent.py Provides ua constant for request headers

Study existing modules in your target category to understand site-specific detection patterns. Each service exposes email registration status differently—some through registration forms, others through password reset flows or API endpoints.

Summary

  • Location matters: Place modules in holehe/modules/<category>/
  • Signature is fixed: async def name(email, client, out)
  • Structure is strict: Use the exact result dictionary format with all 9 keys
  • No registration needed: The import_submodules system discovers your file automatically
  • Defensive coding required: Wrap requests in try/except and set rateLimit: True on failures

Frequently Asked Questions

Do I need to restart Holehe after adding a module?

No. Holehe performs module discovery on every execution via import_submodules("holehe.modules") in holehe/core.py. Save your file and run holehe immediately—the new check is automatically included.

Can I add multiple website checks in one file?

Technically yes, but conventionally no. Each .py file in holehe/modules/ typically contains one site function. This keeps the codebase organized and makes debugging easier. If you need multiple related checks, consider whether they belong as separate modules or in a shared utility file.

How do I determine if an email is registered on a new website?

Every service exposes this differently. Common techniques include:

  • Registration availability APIs — endpoints that return available: true/false
  • Password reset flows — submitting the email and parsing error messages
  • Login form responses — distinguishing "account not found" from "wrong password"
  • Profile lookup endpoints — checking if a public profile exists for that email

Study the target site's network traffic during legitimate use to identify the simplest detection method.

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 →