# Holehe Module Signature: Structure for Asynchronous Email OSINT Checkers

> Discover the Holehe module signature: learn how to create asynchronous email OSINT checkers by defining your module as an async function with specific parameters and return values for seamless integration.

- Repository: [Palenath/holehe](https://github.com/megadose/holehe)
- Tags: api-reference
- Published: 2026-08-26

---

**Every Holehe module must be an asynchronous function defined as `async def module_name(email, client, out)` that accepts an email string, an `httpx.AsyncClient` instance, and a mutable results list, returning `None` while appending a standardized result dictionary to the `out` parameter.**

The megadose/holehe repository provides a modular framework for checking email existence across hundreds of online services. To maintain compatibility with the core execution engine, every module must implement a specific asynchronous function signature. Understanding the **signature for a Holehe module** is essential for contributors adding new OSINT sources or customizing existing checks.

## The Required Function Signature

The core engine located in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) expects a strict interface that enables uniform invocation across all services. The signature for a Holehe module is:

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

```

This convention ensures that the orchestration layer can dynamically load and execute any module without service-specific logic.

### Parameter Breakdown

- **email** (`str`): The target email address being investigated across the target platform.

- **client** (`httpx.AsyncClient`): A pre-configured asynchronous HTTP client that must be used for all network requests. Reusing this client enables connection pooling and ensures uniform timeout and header configuration across the framework.

- **out** (`list`): A mutable list supplied by the core engine that acts as a collector for results. The module must append exactly one dictionary describing the check outcome to this list rather than returning values directly.

## Result Dictionary Structure

When appending to the `out` list, the module must provide a dictionary containing standardized keys that the core engine interprets. According to implementations in [`holehe/modules/shopping/amazon.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/shopping/amazon.py) and [`holehe/modules/social_media/facebook.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/social_media/facebook.py), the required structure includes:

- **name**: Service identifier string (e.g., `"amazon"`, `"google"`)
- **domain**: The service's primary domain (e.g., `"amazon.com"`, `"gmail.com"`)
- **method**: The verification methodology used, typically `"login"` or `"register"`
- **frequent_rate_limit**: Boolean indicating whether the service commonly imposes rate limiting
- **rateLimit**: Boolean indicating whether the current request specifically hit a rate limit
- **exists**: Boolean indicating whether the email address exists on the target platform
- **emailrecovery**: String containing recovery email if exposed by the service, otherwise `None`
- **phoneNumber**: String containing phone number if exposed by the service, otherwise `None`
- **others**: Dictionary or `None` containing additional metadata or alternative identifiers

## Implementation Examples from the Codebase

### Amazon Module (holehe/modules/shopping/amazon.py)

Real-world implementations follow the signature strictly. In [`holehe/modules/shopping/amazon.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/shopping/amazon.py), the function accepts the three required parameters and uses the injected client for requests:

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

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

    headers = {"User-agent": random.choice(ua["browsers"]["chrome"])}
    try:
        # ... HTTP logic using client ...

        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:
        out.append({
            "name": name,
            "domain": domain,
            "method": method,
            "frequent_rate_limit": frequent_rate_limit,
            "rateLimit": True,
            "exists": False,
            "emailrecovery": None,
            "phoneNumber": None,
            "others": None,
        })

```

Similar patterns appear in [`holehe/modules/mails/google.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/mails/google.py) and [`holehe/modules/social_media/facebook.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/social_media/facebook.py), where each module defines an async function with the identical parameter structure.

### Minimal Module Template

For developers creating new services, the minimal compliant structure follows this pattern:

```python
from holehe.localuseragent import ua
import random

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

    # Always use the provided client for HTTP requests

    # result = await client.get(...)

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

```

## Core Engine Integration

The [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) file orchestrates module execution by importing each module dynamically and invoking its function with the three required arguments. The engine initializes a single `httpx.AsyncClient` instance and passes it to every module, ensuring efficient connection reuse across hundreds of concurrent checks. By standardizing on the **async function signature** `(email, client, out)`, the framework decouples the execution logic from individual service implementations, allowing contributors to add new sources without modifying core orchestration code.

## Summary

- Every Holehe module must define an **asynchronous function** with the exact signature `async def module_name(email, client, out)`.
- The function must accept three parameters: the target **email string**, an **httpx.AsyncClient** for HTTP requests, and a mutable **results list**.
- Modules must **append a dictionary** to the `out` list rather than returning values, using keys like `name`, `domain`, `exists`, and `rateLimit`.
- Real implementations in [`holehe/modules/shopping/amazon.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/shopping/amazon.py) and [`holehe/modules/mails/google.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/mails/google.py) demonstrate proper client reuse and error handling patterns.
- The core engine in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) relies on this contract to execute hundreds of modules uniformly without service-specific logic.

## Frequently Asked Questions

### What happens if a Holehe module returns a value instead of appending to out?

The core engine ignores return values from module functions. If a module returns a dictionary instead of appending it to the `out` list, the results will be lost and the check will appear to produce no output. Always append results to the `out` parameter to ensure the core receives the data.

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

No. The `client` parameter provides a pre-configured `httpx.AsyncClient` that enables connection pooling and respects global timeout settings defined in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py). Creating separate client instances within modules would reduce performance and potentially trigger additional rate limiting on target services.

### What is the difference between frequent_rate_limit and rateLimit in the result dictionary?

The `frequent_rate_limit` key indicates that the service generally imposes strict rate limiting (a static property of the platform), while `rateLimit` specifically indicates that the current request was blocked or throttled. Set `frequent_rate_limit` to `True` for services known to aggressively limit requests, and set `rateLimit` to `True` only when the current check encounters a rate limit response.

### Where should I place new modules in the Holehe repository?

Place new modules in the appropriate subdirectory under `holehe/modules/` based on the service category (e.g., `holehe/modules/shopping/` for e-commerce sites, `holehe/modules/social_media/` for social platforms). The core engine automatically discovers and imports Python files from these directories, provided they implement the correct function signature.