Understanding Holehe’s Module Output JSON Structure: RateLimit, Exists, EmailRecovery, PhoneNumber, and Others

Holehe modules return a standardized Python dictionary with nine keys—including rateLimit, exists, emailrecovery, phoneNumber, and others—that enables uniform processing across all 120+ supported sites.

Holehe is an open-source OSINT tool by megadose that checks whether an email address is registered on websites. Each site-specific module in holehe/modules/ produces a consistent JSON-style output dictionary. This article breaks down every field in that structure, explains how holehe/core.py interprets them, and shows how to consume the data programmatically.

The Nine Standard Output Keys

Every module in the Holehe repository returns the same dictionary schema. This uniformity lets the core engine aggregate results without site-specific logic.

Key Type Purpose
name str Internal module identifier (e.g., "twitter")
domain str Human-readable domain (e.g., "twitter.com")
method str Check type—typically "register"
frequent_rate_limit bool UI hint: site often hits rate limits
rateLimit bool This request was blocked by rate limiting
exists bool/None Email found on target site
emailrecovery str/None Recovered or alternative email address
phoneNumber str/None Associated phone number, if exposed
others dict/None Additional metadata (names, dates, custom fields)

Where These Keys Are Defined

Module authors set the first four keys as module-level constants:


# From holehe/modules/social_media/twitter.py

name = "twitter"
domain = "twitter.com"
method = "register"
frequent_rate_limit = False

The remaining keys are populated dynamically based on the HTTP response.

rateLimit: Detecting When a Site Blocks You

The rateLimit boolean indicates whether the remote service returned a rate-limit error (typically HTTP 429) during the check.

In holehe/core.py, the print_result function handles this flag with special formatting:


# holehe/core.py lines 22-28 (excerpt)

if results["rateLimit"] and args.onlyused == False:
    websiteprint = print_color("[x] " + results["domain"], "yellow", args)

When rateLimit=True:

  • Terminal displays [x] in yellow
  • Module counted as not-checked rather than success/failure
  • CSV export records True for audit purposes

Rate Limit Implementation in Modules

Modules catch exceptions and set the flag in their except blocks. From twitter.py:


# holehe/modules/social_media/twitter.py lines 30-36

try:
    response = await client.post(...)
    # ... parse response ...

