Where to Place Custom Modules in Holehe: Complete Directory Structure Guide

Custom modules must be placed inside the holehe/modules/ directory tree, either within existing category folders like social_media/ or shopping/, or within new subdirectories you create, as Holehe automatically discovers all Python files under this package via the import_submodules function in holehe/core.py.

Holehe is an email reconnaissance tool developed by megadose that checks email usage across hundreds of platforms. When extending its capabilities with custom modules, understanding the precise directory structure and discovery mechanism ensures your integrations execute automatically without manual registration.

How Holehe Discovers Custom Modules

Holehe uses dynamic package importing to load every module at runtime. In holehe/core.py at lines 36-47, the import_submodules("holehe.modules") helper walks the holehe.modules package's __path__ and imports each .py file it encounters. This means any Python file placed within the holehe/modules/ hierarchy becomes available immediately upon the next execution—no edits to core.py or registry files are necessary.

The Correct Directory Structure for Custom Modules

All custom modules belong under the holehe/modules/ directory. The repository organizes services into logical categories, and your additions should follow this convention:


holehe/
└─ modules/
   ├─ social_media/
   │   └─ your_service.py
   ├─ shopping/
   │   └─ your_shop.py
   ├─ sport/
   │   └─ your_sport.py
   ├─ custom/              # New category (requires __init__.py)

   │   ├─ __init__.py
   │   └─ foobar.py
   └─ __init__.py

Existing categories include social_media, shopping, sport, music, and others listed in the repository's README. Select the folder that best matches your service type. If none fit, create a new subpackage folder (e.g., holehe/modules/custom/) and include an empty __init__.py file so Python recognizes it as a package.

Required Function Signature and Output Format

Every custom module must expose a single asynchronous function matching the filename. The function signature must be:

async def module_name(email: str, client, out: list):

The function should append a dictionary to the out list containing exactly these keys:

  • name: Service identifier string
  • domain: Associated domain (e.g., "example.com")
  • rateLimit: Boolean indicating if rate limited
  • error: Boolean indicating if an error occurred
  • exists: Boolean indicating if account exists
  • emailrecovery: String with masked recovery email or None
  • phoneNumber: String with phone number or None
  • others: Dictionary with additional metadata or None

Step-by-Step Implementation Examples

Adding a Module to an Existing Category

To add a new social media checker, create holehe/modules/social_media/myservice.py:


# holehe/modules/social_media/myservice.py

import json

async def myservice(email: str, client, out: list):
    """
    Check if email exists on MyService.
    """
    # Replace with actual HTTP logic using client

    out.append({
        "name": "myservice",
        "domain": "myservice.com",
        "rateLimit": False,
        "error": False,
        "exists": False,
        "emailrecovery": None,
        "phoneNumber": None,
        "others": None,
    })

Holehe will automatically import myservice the next time you run the tool.

Creating a New Category Folder

For services that do not fit existing categories, create a new subpackage:

mkdir -p holehe/modules/custom
touch holehe/modules/custom/__init__.py

Then add your module at holehe/modules/custom/foobar.py:


# holehe/modules/custom/foobar.py

async def foobar(email: str, client, out: list):
    out.append({
        "name": "foobar",
        "domain": "foobar.example",
        "rateLimit": False,
        "error": False,
        "exists": True,
        "emailrecovery": "re***@example.com",
        "phoneNumber": None,
        "others": {"info": "Custom metadata"},
    })

Because import_submodules recursively walks subdirectories, the new custom package and its foobar module will be discovered automatically.

Summary

  • Place custom modules inside holehe/modules/ or its subdirectories (e.g., holehe/modules/social_media/).
  • Use existing category folders when possible; create new folders with __init__.py for novel categories.
  • Implement an async def function with the exact signature (email, client, out) and append the required dictionary format to out.
  • No registration is required—the dynamic importer in holehe/core.py (lines 36-47) discovers modules automatically.
  • Reference existing modules like holehe/modules/social_media/twitter.py for production-ready implementation templates.

Frequently Asked Questions

Do I need to register my custom module in a configuration file?

No. According to the megadose/holehe source code, the import_submodules function in holehe/core.py automatically imports every Python file under holehe/modules at runtime. Simply placing your .py file in the correct directory is sufficient.

Can I organize custom modules in nested subdirectories?

Yes. You can create subpackages (folders containing __init__.py) within holehe/modules/, and Holehe will recursively discover modules inside them. This allows logical grouping such as holehe/modules/enterprise/servicenow.py.

What happens if my custom module function raises an exception?

Holehe handles exceptions internally. However, your module should set the error key to True in the output dictionary when catching exceptions, and ideally log the failure details in the others field to maintain consistent reporting across all checks.

Are there naming conventions for custom module files?

The filename should match the function name exactly (e.g., twitter.py contains async def twitter). Use lowercase with underscores for readability. Avoid naming conflicts with existing modules in the holehe/modules/ tree to prevent import collisions.

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 →