Understanding the Standard Signature for a Holehe Module

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 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:

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 and 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 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:

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, where the function appends the standardized dictionary to out without returning a value.

Core Orchestration Usage

The run_modules function in holehe/core.py automates the process across all registered modules:

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, 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 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 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 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.

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 →