# Understanding the Standard Signature for a Holehe Module

> Learn the standard Holehe module signature: an async function with email, client, and out arguments. Discover how to append standardized results efficiently.

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

---

**Every Holehe module must implement an asynchronous function that accepts exactly three arguments—`email`, `client`, and `out`—and appends a standardized result dictionary to the mutable `out` list.**

The megadose/holehe repository implements a modular email reconnaissance framework where each service-specific checker follows a strict callable contract. Understanding the standard signature for a Holehe module is essential for contributing new services or invoking existing checks programmatically. This uniformity enables the core engine in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) to orchestrate concurrent requests across dozens of platforms using a single execution pattern.

## The Asynchronous Three-Parameter Contract

Each module in the `holehe/modules/` directory defines an **asynchronous function** with a fixed three-parameter signature. The orchestrator `run_modules` relies on this consistency to invoke every service checker uniformly without conditional logic for individual services.

The signature follows this exact pattern:

```python
async def <module_name>(email, client, out):

```

### The `email` Parameter

The first argument receives the **target email address** as a string. Modules use this value to construct probe requests that determine whether the address is registered on the specific third-party service.

### The `client` Parameter

The second argument expects an **asynchronous HTTP client**, specifically an `httpx.AsyncClient` instance. Modules leverage this client to perform non-blocking web requests, as demonstrated in both [`holehe/modules/transport/blablacar.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/transport/blablacar.py) and [`holehe/modules/software/office365.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/software/office365.py).

### The `out` Parameter

The third argument is a **mutable Python list** passed by reference. Rather than returning values directly via `return`, modules append a single dictionary describing the check outcome to this list. This callback-style pattern allows the core engine to aggregate results from hundreds of concurrent module executions efficiently.

## The Standardized Result Dictionary Schema

Every module must construct a result dictionary with specific mandatory keys. The structure ensures [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) can parse outcomes consistently across disparate services with varying response formats.

Required keys include:

- `name` — The module's internal identifier string (e.g., `blablacar`)
- `domain` — The target domain being probed (e.g., `blablacar.com`)
- `method` — The interaction type (`register`, `login`, or `other`)
- `frequent_rate_limit` — Boolean indicating if the service aggressively rate-limits requests
- `rateLimit` — Boolean set to `True` if the current request was throttled
- `exists` — Boolean indicating whether the email is registered on the service

Optional keys provide additional context:

- `emailrecovery` — A recovered secondary email address if exposed by the service response
- `phoneNumber` — An associated phone number if leaked by the platform
- `others` — A dictionary container for service-specific metadata

## Practical Implementation Examples

The following examples demonstrate how the signature operates in production code from the megadose/holehe repository.

### Direct Module Invocation

To call a specific module such as **Blablacar** directly, instantiate an HTTP client and prepare the mutable output list:

```python
import asyncio
import httpx
from holehe.modules.transport.blablacar import blablacar

async def check_blablacar(email: str) -> dict:
    async with httpx.AsyncClient() as client:
        out = []
        await blablacar(email, client, out)
        return out[0]  # Result dict for the service

result = asyncio.run(check_blablacar("example@example.com"))
print(result)

```

This pattern matches the implementation in [`holehe/modules/transport/blablacar.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/transport/blablacar.py), where the function appends the standardized dictionary to `out` without returning a value.

### Core Orchestration Usage

The `run_modules` function in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) automates the process across all registered modules:

```python
import asyncio
import httpx
from holehe.core import run_modules  # Core runner that iterates all modules

async def scan_email(email: str):
    async with httpx.AsyncClient() as client:
        results = await run_modules(email, client)  # Internally calls each module

        for r in results:
            print(f"{r['name']}: exists={r['exists']}")

asyncio.run(scan_email("example@example.com"))

```

## Module Discovery and Registration

The framework discovers available modules through [`holehe/modules/__init__.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/__init__.py), which registers all compliant functions. When contributing a new service, placing the async function with the standard three-parameter signature in the appropriate subdirectory ensures automatic inclusion in the scanning pipeline.

Additionally, [`holehe/localuseragent.py`](https://github.com/megadose/holehe/blob/main/holehe/localuseragent.py) provides the `ua` dictionary for random User-Agent selection, which modules frequently access when configuring HTTP headers via the provided `client` instance.

## Summary

- Every Holehe module must implement an **asynchronous function** accepting exactly three parameters: `email`, `client`, and `out`.
- The function must append a **standardized result dictionary** to the mutable `out` list rather than returning data directly.
- Required result keys include `name`, `domain`, `method`, `frequent_rate_limit`, `rateLimit`, and `exists`.
- The `client` parameter expects an `httpx.AsyncClient` instance for asynchronous HTTP operations.
- The core orchestrator in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) relies on this uniform signature to execute modules concurrently and aggregate findings.

## Frequently Asked Questions

### What happens if a Holehe module returns a value instead of using the `out` parameter?

The core engine in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) expects results in the `out` list. If a module returns a value directly, the orchestrator will not capture the result, causing the output to be lost and the aggregation to fail. Always append to `out` and return `None`.

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

While the signature accepts any object as the `client` parameter, all existing modules in `holehe/modules/` assume an `httpx.AsyncClient` interface. Substituting a different client requires ensuring compatibility with `httpx` methods like `.get()`, `.post()`, and async context managers.

### Is the `email` parameter validated before reaching the module?

The core engine passes the email string directly without validation. Each module is responsible for sanitizing and validating the email format before making API requests, as implementation details vary by service requirements.

### How does Holehe handle rate limiting across modules?

Each module sets the `rateLimit` boolean in its result dictionary to indicate when a request was throttled. The `frequent_rate_limit` key signals whether the service generally imposes strict limits, helping users interpret temporary failures versus definitive negative results.