How to Import and Use Holehe Modules in Custom Python Scripts

You can import and use Holehe modules by leveraging two helper utilities from holehe.core — import_submodules() to dynamically load all service probes, and get_functions() to extract the callable async functions — then run them concurrently with Trio and an httpx.AsyncClient.

Holehe is an OSINT tool for checking email address registrations across hundreds of online services. While it ships with a command-line interface, its modular architecture makes it easy to embed directly into your own Python applications. According to the megadose/holehe source code, each service probe lives under holehe.modules and follows a consistent async signature, enabling programmatic reuse without CLI overhead.

Holehe's Module Architecture

The repository organizes email verification logic into independent modules, each targeting a specific platform (Twitter, Instagram, Amazon, etc.). Understanding this structure is key to importing and using Holehe modules effectively.

Module Location and Signature

All modules reside under the holehe.modules package. Each exposes a single asynchronous function with this signature:

async def <service_name>(email: str, client: httpx.AsyncClient, out: list) -> None

The function receives an email string, an HTTP client instance, and a shared results list. It appends discovery results directly to out rather than returning values.

Core Helper Utilities

In holehe/core.py, two functions handle module discovery and extraction:

Utility Purpose Source Location
import_submodules Dynamically imports every submodule under a package path using pkgutil.walk_packages holehe/core.py:37-48
get_functions Converts imported modules into a flat list of callable probe functions; respects --no-password-recovery flags holehe/core.py:50-64

These utilities separate discovery from execution, making the codebase extensible and testable.

How the CLI Uses These Modules

The maincore function in holehe/core.py demonstrates the standard workflow for importing and using Holehe modules:

  1. Parse command-line arguments
  2. Call import_submodules("holehe.modules") to load all service modules
  3. Call get_functions(modules, args) to extract probe functions
  4. Create an httpx.AsyncClient with user-specified timeout
  5. Launch probes concurrently via Trio's nursery.start_soon()
  6. Collect results from the shared out list

You can replicate this exact pattern in custom scripts, substituting your own logic for steps 1 and 6.

Example 1: Basic Email Check with All Modules

This script mirrors the CLI workflow but gives you direct access to raw results:

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

async def check_email(email: str):
    # 1. Import every module under holehe.modules

    modules = import_submodules("holehe.modules")
    
    # 2. Extract probe functions (default behavior, no filtering)

    functions = get_functions(modules)
    
    # 3. Create async HTTP client

    client = httpx.AsyncClient(timeout=10)
    
    # 4. Prepare results collection list

    results = []
    
    # 5. Launch all probes concurrently with Trio

    async with trio.open_nursery() as nursery:
        for probe in functions:
            nursery.start_soon(probe, email, client, results)
    
    await client.aclose()
    return results

if __name__ == "__main__":
    email = "target@example.com"
    data = trio.run(check_email, email)
    
    for entry in data:
        print(f"Service: {entry.get('name')}, Exists: {entry.get('exists')}")

Key points: The get_functions() call returns all available probes. Each probe runs independently; failures in one don't affect others due to launch_module's internal try/except wrapping in core.py.

Example 2: Selective Module Usage

For targeted checks, bypass get_functions() and import specific modules directly:

import httpx
import trio
from holehe.core import import_submodules

async def check_twitter_instagram(email: str):
    # Import full module tree

    modules = import_submodules("holehe.modules")
    
    # Select specific modules by fully-qualified name

    target_modules = {
        "holehe.modules.social_media.twitter",
        "holehe.modules.social_media.instagram"
    }
    
    # Extract probe functions manually

    selected_funcs = [
        modules[name].__dict__[name.split(".")[-1]]
        for name in target_modules
        if name in modules
    ]
    
    client = httpx.AsyncClient(timeout=8)
    out = []
    
    async with trio.open_nursery() as nursery:
        for fn in selected_funcs:
            nursery.start_soon(fn, email, client, out)
    
    await client.aclose()
    return out

if __name__ == "__main__":
    results = trio.run(check_twitter_instagram, "user@domain.com")
    print(results)

Key points: Direct module access reduces overhead when you only need specific platforms. The __dict__ lookup uses the final segment of the module path as the function name.

Example 3: Integration with Existing Async Workflows

Embed Holehe checks into larger Trio applications using queues or other communication patterns:

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

async def holehe_background_task(email: str, result_queue: trio.MemorySendChannel):
    """Run Holehe probes and send results through a Trio channel."""
    modules = import_submodules("holehe.modules")
    probes = get_functions(modules)
    
    async with httpx.AsyncClient(timeout=12) as client:
        results = []
        async with trio.open_nursery() as nursery:
            for probe in probes:
                nursery.start_soon(probe, email, client, results)
    
    # Send completed results to parent task

    await result_queue.send(results)

async def main_application():
    """Example of Holehe integrated into a larger program."""
    send_channel, receive_channel = trio.open_memory_channel(0)
    
    async with trio.open_nursery() as nursery:
        # Start Holehe check in background

        nursery.start_soon(
            holehe_background_task, 
            "alice@example.org", 
            send_channel
        )
        
        # Concurrently run other tasks...

        # Then collect Holehe results when ready

        report = await receive_channel.receive()
        
        print(f"Found {len(report)} services with registrations")
        for item in report:
            if item.get("exists"):
                print(f"  - {item['name']}: account exists")

if __name__ == "__main__":
    trio.run(main_application)

Key points: The results list behaves like standard Python data — serializable, storable, and transformable. Use Trio channels, asyncio queues, or direct return values depending on your architecture.

Key Source Files for Reference

File Role
holehe/core.py Central orchestration: import_submodules(), get_functions(), maincore()
holehe/modules/__init__.py Package marker enabling module discovery
holehe/modules/social_media/twitter.py Example probe showing expected function signature
holehe/localuseragent.py Random User-Agent generator used by probes
holehe/instruments.py Trio progress bar implementation for CLI

Summary

  • Import modules dynamically with import_submodules("holehe.modules") from holehe/core.py
  • Extract probe functions using get_functions() for automatic discovery, or reference modules directly by their fully-qualified names
  • Execute probes concurrently via Trio's nursery pattern with a shared httpx.AsyncClient
  • Handle results flexibly — the out list receives standard dictionaries with name, exists, and other service-specific fields
  • Reuse any probe individually by importing its module and calling its async function directly

Frequently Asked Questions

Can I use Holehe modules without the Trio concurrency library?

No. According to the megadose/holehe source code, all probe functions are designed for Trio and use trio.open_nursery() for concurrent execution. The probes themselves are standard async def functions, but the execution pattern expects Trio. Using asyncio would require wrapping or rewriting the concurrency logic.

How do I filter out password-recovery probes when importing modules?

Pass a configuration object to get_functions() with the nopasswordrecovery attribute set. The function signature is get_functions(modules, args=None), and when args has nopasswordrecovery=True, it filters out probes that only check password recovery endpoints. Check holehe/core.py:50-64 for the exact implementation.

What data structure does each probe return to the results list?

Each probe appends a dictionary with consistent keys: name (service name), domain (service domain), method (detection method), frequent_rate_limit (boolean), rateLimit (boolean), exists (boolean), and optionally emailrecovery, phoneNumber, or others with leaked account metadata. The exact fields vary by service.

Can I modify the HTTP timeout for individual probes?

No timeouts are set per-probe. Create the httpx.AsyncClient with your desired timeout before launching probes — this single client instance is shared across all concurrent checks. In holehe/core.py, the CLI passes --timeout directly to httpx.AsyncClient(timeout=...).

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 →