Holehe JSON Output Format: Standardized Schema and Field Reference

Holehe returns email-check results as a list of flat Python dictionaries that serialize directly to JSON, with each entry containing standardized fields like exists, rateLimit, emailrecovery, and others.

Holehe is an open-source OSINT tool by @megadose that checks whether an email address is registered across hundreds of websites. Understanding the Holehe JSON output format is essential for building automated workflows, because the tool's programmatic value lies in this consistent, machine-readable schema. The format is defined implicitly in holehe/core.py and implemented by every module in holehe/modules/.

Core Structure: A List of Uniform Dictionaries

Holehe aggregates results into a single Python list variable named out. Each element is a dictionary representing one site check. Because the structure uses only built-in types—strings, booleans, None, and nested dicts—it requires no custom encoders.

from holehe.core import launch_module

# Inside the execution loop, each module appends to `out`:

out = []  # Collects all results

await launch_module(site, email, client, out)

# `out` is now JSON-serializable

The list structure enables parallel processing and simple downstream parsing with standard JSON libraries.

Standard Fields in Every Holehe JSON Entry

Every dictionary follows the same schema, whether produced by twitter.py, blablacar.py, or any other module. Fields are populated according to the site's response and the module's capabilities.

Field Type Purpose
name str Internal module identifier (e.g., "blablacar").
domain str Public domain queried (e.g., "blablacar.com").
method str | absent HTTP method used: "register", "login", etc.
frequent_rate_limit bool | absent Whether the service commonly returns rate limits.
rateLimit bool True if the request was blocked temporarily.
error bool True if the module raised an unexpected exception.
exists bool True if the email is registered on this service.
emailrecovery str | None Password-recovery hint (masked email) if exposed.
phoneNumber str | None Recovered phone number if exposed.
others dict | None Additional metadata (name, dates, raw responses).

The name and domain fields are guaranteed. Optional fields appear only when modules explicitly set them.

How Fields Are Populated: Source Code Analysis

Automatic Fields from Core Logic

In holehe/core.py, the launch_module function provides fallback handling when a module crashes. This fallback constructs a minimal valid entry:


# holehe/core.py lines 71-73 (simplified)

name = str(module).split('<function ')[1].split(' ')[0]
data[name] = {"exists": False, ...}  # domain derived similarly

out.append({"name": name, "domain": data[name], ...})

The fallback also sets error: True when an exception occurs, ensuring the output remains schema-compliant even during failures.

Module-Generated Fields

Individual modules populate the remaining fields based on HTTP responses. Consider blablacar.py:


# holehe/modules/transport/blablacar.py lines 5-9

def blablacar(email, client, out):
    # Module metadata

    method = "register"
    frequent_rate_limit = True
    # ... request logic ...

On success (lines 68-74), it returns:

{
    "exists": True,           # Email found on service

    "rateLimit": False,       # No throttling occurred

    "emailrecovery": None,    # No recovery hint extracted

    "phoneNumber": None,
    "others": None
}

The others field captures unstructured data. For example, if a module extracts a user's full name or account creation date, it places these in others rather than polluting the top-level schema.

Working with Holehe JSON Output

Command-Line JSON Export

Holehe's table output is human-readable but not machine-friendly. Capture raw JSON using Python's eval-to-JSON pattern:


# Basic CLI usage (colored table)

holehe test@example.com

# Extract valid JSON

holehe test@example.com --no-color --no-clear | \
    python -c "import sys, json; print(json.dumps(eval(sys.stdin.read())))"

Library Usage for Programmatic Access

For production automation, import Holehe directly:

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

async def check_email(email: str) -> str:
    """Return Holehe results as a JSON string."""
    modules = import_submodules("holehe.modules")
    sites = get_functions(modules)
    
    out = []
    async with httpx.AsyncClient() as client:
        for site in sites:
            await launch_module(site, email, client, out)
    
    return json.dumps(out, indent=2)

# Execute

result = asyncio.run(check_email("target@example.com"))
print(result)

Sample output:

[
  {
    "name": "blablacar",
    "domain": "blablacar.com",
    "method": "register",
    "frequent_rate_limit": true,
    "rateLimit": false,
    "error": false,
    "exists": true,
    "emailrecovery": null,
    "phoneNumber": null,
    "others": null
  }
]

Field-by-Field Reference for Automation

exists vs. rateLimit vs. error

These three booleans encode distinct failure modes:

  • exists: True — Confirmed registration. The email has an account.
  • rateLimit: True — Cannot determine; the service blocked the request. Retry later.
  • error: True — Module crashed. The result is unreliable; check logs.

A result with exists: False and rateLimit: True is inconclusive, not negative.

emailrecovery and phoneNumber Pattern Matching

When services expose password-recovery hints, modules attempt extraction:


# Typical extraction pattern in modules

"emailrecovery": response.text.split('maskedEmail":"')[1].split('"')[0],

These fields often contain partially redacted values like t***@example.com. Parse defensively—services change their masking formats without warning.

Extending with others

The others field preserves forward compatibility. When Holehe adds new extractors, they place data here rather than breaking existing parsers. Structure varies by module:

"others": {
    "full_name": "John Doe",
    "created_at": "2019-03-15",
    "raw_error": "Invalid captcha"  # Even errors may appear here

}

Key Files Defining the Output Format

Path Responsibility
holehe/core.py Orchestration, fallback entry construction, error handling. Contains launch_module and the schema fallback.
holehe/modules/**/*.py Site-specific logic. Every module must return a dictionary matching the core schema.
holehe/instruments.py Progress instrumentation (does not affect output format).

Summary

  • Holehe produces a list of flat dictionaries that serialize directly to JSON without custom encoders.
  • Ten standardized fields cover identification (name, domain), status (exists, rateLimit, error), and extracted intelligence (emailrecovery, phoneNumber, others).
  • The core fallback in launch_module ensures schema compliance even when modules crash.
  • others provides an extension point for unstructured data without breaking backward compatibility.
  • For automation, import launch_module directly rather than parsing CLI output.

Frequently Asked Questions

How do I know if an email is definitely not registered?

You cannot be certain when rateLimit: True. Only exists: False with rateLimit: False indicates absence. Rate-limited responses require retry.

Why is emailrecovery often null?

Most services mask recovery hints or require additional authentication steps. Modules populate this field only when the unauthenticated response exposes recoverable data.

Can I add custom fields to the output?

Yes, through the others dictionary in your own module. Do not add top-level fields—this breaks compatibility with standard Holehe parsers.

What version of Holehe uses this exact schema?

The schema described reflects the current master branch as implemented in megadose/holehe. Specific field availability (method, frequent_rate_limit) depends on whether individual modules choose to populate them.

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 →