How to Use Holehe for API Checks: A Complete Guide to Programmatic Email OSINT

Holehe exposes its core OSINT engine as a Python API, allowing you to programmatically check whether an email address is registered across 120+ online services using async functions like import_submodules, get_functions, and launch_module.

Holehe is an open-source email reconnaissance tool developed by megadose that checks registration status across social media, financial, and software platforms. While most users know it as a CLI tool, its real power lies in its modular Python API that you can embed directly into security tooling, automation pipelines, and custom dashboards.

Holehe's API Architecture

The codebase in megadose/holehe separates concerns into three layers that together form its public API surface:

Dynamic Module Discovery

All service checks live as individual Python files under holehe/modules/. The import_submodules function recursively walks this package tree, importing every module so they become callable functions at runtime.

from holehe.core import import_submodules

modules = import_submodules("holehe.modules")

This implementation in holehe/core.py lines 37-47 enables Holehe to add new services without code changes—simply drop a new module file into the directory.

Function Extraction and Filtering

Once modules are loaded, get_functions extracts the actual check functions (e.g., office365, github, instagram) and applies CLI-style filtering. Pass a mock arguments object to skip password-recovery checks when you don't need them.

from holehe.core import get_functions

# Create filter args

args = type('Args', (), {'nopasswordrecovery': True})
services = get_functions(modules, args)

This corresponds to holehe/core.py lines 50-63. The function returns a list of coroutine functions ready for async execution.

Asynchronous Execution Engine

The launch_module coroutine runs individual service checks with httpx and trio. It handles exceptions internally and normalizes every result into a standard dictionary with consistent keys: exists, rateLimit, emailrecovery, others, name, and domain.

from holehe.core import launch_module

results = []
await launch_module(service_func, email, client, results)

Found in holehe/core.py lines 66-71, this abstraction lets you run checks without managing response parsing yourself.

Complete API Usage Example

Here's a production-ready pattern for using Holehe programmatically:


# holehe_api_check.py

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


async def check_email_across_services(email: str, timeout: int = 15):
    """
    Run Holehe checks against all supported services concurrently.
    Returns list of normalized result dictionaries.
    """
    # 1. Load all service modules dynamically

    modules = import_submodules("holehe.modules")
    
    # 2. Extract functions, skipping password recovery checks

    args = type('Args', (), {'nopasswordrecovery': True})
    services = get_functions(modules, args)
    
    # 3. Shared results container (thread-safe for trio)

    results = []
    
    # 4. Configure async HTTP client with custom timeout

    client = httpx.AsyncClient(timeout=timeout, follow_redirects=True)
    
    try:
        # 5. Execute all checks concurrently using trio nursery

        async with trio.open_nursery() as nursery:
            for service_func in services:
                nursery.start_soon(
                    launch_module,
                    service_func,
                    email,
                    client,
                    results
                )
    finally:
        await client.aclose()
    
    return results


def format_output(results: list) -> None:
    """Pretty-print results sorted by service name."""
    for r in sorted(results, key=lambda x: x["name"]):
        status = "✔️ REGISTERED" if r["exists"] else "❌ not found"
        rate_note = " (rate limited)" if r["rateLimit"] else ""
        print(f"{status:<15} {r['domain']:<20} {rate_note}")


if __name__ == "__main__":
    import sys
    
    email = sys.argv[1] if len(sys.argv) > 1 else "test@example.com"
    raw_results = trio.run(check_email_across_services, email)
    format_output(raw_results)

Run with: python holehe_api_check.py target@gmail.com

Targeting Individual Services

For targeted checks—useful when you only care about specific platforms—import and call service functions directly:

import trio
import httpx
from holehe.modules.software.office365 import office365
from holehe.modules.social_media.github import github
from holehe.modules.social_media.instagram import instagram


async def targeted_check(email: str, services: list):
    results = []
    
    async with httpx.AsyncClient() as client:
        for svc in services:
            out = []
            await svc(email, client, out)
            results.extend(out)
    
    return results


# Check only Office 365 and GitHub

services = [office365, github]
targeted = trio.run(targeted_check, "user@company.com", services)

for r in targeted:
    print(f"{r['domain']}: exists={r['exists']}, rateLimit={r['rateLimit']}")

Direct imports bypass module discovery entirely, reducing startup time and giving you precise control over which endpoints you hit.

Understanding Service Module Structure

Each service follows a minimal contract defined in files like holehe/modules/software/office365.py. A valid service function signature is:

