# Required Signature for a Holehe Site Module: Complete Implementation Guide

> Implement Holehe site modules with this guide. Learn the required signature: an async function with email, client, and out parameters, returning None and appending results to the out list.

- Repository: [Palenath/holehe](https://github.com/megadose/holehe)
- Tags: how-to-guide
- Published: 2026-08-24

---

**Every Holehe site module must expose a single asynchronous function accepting three parameters—`email`, `client`, and `out`—and return `None` while appending a standardized result dictionary to the mutable `out` list.**

Holehe is an open-source email OSINT framework that verifies account existence across hundreds of platforms. To extend its coverage, developers create site modules that plug into the core engine via a strict asynchronous interface. Understanding the required signature for a Holehe site module ensures your code integrates seamlessly with the tool's concurrent execution and result aggregation systems.

## The Standardized Asynchronous Signature

Each module in the `holehe/modules/` directory must implement a function conforming to this exact signature:

```python
async def <module_name>(email: str, client: httpx.AsyncClient, out: list) -> None

```

This signature enables the orchestration logic in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) to uniformly discover and invoke modules using reflection. The core's `launch_module` function calls each module with `await module(email, client, out)`, passing a shared HTTP client and a mutable list for result collection.

### Parameter Breakdown

- **`email`** (`str`): The target email address to investigate.
- **`client`** (`httpx.AsyncClient`): A pre-configured asynchronous HTTP client supplied by the core. Modules must use this instance for all requests to ensure proper timeout handling and connection pooling.
- **`out`** (`list`): A mutable list passed by reference. The module must append a result dictionary to this list; the core aggregates these entries after all modules complete.

## Required Result Dictionary Structure

When appending to `out`, modules must provide a dictionary containing these mandatory keys:

- `name`: Internal identifier (typically matches the function name)
- `domain`: Target domain (e.g., `"twitter.com"`)
- `method`: Verification approach (`"register"` or `"login"`)
- `frequent_rate_limit`: Boolean indicating if the service aggressively rate-limits
- `rateLimit`: Boolean indicating if the current request encountered rate limiting
- `exists`: Boolean indicating if the email is registered on the service
- `emailrecovery`: Optional string containing a password-recovery hint
- `phoneNumber`: Optional string with a discovered phone number
- `others`: Optional dictionary containing additional metadata (e.g., full name, account creation date)

## Real-World Implementation Example

The Twitter module at [`holehe/modules/social_media/twitter.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/social_media/twitter.py) demonstrates the canonical implementation:

```python
from holehe.core import *
from holehe.localuseragent import *

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

    try:
        req = await client.get(
            "https://api.twitter.com/i/users/email_available.json",
            params={"email": email})
        if req.json()["taken"]:
            out.append({
                "name": name,
                "domain": domain,
                "method": method,
                "frequent_rate_limit": frequent_rate_limit,
                "rateLimit": False,
                "exists": True,
                "emailrecovery": None,
                "phoneNumber": None,
                "others": None,
            })
    except Exception:
        raise

```

The function never returns a value; it exclusively mutates `out` via `append`. The core's `launch_module` wrapper catches exceptions and injects standardized error entries, so modules can propagate errors without handling aggregation logic.

## Creating a Minimal Custom Module

For a fictional service, implement the same pattern:

```python

# 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

    resp = await client.head(f"https://api.example.com/users/{email}")
    exists = resp.status_code == 200

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

```

## How the Core Engine Invokes Modules

The [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) file orchestrates execution through several key mechanisms:

1. **Discovery**: The `import_submodules` function dynamically imports all Python files within the `holehe/modules` package.
2. **Extraction**: `get_functions` retrieves callable objects from imported modules using introspection.
3. **Invocation**: `launch_module` awaits each discovered function with the standard signature, ensuring the shared `httpx.AsyncClient` and result list propagate correctly.
4. **Concurrency**: The framework uses Trio nurseries to execute modules concurrently. The shared `out` list enables thread-safe result collection without complex synchronization.

This architecture explains why strict adherence to the signature is mandatory—any deviation breaks the reflection-based invocation chain in `launch_module`.

## Summary

- Site modules must expose an async function accepting `email`, `client`, and `out` parameters exactly as typed.
- Functions must return `None` and append standardized result dictionaries to the `out` list.
- Required dictionary keys include `name`, `domain`, `method`, `exists`, `rateLimit`, and `frequent_rate_limit`, with optional fields for `emailrecovery`, `phoneNumber`, and `others`.
- The core engine in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) handles module discovery, concurrent execution via Trio, and automatic error handling.
- Reference [`holehe/modules/social_media/twitter.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/social_media/twitter.py) for production-ready implementation patterns.

## Frequently Asked Questions

### What happens if my module doesn't append to the `out` list?

The module produces no visible output. Because `launch_module` in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) does not inspect return values, failing to append to `out` effectively silences the module—the core will not report results for that service.

### Can I use a different HTTP client instead of the provided `httpx.AsyncClient`?

No. Modules must use the injected `client` parameter to ensure consistent timeout configurations, connection pooling, and integration with the core's rate-limiting instrumentation. Using an independent client bypasses these protections and may trigger IP bans.

### How does the core handle exceptions in site modules?

The `launch_module` function wraps each invocation in a try-except block. When a module raises an exception, the core automatically appends an error entry to the `out` list and continues execution. This design allows other modules to proceed while preserving diagnostic information for the failed check.

### Is the `method` field limited to specific values?

While the core accepts any string, convention restricts this field to `"register"` for registration page probes and `"login"` for authentication endpoint checks. These standardized values help users interpret whether the verification used signup flows or login attempts.