How to Integrate Holehe as a Python Library in Custom OSINT Tools

You can integrate holehe as a Python library by importing import_submodules, get_functions, and launch_module from holehe.core, then running the async service checks with Trio and httpx.

Holehe is a fast, asynchronous email-to-service correlation tool maintained by megadose. While it ships with a command-line interface, its core engine exposes a public API that lets you embed email reconnaissance directly into custom OSINT pipelines. This guide shows exactly how to integrate holehe as a Python library using the same primitives the CLI relies on.


Core Architecture of Holehe's Public API

The library's design separates discovery, execution, and result handling into distinct layers in holehe/core.py.

Component Role Key Source File
holehe.core Central engine providing import_submodules, get_functions, launch_module, and maincore. holehe/core.py
holehe.modules Package containing one file per service (social media, e-commerce, etc.). Each exports a single async function with signature (email, client, out). holehe/modules/social_media/twitter.py (example)
holehe.instruments.TrioProgress Optional Trio instrument for progress bar display during async execution. holehe/instruments.py
holehe.localuseragent.ua Random User-Agent generator used per request to reduce fingerprinting. holehe/localuseragent.py

The execution flow works as follows:

  1. import_submodules("holehe.modules") walks the package tree using pkgutil.walk_packages and dynamically loads every service module.

  2. get_functions() inspects each loaded module and extracts the public async function—the last callable object defined in the file.

  3. launch_module schedules each service inside a Trio nursery, passing a shared httpx.AsyncClient and a shared out list where results are appended.

  4. Result normalization ensures every module returns a dict with consistent keys: name, domain, rateLimit, error, exists, emailrecovery, phoneNumber, others.

Because maincore is CLI-oriented, you bypass it entirely and call the lower-level helpers directly for programmatic use.


Step-by-Step Integration Guide

Follow these steps to embed holehe into your own Python-based OSINT tooling.

Step 1: Install the Library

pip install holehe

Or add to your requirements.txt:


holehe>=1.60

Step 2: Import Core Helpers

from holehe.core import import_submodules, get_functions, launch_module

These three functions are the only imports you need for full functionality.

Step 3: Create a Shared HTTPX Client

import httpx

client = httpx.AsyncClient(timeout=10)  # matches CLI default

Reusing a single client across all service checks saves connection overhead and respects HTTP/2 multiplexing where available.

Step 4: Load and Filter Modules

all_modules = import_submodules("holehe.modules")

# Optional: filter to specific service categories

wanted = {
    k: v for k, v in all_modules.items()
    if any(s in k for s in ("twitter", "instagram", "snapchat"))
}

functions = get_functions(wanted)  # or pass `all_modules` for everything

The get_functions() helper returns a list of async callables ready for execution.

Step 5: Run Checks Concurrently with Trio

import trio

async def run_checks(email):
    out = []
    async with client:
        async with trio.open_nursery() as nursery:
            for fn in functions:
                nursery.start_soon(launch_module, fn, email, client, out)
    return out

Trio is holehe's native async runtime. The nursery pattern ensures all services run in parallel and cleans up properly on cancellation or error.

Step 6: Process Results Programmatically

def analyze_results(results):
    for r in results:
        if r["exists"]:
            print(f"[+] {r['domain']}: account found")
        elif r["rateLimit"]:
            print(f"[x] {r['domain']}: rate limited")
        elif r["error"]:
            print(f"[!] {r['domain']}: {r['error']}")
        else:
            print(f"[-] {r['domain']}: no account")

# Run it

results = trio.run(run_checks, "target@example.com")
analyze_results(results)

Each result dict follows the same schema used by the CLI, so you can feed data directly into databases, SIEMs, or graph-based OSINT platforms like Maltego.

Step 7: Optional CSV Export

from holehe.core import export_csv
import argparse

# Create minimal namespace matching CLI expectations

args = argparse.Namespace(csvoutput=True, filename="results.csv")
export_csv(results, args, email="target@example.com")

The export_csv helper reuses the CLI's formatting logic without requiring subprocess calls.


Complete Minimal Example

This self-contained script mirrors CLI behavior while exposing raw results for custom handling:

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