async def servicename(email: str, client: httpx.AsyncClient, out: list) -> None:
    # Build and send request

    response = await client.get("https://...", headers=...)
    
    # Parse response and append result dict

    out.append({
        "name": "ServiceName",
        "domain": "service.com",
        "method": "http",
        "frequent_rate_limit": False,
        "rateLimit": False,
        "exists": True,  # or False

        "emailrecovery": None,  # or recovered partial email

        "phoneNumber": None,  # or recovered digits

        "others": None  # or additional metadata

    })

The Office 365 implementation (lines 5-49 in holehe/modules/software/office365.py) demonstrates error handling patterns: it first probes with a random local part to establish baseline behavior, then checks the real address, catching rate limits and parsing XML responses for definitive "exists" signals.

Custom Filtering and Module Selection

Build category-specific checks by inspecting module paths:

import importlib
from pathlib import Path

def get_services_by_category(category: str):
    """
    Load only services from a specific category directory.
    Categories: social_media, financial, software, etc.
    """
    base_path = Path("holehe/modules") / category
    services = []
    
    for py_file in base_path.glob("*.py"):
        if py_file.name.startswith("_"):
            continue
        module_path = f"holehe.modules.{category}.{py_file.stem}"
        mod = importlib.import_module(module_path)
        
        # Extract service function (matches filename)

        func = getattr(mod, py_file.stem, None)
        if func:
            services.append(func)
    
    return services

This pattern lets you create scoped checks—for example, only financial services for fraud investigations, or only social media for digital footprinting.

Integrating with Async Applications

Holehe's trio-based architecture integrates cleanly with other async frameworks. Here's a FastAPI endpoint pattern:

from fastapi import FastAPI
import trio
from concurrent import futures

app = FastAPI()

# Thread pool for sync barrier around trio

trio_executor = futures.ThreadPoolExecutor(max_workers=4)

async def run_holehe_in_thread(email: str):
    """Bridge sync FastAPI with trio-based Holehe."""
    loop = asyncio.get_event_loop()
    return await loop.run_in_executor(
        trio_executor,
        trio.run,
        check_email_across_services,
        email
    )

@app.get("/check/{email}")
async def check_email(email: str):
    results = await run_holehe_in_thread(email)
    return {
        "email": email,
        "services_checked": len(results),
        "registrations_found": sum(1 for r in results if r["exists"]),
        "details": results
    }

Key API Functions Reference

Function Location Purpose
import_submodules(package) holehe/core.py:37-47 Recursively load all service modules
get_functions(modules, args) holehe/core.py:50-63 Extract callable checks, apply filters
launch_module(fn, email, client, out) holehe/core.py:66-71 Execute single check, normalize output
maincore() holehe/core.py:78-102 CLI entry point—reference for orchestration

Performance Considerations

  • Concurrency: Default execution spawns all ~120 checks simultaneously. Use trio.CapacityLimiter to throttle if hitting connection limits.
  • Timeouts: Individual service timeouts default to httpx defaults (5s connect, read). Pass custom timeout to httpx.AsyncClient for slower networks.
  • Rate limiting: Check result["rateLimit"] to identify services temporarily blocking your IP; implement retry logic or proxy rotation based on this signal.
  • Memory: Results append to a shared list; for large-scale batch processing, yield results via trio.MemorySendChannel instead.

Summary

  • Holehe's API centers on three functions: import_submodules, get_functions, and launch_module in holehe/core.py.
  • Dynamic loading enables automatic discovery of new services without code changes.
  • Async-first design with trio and httpx supports high-concurrency checks across 120+ platforms.
  • Direct imports of service functions enable targeted, fast checks for specific platforms.
  • Normalized output provides consistent fields (exists, rateLimit, emailrecovery) regardless of underlying service implementation.

Frequently Asked Questions

Can I use Holehe without the command line?

Yes. Import holehe.core functions directly in Python scripts. The CLI in maincore() is a thin wrapper around the same API functions—import_submodules, get_functions, and launch_module—that you can call programmatically for custom integrations.

How do I check just one specific service instead of all 120+?

Import the service function directly from its module path, such as from holehe.modules.social_media.github import github, then call it with (email, client, output_list). This bypasses module discovery and runs only that check.

What information does each API check return?

Every service returns a standardized dictionary with: name (service identifier), domain (service domain), exists (boolean registration status), rateLimit (boolean throttling indicator), emailrecovery (partial email if recovered), phoneNumber (digits if recovered), and others (service-specific metadata).

Does Holehe support custom HTTP proxies or headers?

Yes. Pass a configured httpx.AsyncClient instance to launch_module or service functions. Set proxies via httpx.AsyncClient(proxies={"https://": "http://proxy:8080"}) and custom headers through the headers parameter.

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 →