Standard Output Format for Holehe Modules: Complete Developer Guide

Each Holehe module appends a standardized dictionary with nine specific keys—name, domain, method, frequent_rate_limit, rateLimit, exists, emailrecovery, phoneNumber, and others—to a shared out list, enabling uniform result aggregation across all OSINT checks.

Holehe is an asynchronous email reconnaissance tool developed by megadose that verifies account existence across hundreds of platforms. Understanding the standard output format for Holehe modules is critical for developers extending the framework or parsing results programmatically. This guide examines the exact schema enforced by the core engine and demonstrates implementation patterns using production code from the repository.

The Standardized Dictionary Schema

Every module must conform to a strict dictionary contract when appending results. According to the launch_module implementation in holehe/core.py (lines 66-71), the core engine expects dictionaries containing these exact keys:

  • name — The service identifier (e.g., "twitter", "facebook").
  • domain — The primary domain of the service (e.g., "twitter.com").
  • method — The interaction type, typically "register" for registration checks.
  • frequent_rate_limit — Boolean flag indicating whether the service enforces aggressive rate limiting.
  • rateLimit — Boolean set to True when the request hits a rate limit, otherwise False.
  • exists — Boolean indicating whether the email is registered on the service.
  • emailrecovery — Optional string containing a recovery email hint, or None.
  • phoneNumber — Optional string containing an associated phone number, or None.
  • others — Dictionary for additional metadata (e.g., full name, account creation date), or None.

This schema ensures that the aggregation logic in print_result and export_csv functions can process outputs from any module without service-specific parsing.

Core Module Execution Logic

The holehe/core.py file orchestrates module discovery and execution. The import_submodules function (lines 36-47) dynamically loads every Python file placed in holehe/modules/, while launch_module invokes each check asynchronously.

The out parameter is a Python list passed by reference to all concurrent modules. Because Holehe uses asyncio to run checks in parallel, the shared list accumulates standardized dictionaries from hundreds of sources simultaneously. The core engine then iterates over this list to generate CLI tables or CSV exports, relying entirely on the consistent key structure described above.

Reference Implementations from Production Modules

Twitter Module Pattern

The twitter.py module (lines 17-31) provides a minimal yet complete implementation. After querying Twitter's registration endpoint, it constructs the dictionary with all required keys, setting exists based on the HTTP response status and rateLimit to False unless the request encounters throttling.

Facebook Module Complexity

The facebook.py module (lines 30-82) demonstrates advanced usage involving CSRF token extraction and session management. Despite the complex authentication flow, it maintains the identical output structure, proving that the standard format accommodates varied API interaction patterns while preserving consistency for the core aggregator.

Creating Custom Modules

To build a compatible module, define an asynchronous function accepting three parameters: email, client (an HTTPX AsyncClient), and out (the shared results list).


# mycustomservice.py

import httpx

async def mycustomservice(email, client, out):
    name = "mycustomservice"
    domain = "example.com"
    method = "register"
    frequent_rate_limit = False
    
    try:
        response = await client.get(
            f"https://{domain}/api/check",
            params={"email": email},
            timeout=10
        )
        exists = response.json().get("registered", False)
        
        out.append({
            "name": name,
            "domain": domain,
            "method": method,
            "frequent_rate_limit": frequent_rate_limit,
            "rateLimit": False,
            "exists": exists,
            "emailrecovery": None,
            "phoneNumber": None,
            "others": None
        })
    except httpx.HTTPStatusError:
        out.append({
            "name": name,
            "domain": domain,
            "method": method,
            "frequent_rate_limit": frequent_rate_limit,
            "rateLimit": True,
            "exists": False,
            "emailrecovery": None,
            "phoneNumber": None,
            "others": None
        })

Place this file in holehe/modules/social_media/ to enable automatic discovery by the import_submodules loader.

Command-Line and Programmatic Usage

Invoke Holehe via CLI to observe the standardized output:

holehe target@example.com

For programmatic integration, import the core launcher directly:

import asyncio
import httpx
from holehe.core import launch_module
from holehe.modules.social_media import twitter

async def check_single():
    async with httpx.AsyncClient() as client:
        results = []
        await launch_module(twitter.twitter, "user@example.com", client, results)
        
        for entry in results:
            if entry["exists"]:
                print(f"Found on {entry['name']}: {entry['domain']}")

asyncio.run(check_single())

The results list contains dictionaries adhering strictly to the nine-key schema, regardless of which modules executed.

Summary

  • The standard output format for Holehe modules requires a dictionary with exactly nine keys: name, domain, method, frequent_rate_limit, rateLimit, exists, emailrecovery, phoneNumber, and others.
  • Modules append these dictionaries to a shared out list passed by the launch_module function in holehe/core.py.
  • The exists boolean indicates account presence, while rateLimit signals throttling issues, and others accommodates supplementary metadata.
  • Production examples in twitter.py and facebook.py demonstrate both simple and complex implementations of the schema.
  • Custom modules must use the async signature (email, client, out) and maintain the exact key structure for compatibility with the core aggregation engine.

Frequently Asked Questions

What happens if a module omits required keys from the output dictionary?

The core engine's formatting functions in holehe/core.py expect all nine keys to be present. Omitting keys such as exists or rateLimit will raise a KeyError during result processing or CSV export. Always populate every field, using None for optional data and False for boolean defaults when information is unavailable.

Can modules add custom fields outside the standard schema?

While the others key exists specifically for extensible metadata, adding new top-level keys outside the defined schema breaks compatibility with the core aggregation logic. Store any additional data—such as profile URLs or account creation dates—within the others dictionary to maintain forward compatibility with future Holehe versions.

How does the output format handle partial or rate-limited responses?

When a module encounters rate limiting, it must set rateLimit to True and exists to False. The frequent_rate_limit metadata flag alerts users that the service aggressively restricts requests, allowing the CLI to implement delays or warning messages. This standardized error handling ensures consistent behavior across all 300+ supported platforms.

Is the dictionary format consistent between CLI usage and Python API calls?

Yes. Whether invoking Holehe from the terminal, importing launch_module directly, or integrating the modules into larger async workflows, the standard output format remains identical. The dictionary structure serves as the universal contract between individual service modules and the core processing engine, ensuring predictable data structures regardless of the interface used.

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 →