Holehe Module Output Schema: How `emailrecovery` and `phoneNumber` Work

The module output schema in holehe is a standardized dictionary with nine fields including emailrecovery and phoneNumber, which contain recovery email addresses and masked phone numbers returned by target services.

Holehe is an open-source OSINT tool that checks whether an email address is registered on hundreds of platforms. Each service check runs as an independent module that returns results in a uniform format. Understanding this schema is essential for anyone building custom modules or parsing holehe output programmatically.

The Standard Output Schema Structure

Every module in holehe appends a dictionary to the shared result list (out) with these exact keys:

Key Type Purpose
name str Internal module identifier (e.g., "twitter")
domain str Human-readable service domain (e.g., "twitter.com")
method str Test method used: "register" or "login"
frequent_rate_limit bool Whether the service has known rate-limiting behavior
rateLimit bool True if the current request hit a rate limit
exists bool True if the email is registered on the service
emailrecovery str or None Recovery email returned by the service (often masked)
phoneNumber str or None Phone number returned by the service (often masked)
others dict Optional free-form container for additional extracted data

This schema is enforced through the consuming code in holehe/core.py, specifically in the print_result and launch_module functions. The uniformity allows the core engine to process results from any service identically.

How emailrecovery and phoneNumber Get Populated

The emailrecovery and phoneNumber fields are service-dependent. Most modules initialize both as None and only overwrite them when the target API explicitly returns recovery information.

Example: Adobe Module with Recovery Data

In holehe/modules/software/adobe.py, the module extracts secondary email and security phone information when available:


# Simplified excerpt from holehe/modules/software/adobe.py

async def adobe(email, client, out):
    # ... HTTP request logic ...

    
    # When Adobe returns recovery information:

    out.append({
        "name": "adobe",
        "domain": "adobe.com",
        "method": "register",
        "frequent_rate_limit": False,
        "rateLimit": False,
        "exists": True,
        "emailrecovery": "j***@example.com",  # Masked recovery email

        "phoneNumber": "+1 ***-***-1234",     # Masked phone

        "others": {}
    })

Example: Twitter Module Without Recovery Data

By contrast, holehe/modules/social_media/twitter.py only checks for existence and leaves recovery fields empty:


# Simplified pattern from holehe/modules/social_media/twitter.py

out.append({
    "name": "twitter",
    "domain": "twitter.com",
    "method": "register",
    "frequent_rate_limit": True,
    "rateLimit": False,
    "exists": True,      # Email found

    "emailrecovery": None,
    "phoneNumber": None,
    "others": {}
})

Console Output Formatting in print_result

The print_result function in holehe/core.py concatenates emailrecovery and phoneNumber when present:


# From holehe/core.py print_result function

toprint = f"[+] {results['name']} {email}"
if results["emailrecovery"] is not None:
    toprint += " " + results["emailrecovery"]
if results["phoneNumber"] is not None:
    toprint += " / " + results["phoneNumber"]

This produces console output like:


[+] adobe target@example.com j***@example.com / +1 ***-***-1234
[+] twitter target@example.com

Notice how Twitter shows no recovery data while Adobe displays both fields.

Building a Custom Module with the Schema

When creating your own holehe module, follow this pattern exactly:


# Minimal custom module template

async def my_service(email, client, out):
    """
    Check if email exists on my-service.com
    """
    # Perform HTTP checks with the provided client

    # ...

    
    # Always append the full schema dictionary

    out.append({
        "name": "my_service",           # Function name

        "domain": "my-service.com",     # Service domain

        "method": "register",           # Detection method used

        "frequent_rate_limit": False,   # Known behavior

        "rateLimit": False,             # This request status

        "exists": True,                 # Detection result

        "emailrecovery": None,          # Or extracted value

        "phoneNumber": None,            # Or extracted value

        "others": {                     # Optional extra data

            "username": "extracted_user"
        }
    })

Key Source Files Defining the Schema

File Role
holehe/core.py Defines consumption logic in print_result and launch_module; documents schema through function contracts
holehe/modules/software/adobe.py Reference implementation showing emailrecovery and phoneNumber population
holehe/modules/social_media/twitter.py Reference implementation with null recovery fields
holehe/modules/__init__.py Module discovery and loading for the core engine

Summary

  • Holehe uses a strict 9-field output schema that every module must follow.
  • emailrecovery contains recovery email addresses (often masked) when services expose them.
  • phoneNumber contains phone numbers (often masked) when services expose them.
  • Both fields default to None and are omitted from console output when absent.
  • The schema is enforced by holehe/core.py, not by per-module validation.
  • Adobe serves as the canonical example of a module extracting recovery data.

Frequently Asked Questions

What happens if a module doesn't include all nine fields?

The print_result function in holehe/core.py expects all keys; omitting fields causes KeyError exceptions. All modules in the repository follow the complete schema even when returning None for unused fields.

Why do some services show masked emails while others show full addresses?

Masking is performed by the target service's API, not by holehe. The emailrecovery field stores whatever string the service returns—some mask partially (Adobe), some return full addresses, and most return nothing at all.

Can modules add arbitrary fields beyond the standard schema?

No—the core engine only recognizes the nine defined keys. However, the others dictionary accepts any additional key-value pairs for extended data extraction, and these display in console output.

How do I check if a module supports phone number extraction?

Inspect the module source in holehe/modules/ or run holehe and check output. Modules populating phoneNumber explicitly assign it in their out.append() call rather than leaving it as None.

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 →