How to Add a New Website Module to holehe: Complete Developer Guide
holehe automatically discovers new website modules by dynamically importing all Python files in the holehe.modules package, requiring only a single async function with a specific signature to integrate a new service.
Adding a new website module to holehe extends the tool’s ability to investigate email addresses across additional platforms. The megadose/holehe repository leverages a dynamic import system in holehe/core.py that eliminates the need for manual registration or configuration files. By following the established pattern for async check functions and result formatting, you can integrate any website that exposes an email validation endpoint through registration or login forms.
How the Dynamic Module Discovery Works
The holehe CLI relies on a recursive package walker to find check functions at runtime. In holehe/core.py, the import_submodules() function (lines 37-47) traverses the holehe.modules directory tree and imports every Python file it encounters.
When you add a new file containing an async def <sitename>(email, client, out) function, the loader automatically registers it. The CLI then calls this function for each target email address, passing in an httpx.AsyncClient instance for HTTP requests and a list out to collect results. No additional configuration or registry edits are required—placement in the correct subdirectory is sufficient.
Step-by-Step Implementation Guide
1. Select the Appropriate Category
Modules are organized by theme within holehe/modules/. Choose the folder that best represents the target website:
social_media/for platforms like Instagram, Twitter, or Facebookprograming/for developer services like GitHub or Stack Overflowforum/for community discussion boardsshopping/for e-commerce sitesmail/for email providers
If no category fits, create a new folder inside holehe/modules/; the dynamic loader will recurse into it automatically.
2. Create the Module File
Create a new Python file named after the website (e.g., targetsite.py) inside your chosen category folder. For example:
holehe/modules/social_media/targetsite.py
3. Implement the Async Check Function
Every module must define an async function with the exact signature:
async def targetsite(email, client, out):
The parameters are:
email(str): The email address to investigateclient(httpx.AsyncClient): A shared async HTTP client with connection poolingout(list): A list to which you append result dictionaries
Inside the function, define these metadata variables before making HTTP requests:
name = "targetsite" # Internal identifier
domain = "targetsite.com" # Domain name
method = "register" # Usually "register" or "login"
frequent_rate_limit = False # Set True if site aggressively rate-limits
Required Data Structure and Result Formatting
Your function must append a dictionary to the out list with specific keys that the print_result() function expects. The structure is:
out.append({
"name": name, # String: module identifier
"domain": domain, # String: domain name
"method": method, # String: check method used
"frequent_rate_limit": frequent_rate_limit, # Boolean: rate limit tendency
"rateLimit": False, # Boolean: hit rate limit this run?
"exists": True, # Boolean: email found on site?
"emailrecovery": None, # String or None: recovery email if exposed
"phoneNumber": None, # String or None: phone number if exposed
"others": None # Dict or None: additional metadata
})
If the check encounters an unrecoverable error (e.g., missing CSRF token or service downtime), append a result with rateLimit: True or appropriate error state and return None early to halt further processing.
Complete Module Template
Copy this template to holehe/modules/<category>/yourservice.py and adapt the request logic:
# File: holehe/modules/social_media/yourservice.py
from holehe.core import *
from holehe.localuseragent import *
import random
async def yourservice(email, client, out):
name = "yourservice"
domain = "yourservice.com"
method = "register"
frequent_rate_limit = False
headers = {
"User-Agent": random.choice(ua["browsers"]["chrome"]),
"Accept": "application/json, text/plain, */*",
"Accept-Language": "en-US,en;q=0.5",
"Origin": f"https://{domain}",
"DNT": "1",
"Connection": "keep-alive",
}
# Example: fetch CSRF token if required by the site
try:
resp = await client.get(f"https://{domain}/signup", headers=headers)
token = resp.text.split('csrf_token":"')[1].split('"')[0]
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,
})
return None
# Submit email to validation endpoint
data = {"email": email, "username": "tmpuser"}
headers["x-csrftoken"] = token
check = await client.post(f"https://{domain}/api/check", data=data, headers=headers)
try:
result = check.json()
exists = result.get("error") == "email_taken"
except Exception:
exists = False
out.append({
"name": name,
"domain": domain,
"method": method,
"frequent_rate_limit": frequent_rate_limit,
"rateLimit": False,
"exists": exists,
"emailrecovery": None,
"phoneNumber": None,
"others": None,
})
Key Source Files for Reference
Understanding these core files helps ensure your module follows holehe conventions:
holehe/core.py– Contains theimport_submodules()function (lines 37-47) responsible for dynamic module discovery and the result printing logicholehe/modules/social_media/instagram.py– A production-ready reference showing header preparation, token extraction, error handling, and result formattingholehe/localuseragent.py– Provides theuadictionary containing browser user-agent strings; import this to rotate identities and avoid detection
Summary
- Dynamic discovery: Place any Python file under
holehe/modules/and theimport_submodules()loader inholehe/core.pyautomatically registers it - Required signature: Implement
async def sitename(email, client, out)with exact parameter names - Mandatory metadata: Define
name,domain,method, andfrequent_rate_limitvariables at the function start - Result contract: Append a dictionary to
outwith keysname,domain,method,frequent_rate_limit,rateLimit,exists,emailrecovery,phoneNumber, andothers - Error handling: Return
Noneearly after appending an error state tooutwhen encountering unrecoverable failures - HTTP client: Use the passed
httpx.AsyncClientfor all requests to enable connection reuse and proper async execution
Frequently Asked Questions
Do I need to manually register my new module in a configuration file?
No. According to the import_submodules() implementation in holehe/core.py, the tool walks the entire holehe.modules package tree at startup. As long as your file exists under holehe/modules/ (in any subfolder) and contains the properly named async function, holehe discovers it automatically.
What is the exact function signature required for a holehe module?
The function must be declared as async def <sitename>(email, client, out): where email is the string to check, client is an httpx.AsyncClient instance for HTTP requests, and out is a list where you append the result dictionary. The function name should match the filename and the name variable defined inside.
How should I handle websites that frequently rate-limit requests?
Set frequent_rate_limit = True in your module's metadata variables. This flag signals to users that the service aggressively limits requests. If you encounter a rate limit during the check, set "rateLimit": True in the result dictionary you append to out, and return None to stop further processing for that email.
Can I use custom HTTP headers or user agents in my module?
Yes. Import from holehe.localuseragent import * to access the ua dictionary, then select a random browser user agent with random.choice(ua["browsers"]["chrome"]). Construct your headers dictionary using standard browser headers to mimic legitimate traffic, and pass these headers to all client.get() or client.post() calls.
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 →