async def holehe_check(email: str):
    # Load all available service modules

    modules = import_submodules("holehe.modules")
    funcs = get_functions(modules)

    # Execute all checks concurrently

    async with httpx.AsyncClient(timeout=10) as client:
        out = []
        async with trio.open_nursery() as nursery:
            for fn in funcs:
                nursery.start_soon(launch_module, fn, email, client, out)

    # Return sorted by service name for consistent output

    return sorted(out, key=lambda x: x["name"])


if __name__ == "__main__":
    email = "victim@example.com"
    results = trio.run(holehe_check, email)

    for r in results:
        status = "[+]" if r["exists"] else "[-]"
        recovery = f" (recovery: {r['emailrecovery']})" if r.get("emailrecovery") else ""
        print(f"{status} {r['domain']}{recovery}")

Replace the print block with your own logic: Elasticsearch indexing, Neo4j graph insertion, or webhook triggers to other OSINT tools.


Key Source Files Reference

File Purpose
holehe/core.py Main orchestration with import_submodules, get_functions, launch_module, export_csv, and result formatting
holehe/modules/**/*.py Individual service implementations—one async function per target site
holehe/instruments.py TrioProgress class for optional CLI progress bars
holehe/localuseragent.py ua() function for randomized User-Agent strings

All paths are relative to the repository root at megadose/holehe.


Advanced Integration Patterns

Selective Service Loading for Performance

from pathlib import Path

def load_category(category: str):
    """Load only modules from a specific subdirectory."""
    base = Path(__file__).parent / "holehe" / "modules" / category
    modules = {}
    for mod in base.glob("*.py"):
        if mod.stem == "__init__":
            continue
        # Build dotted path and import

        dotted = f"holehe.modules.{category}.{mod.stem}"
        modules[dotted] = __import__(dotted, fromlist=[None])
    return modules

This avoids importing 100+ modules when you only need social media checks.

Custom Result Handlers with Structured Logging

import json
import structlog

logger = structlog.get_logger()

async def logged_check(email: str):
    results = await holehe_check(email)
    for r in results:
        logger.info(
            "holehe_check_complete",
            email=email,
            domain=r["domain"],
            exists=r["exists"],
            rate_limited=r["rateLimit"],
            error=r["error"],
        )
    return results

Structured logging enables downstream SIEM correlation and audit trails.

Integration with Async Web Frameworks

from fastapi import FastAPI
import trio
from trio_asyncio import trio_as_aio

app = FastAPI()

@app.get("/check/{email}")
async def api_check(email: str):
    # Bridge Trio to asyncio for FastAPI compatibility

    results = await trio_as_aio(trio.run, holehe_check, email)
    return {"email": email, "services": results}

Use trio-asyncio or similar bridges when mixing holehe with asyncio-based tools.


Summary

  • Install once: pip install holehe gives you the full public API.

  • Import three functions: import_submodules, get_functions, and launch_module from holehe.core are the complete integration surface.

  • Use Trio: The library is built on Trio; use trio.open_nursery() for concurrent execution.

  • Share one client: Pass a single httpx.AsyncClient to all launch_module calls for connection efficiency.

  • Consume standard dicts: Results use a fixed schema with exists, rateLimit, error, emailrecovery, and phoneNumber keys.

  • Avoid subprocess: Direct API calls are faster, cleaner, and give you full control over error handling and result routing.


Frequently Asked Questions

Can I use holehe with asyncio instead of Trio?

Holehe's core in holehe/core.py uses Trio-specific APIs like trio.open_nursery(). To use it with asyncio, bridge with trio-asyncio or run holehe in a separate thread and communicate via queues. Direct asyncio compatibility would require modifying launch_module and the nursery patterns.

How do I add my own custom service checks?

Create a Python file in holehe/modules/ or a custom package, define an async function with signature async def myservice(email, client, out), and append a result dict to out. The get_functions() helper will auto-discover it if you pass your package name to import_submodules().

What rate limiting protections exist?

Each module handles its own rate-limit detection and sets rateLimit: True in results when triggered. The shared httpx.AsyncClient does not enforce global delays—implement your own throttling (e.g., trio.sleep() between batches) if scanning multiple emails against the same service.

Is the User-Agent rotation automatic?

Yes. Every request in the standard modules pulls from holehe.localuseragent.ua(), which returns a randomized desktop browser User-Agent string. No additional configuration is required for basic OPSEC.

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 →