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

> Explore the holehe module output schema, detailing how fields like emailrecovery and phoneNumber provide recovery emails and masked phone numbers from target services.

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

---

**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`](https://github.com/megadose/holehe/blob/main/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`](https://github.com/megadose/holehe/blob/main/holehe/modules/software/adobe.py), the module extracts secondary email and security phone information when available:

```python

# 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`](https://github.com/megadose/holehe/blob/main/holehe/modules/social_media/twitter.py) only checks for existence and leaves recovery fields empty:

```python

# 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`](https://github.com/megadose/holehe/blob/main/holehe/core.py) concatenates `emailrecovery` and `phoneNumber` when present:

```python

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

```python

# 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`](https://github.com/megadose/holehe/blob/main/holehe/core.py) | Defines consumption logic in `print_result` and `launch_module`; documents schema through function contracts |
| [`holehe/modules/software/adobe.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/software/adobe.py) | Reference implementation showing `emailrecovery` and `phoneNumber` population |
| [`holehe/modules/social_media/twitter.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/social_media/twitter.py) | Reference implementation with null recovery fields |
| [`holehe/modules/__init__.py`](https://github.com/megadose/holehe/blob/main/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`](https://github.com/megadose/holehe/blob/main/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`](https://github.com/megadose/holehe/blob/main/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`.