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, LinkedInforum— discussion boards and community sitessoftware— developer tools and SaaS platformsecommerce— online stores and marketplacesentertainment— 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_submodulessystem discovers your file automatically - Defensive coding required: Wrap requests in
try/exceptand setrateLimit: Trueon 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →