How Holehe Parses API Responses and Handles Error Messages

Holehe normalizes every service interaction into a standardized dictionary contract, using launch_module in holehe/core.py to catch exceptions and print_result to surface error details from the others field.

Holehe, the open-source OSINT tool created by megadose, verifies email address existence across hundreds of platforms by orchestrating concurrent HTTP requests. Understanding how Holehe parses API responses and error messages reveals a robust architecture that converts diverse JSON and HTML payloads into uniform result structures while gracefully handling rate limits and network failures.

The Async Module Contract

Every Holehe service module implements an identical asynchronous interface. Each module is an async function accepting three parameters: the target email string, an httpx.AsyncClient instance, and a shared out list.

The function performs its HTTP request—either via await client.get(), post(), or HTML fetching—and must append a standardized dictionary to the out list before returning. This contract ensures the core execution engine can aggregate results from hundreds of concurrent modules without knowing implementation details of individual services.


# Simplified structure of a Holehe module

async def github(email: str, client: httpx.AsyncClient, out: list):
    resp = await client.get(f"https://api.github.com/users/{email}")
    # Parsing logic here...

    out.append({
        "domain": "github.com",
        "rateLimit": False,
        "error": False,
        "exists": True,
        "emailrecovery": None,
        "phoneNumber": None,
        "others": None,
    })

Parsing JSON and HTML Responses

Holehe modules handle two primary response formats, selecting the appropriate parser based on the service's API design.

JSON Response Handling

For REST APIs, modules call response.json() to deserialize payloads. The code then inspects specific fields that indicate account existence or error states—such as "message": "Not Found" or user profile data.

data = resp.json()
if data.get("message") == "Not Found":
    exists = False
else:
    exists = True

HTML Response Handling

For services returning HTML, Holehe uses BeautifulSoup to parse the DOM. Modules import from bs4 import BeautifulSoup and search for error banners, account status indicators, or specific text patterns that reveal whether an email is registered.

from bs4 import BeautifulSoup

soup = BeautifulSoup(response.text, 'html.parser')
error_banner = soup.find('div', {'class': 'error-message'})
if error_banner and 'not found' in error_banner.text.lower():
    exists = False

HTTP Status Code Inspection and Rate Limiting

Before parsing payloads, modules check resp.status_code. A 429 status code (or rate-limit headers) triggers an immediate short-circuit that sets the rateLimit flag to True and skips further processing.

if resp.status_code == 429:
    out.append({
        "domain": "github.com",
        "rateLimit": True,
        "error": False,
        "exists": False,
        "emailrecovery": None,
        "phoneNumber": None,
        "others": None,
    })
    return

Exception Handling in the Core Launcher

When a module raises an unexpected exception—whether from network timeouts, parsing errors, or service changes—the core launcher catches it and constructs a standardized error record. According to the source code in holehe/core.py, the launch_module function (lines 166-178) wraps module execution in a try-except block that extracts the module name and populates the error fields.


# Simplified logic from holehe/core.py launch_module

try:
    await module(email, client, out)
except Exception:
    name = str(module).split('<function ')[1].split(' ')[0]
    out.append({
        "name": name,
        "domain": data[name],  # Module-to-domain mapping

        "rateLimit": False,
        "error": True,
        "exists": False,
        "emailrecovery": None,
        "phoneNumber": None,
        "others": None,
    })

Normalized Output Schema and Error Messages

Every module returns a dictionary containing seven standardized keys that the printer expects:

  • domain: Human-readable site name (e.g., github.com).
  • rateLimit: Boolean indicating if the service returned a 429 or rate-limit header.
  • error: Boolean set to True when an unexpected error occurs.
  • exists: Boolean indicating if the email is known to exist on the service.
  • emailrecovery / phoneNumber: Optional recovered contact data.
  • others: A catch-all dictionary that may contain "errorMessage" or auxiliary metadata.

Displaying Errors to the User

The printer logic in holehe/core.py (lines 124-132) processes the aggregated results. When error is True, it checks results["others"] for an errorMessage key, prefixes the domain with [!], and displays the message in red.

if results.get("error"):
    toprint = ""
    if results["others"] and "errorMessage" in results["others"]:
        toprint = " Error message: " + results["others"]["errorMessage"]
    print(colored("[!] " + results["domain"] + toprint, "red"))

Summary

  • Uniform Interface: Every module is an async function writing to a shared out list with a standardized schema.
  • Dual Parsing: Holehe parses JSON via response.json() and HTML via BeautifulSoup DOM selectors.
  • Rate Limit Detection: HTTP 429 responses set rateLimit: True and bypass payload parsing.
  • Centralized Error Handling: The launch_module function in holehe/core.py (lines 166-178) catches all exceptions and forces error: True in the output record.
  • User-Friendly Reporting: The printer extracts others["errorMessage"] to display service-specific errors prefixed with [!] (lines 124-132).

Frequently Asked Questions

How does Holehe handle rate-limited responses from services?

Holehe interprets HTTP 429 status codes as rate-limit conditions. When a module detects this status, it immediately appends a result dictionary with "rateLimit": True to the output list and returns without parsing the response body, preventing unnecessary processing and clearly flagging the restriction to the user.

What parsing libraries does Holehe use for HTML responses?

Holehe uses BeautifulSoup (from bs4 import BeautifulSoup) to parse HTML responses. Modules search for specific DOM selectors, error banners, or text patterns that indicate account status, allowing Holehe to extract intelligence from services that do not provide JSON APIs.

Where does Holehe catch exceptions when a module crashes?

The core launcher in holehe/core.py wraps each module call in a try-except block within the launch_module function (lines 166-178). When an exception occurs, it extracts the module name and appends an error record with "error": True, ensuring that one failing module does not crash the entire scan.

What standardized keys must every Holehe module include in its output?

Every module must return a dictionary containing: domain (site name), rateLimit (boolean), error (boolean), exists (boolean), emailrecovery (string or None), phoneNumber (string or None), and others (dictionary or None). The others field specifically stores "errorMessage" for service-specific error details.

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 →