# Standardized Output Format for Holehe Modules: A Complete Guide to OSINT Result Structuring

> Learn the standardized output format for holehe modules. This guide explains how dictionaries with standardized keys enable uniform OSINT aggregation across all services.

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

---

**Each holehe module returns a dictionary with nine standardized keys appended to a shared `out` list, enabling uniform OSINT aggregation across all supported services.**

The holehe framework by megadose unifies email reconnaissance across hundreds of platforms through a strict output contract. Every asynchronous module must populate the same dictionary schema, allowing the core engine to process results without service-specific logic. This article examines the exact field specifications, implementation in the source code, and how to build compliant custom modules.

## The Nine-Field Output Schema

Every module in holehe appends a dictionary to the shared `out` list with these mandatory keys:

| Field | Type | Purpose |
|-------|------|---------|
| `name` | str | Service identifier (e.g., `"twitter"`, `"facebook"`) |
| `domain` | str | Primary domain of the service |
| `method` | str | Interaction type—typically `"register"` for account existence checks |
| `frequent_rate_limit` | bool | Indicates if the service enforces strict rate limiting |
| `rateLimit` | bool | `True` when the current request was blocked or throttled |
| `exists` | bool | `True` if the email is registered on the platform |
| `emailrecovery` | str \| None | Partial email shown in password recovery flows |
| `phoneNumber` | str \| None | Phone number mask if exposed during checks |
| `others` | dict \| None | Additional metadata (full name, registration date, etc.) |

This schema is enforced through convention rather than validation—modules that deviate will cause downstream errors in `print_result` or export functions.

## How the Core Engine Enforces the Format