except Exception:
    out.append({
        "name": name,
        "domain": domain,
        "method": method,
        "frequent_rate_limit": frequent_rate_limit,
        "rateLimit": True,  # ← set here

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

exists: The Core Finding

The exists field carries the primary OSINT result:

Value Meaning Display
True Email registered on site [+] in green
False Email not found [-] in magenta
None Check inconclusive (error/rate-limit) varies

The print_result logic in holehe/core.py prioritizes this check:


# holehe/core.py lines 32-38 (excerpt)

elif results["exists"] == False and args.onlyused == False:
    websiteprint = print_color("[-] " + results["domain"], "magenta", args)
elif results["exists"] == True:
    websiteprint = print_color("[+] " + results["domain"], "green", args)

emailrecovery: Extracted Alternative Emails

Some services leak partial recovery email addresses during registration flows. The emailrecovery field captures these when available.

Notable characteristics:

  • Most modules return None
  • Services with exposed recovery flows (e.g., Adobe historically) may return masked emails like t***@gmail.com
  • Always validate—format varies by site implementation

phoneNumber: Leaked Phone Numbers

The phoneNumber field holds associated phone numbers when services expose them in registration responses. Like emailrecovery, this is service-dependent and often None.

others: Extensible Metadata Container

The others field provides a flexible dict for module-specific discoveries. Common uses include:

  • FullName: Real name associated with account
  • creation_date: Account registration timestamp
  • approx_lifetime: Estimated account age
  • Custom error messages or debug info

The print_result function checks for error in this dictionary:


# holehe/core.py lines 28-32

elif "error" in results.keys() and results["error"] and args.onlyused == False:
    # Handle error case

Consuming the JSON Output Programmatically

Access Raw Results from Python

import asyncio
import httpx
from holehe.core import import_submodules, get_functions, launch_module

async def check_email(email: str, target_module: str):
    """Run a single Holehe module and return its JSON output."""
    modules = import_submodules("holehe.modules")
    all_funcs = get_functions(modules)
    
    # Find target function by name

    func = next(f for f in all_funcs if f.__name__ == target_module)
    
    async with httpx.AsyncClient() as client:
        out = []
        await launch_module(func, email, client, out)
        return out[0]  # The standardized dictionary

# Usage

result = asyncio.run(check_email("test@example.com", "twitter"))
print(result)

Example output:

{
    'name': 'twitter',
    'domain': 'twitter.com',
    'method': 'register',
    'frequent_rate_limit': False,
    'rateLimit': False,
    'exists': True,
    'emailrecovery': None,
    'phoneNumber': None,
    'others': None
}

Filter Results by Status

def categorize_results(results_list):
    """Sort Holehe outputs into status categories."""
    categorized = {
        'found': [],
        'not_found': [],
        'rate_limited': [],
        'errors': []
    }
    for r in results_list:
        if r['rateLimit']:
            categorized['rate_limited'].append(r)
        elif 'error' in (r.get('others') or {}):
            categorized['errors'].append(r)
        elif r['exists'] is True:
            categorized['found'].append(r)
        elif r['exists'] is False:
            categorized['not_found'].append(r)
    return categorized

CSV Export Structure

Holehe's CLI exports all fields when using -C or --csv:

holehe test@example.com --csv

# Creates: holehe_1724523785_test@example.com_results.csv

CSV columns match the dictionary keys exactly:

name,domain,method,frequent_rate_limit,rateLimit,exists,emailrecovery,phoneNumber,others
twitter,twitter.com,register,False,False,True,,,
facebook,facebook.com,register,False,False,False,,,
instagram,instagram.com,register,True,True,,,,

Key Source Files

File Purpose Critical Lines
holehe/core.py Output processing, printing, CSV export print_result (~L22-48), export_csv
holehe/modules/social_media/twitter.py Exemplary module implementation Exception handling (~L30-36)
holehe/localuseragent.py Shared user-agent rotation Used by most modules
holehe/instruments.py Progress bar for parallel execution Async instrumentation

Summary

  • Uniform schema: Every Holehe module returns the same nine-key dictionary, enabling consistent processing across 120+ sites.
  • Status hierarchy: rateLimit takes precedence, then error in others, then exists True/False for final display determination.
  • Optional enrichment: emailrecovery, phoneNumber, and others capture additional OSINT when services leak it.
  • Programmatic access: Import launch_module from holehe.core to consume results directly without CLI overhead.

Frequently Asked Questions

What does rateLimit=True mean versus frequent_rate_limit=True?

rateLimit=True means this specific request was blocked by the remote service (HTTP 429 or similar). frequent_rate_limit is a static module property warning that the site often rate-limits—it's used for UI highlighting only. A site can have frequent_rate_limit=False but still return rateLimit=True on any given run.

Why is exists sometimes None instead of True or False?

exists is None when the module cannot determine registration status. This occurs when rateLimit=True (request blocked), when an unhandled exception occurs, or when the service returns an ambiguous response. The print_result function in holehe/core.py handles None as an inconclusive state.

How do I extract data from the others dictionary?

The others field is free-form, so safely access it with .get():

others = result.get('others') or {}
full_name = others.get('FullName')
creation_date = others.get('creation_date')

Check individual module implementations in holehe/modules/ to discover what keys each may populate.

Can I add custom fields to the others dictionary in my own module?

Yes. When writing a custom module, populate others with any discovery data:

out.append({
    # ... standard keys ...

    'others': {
        'custom_field': extracted_value,
        'metadata': {'source': 'api_endpoint_v2'}
    }
})

Ensure values are JSON-serializable for proper CSV export handling.

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 →