The standardization mechanism resides in [[`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py)](https://github.com/megadose/holehe/blob/master/holehe/core.py), specifically the `launch_module` function around **lines 66-71**. This coroutine receives each loaded module, the target email, an HTTP client, and the shared `out` list:

```python

# From holehe/core.py (conceptual flow)

async def launch_module(module, email, client, out):
    await module(email, client, out)
    # Module responsible for appending dict to out

```

The `out` list accumulates results from all concurrent module executions. After completion, `print_result` iterates over this list expecting the nine-field structure, while `export_csv` maps these dictionaries directly to column headers.

Module discovery occurs through `import_submodules` (**lines 36-47**), which recursively imports all Python files under `holehe/modules/`. Any file defining an async function matching the signature `async def servicename(email, client, out)` becomes automatically available.

## Real-World Implementation Examples

### Twitter Module: Minimal Schema Compliance

The Twitter checker in [[`holehe/modules/social_media/twitter.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/social_media/twitter.py)](https://github.com/megadose/holehe/blob/master/holehe/modules/social_media/twitter.py) (**lines 17-31**) demonstrates the baseline implementation:

```python

# Simplified from twitter.py

out.append({
    "name": "twitter",
    "domain": "twitter.com",
    "method": "register",
    "frequent_rate_limit": False,
    "rateLimit": False,
    "exists": True,  # Determined from API response

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

```

This module performs a single POST request to Twitter's validation endpoint and translates the HTTP response directly into the standardized format.

### Facebook Module: Complex Logic, Same Structure

The Facebook checker in [[`holehe/modules/social_media/facebook.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/social_media/facebook.py)](https://github.com/megadose/holehe/blob/master/holehe/modules/social_media/facebook.py) (**lines 30-82**) shows how intricate authentication flows—CSRF token extraction, cookie management, redirect following—still converge on identical output:

```python

# Conceptual excerpt from facebook.py

out.append({
    "name": "facebook",
    "domain": "facebook.com",
    "method": "register",
    "frequent_rate_limit": True,
    "rateLimit": False,
    "exists": exists,  # Boolean from response parsing

    "emailrecovery": None,
    "phoneNumber": None,
    "others": others,  # May contain 'name' or 'profile_photo'

})

```

Despite requiring multiple requests and header manipulation, the final dictionary adheres precisely to the schema.

## Creating Compliant Custom Modules

To extend holehe with new services, implement the standard output format as follows:

```python

# custom_module.py — place in holehe/modules/category/

import httpx

async def newservice(email, client, out):
    """
    Example custom module following holehe's standardized output format.
    """
    name = "newservice"
    domain = "newservice.com"
    method = "register"
    frequent_rate_limit = False
    
    try:
        response = await client.post(
            "https://newservice.com/api/v1/users/check",
            json={"email": email},
            timeout=10
        )
        
        data = response.json()
        exists = data.get("account_exists", False)
        others = None
        
        if exists and "username" in data:
            others = {"username": data["username"]}
        
        result = {
            "name": name,
            "domain": domain,
            "method": method,
            "frequent_rate_limit": frequent_rate_limit,
            "rateLimit": False,
            "exists": exists,
            "emailrecovery": data.get("recovery_hint"),
            "phoneNumber": None,
            "others": others,
        }
        
    except httpx.HTTPStatusError as e:
        # Rate limited or blocked

        result = {
            "name": name,
            "domain": domain,
            "method": method,
            "frequent_rate_limit": frequent_rate_limit,
            "rateLimit": e.response.status_code == 429,
            "exists": False,
            "emailrecovery": None,
            "phoneNumber": None,
            "others": None,
        }
        
    except Exception:
        # Generic failure

        result = {
            "name": name,
            "domain": domain,
            "method": method,
            "frequent_rate_limit": frequent_rate_limit,
            "rateLimit": False,
            "exists": False,
            "emailrecovery": None,
            "phoneNumber": None,
            "others": None,
        }
    
    out.append(result)

```

Place this file anywhere under `holehe/modules/` and restart holehe—the `import_submodules` function will detect and load it automatically.

## Consuming Standardized Output Programmatically

The uniform structure enables reliable downstream processing:

```python
import asyncio
import json
from holehe.core import launch_module
from holehe.modules.social_media import twitter

async def analyze(email):
    async with httpx.AsyncClient() as client:
        results = []
        await launch_module(twitter.twitter, email, client, results)
        
        # Guaranteed fields regardless of service

        for r in results:
            print(f"{r['name']}: {'Found' if r['exists'] else 'Not found'}")
            if r['rateLimit']:
                print("  ⚠️ Rate limited")
            if r['others']:
                print(f"  Extra: {r['others']}")

# Execute

asyncio.run(analyze("target@example.com"))

```

This predictability is why the export functions in [[`core.py`](https://github.com/megadose/holehe/blob/main/core.py)](https://github.com/megadose/holehe/blob/master/holehe/core.py) can generate CSV, JSON, or terminal tables without service-specific handlers.

## Summary

- **Nine mandatory fields** define the holehe standardized output format: `name`, `domain`, `method`, `frequent_rate_limit`, `rateLimit`, `exists`, `emailrecovery`, `phoneNumber`, and `others`
- **Core enforcement** occurs through the `launch_module` function and shared `out` list in [[`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py)](https://github.com/megadose/holehe/blob/master/holehe/core.py)
- **Reference implementations** exist in [[`twitter.py`](https://github.com/megadose/holehe/blob/main/twitter.py)](https://github.com/megadose/holehe/blob/master/holehe/modules/social_media/twitter.py) (minimal) and [[`facebook.py`](https://github.com/megadose/holehe/blob/main/facebook.py)](https://github.com/megadose/holehe/blob/master/holehe/modules/social_media/facebook.py) (complex)
- **Custom modules** must append dictionaries matching this exact schema to integrate automatically

## Frequently Asked Questions

### What happens if a module omits required fields in the output dictionary?

The core engine will raise a `KeyError` during result formatting in `print_result` or export operations. While the `launch_module` function itself does not validate the dictionary, downstream consumers expect all nine fields. Missing keys cause crashes when generating CSV headers or terminal tables.

### Can the `others` field contain nested structures beyond flat dictionaries?

Yes. The `others` field accepts any JSON-serializable value, though flat dictionaries are conventional. Some modules store strings like full names, while others use nested objects with multiple metadata points. The export functions handle this through standard JSON serialization.

### How does holehe handle rate limiting uniformly across different services?

Each module sets `rateLimit` to `True` when detecting HTTP 429 responses, CAPTCHA challenges, or authentication blocks. The `frequent_rate_limit` flag in the schema serves as documentation—warning users that a service aggressively limits requests. The core engine does not automatically retry; modules must implement their own backoff logic or fail fast with `rateLimit=True`.