How to Run Multiple Holehe Modules Concurrently Using the Python API
Holehe uses Trio’s nursery pattern to launch asynchronous checks against hundreds of services simultaneously, sharing a single HTTP client for optimal performance.
Holehe is an open-source OSINT tool that discovers whether an email address has been used on hundreds of online services. Instead of executing checks sequentially, the Python API leverages Trio—a structured concurrency library—to query multiple endpoints in parallel. This article explains how to run multiple Holehe modules concurrently using the exact utilities exposed in megadose/holehe.
Understanding the Core Concurrency Model
The heavy lifting happens inside holehe/core.py, where the library orchestrates async execution. Rather than using asyncio, Holehe builds on Trio’s nursery concept: a lightweight task manager that starts many coroutines at once and waits for all of them to finish.
The workflow follows four distinct steps:
- Import every submodule under
holehe.modulesviaimport_submodules. - Extract check functions from those modules using
get_functions. - Create a shared
httpx.AsyncClient(lines 13‑14 inholehe/core.py) to enable TCP connection reuse across all tasks. - Open a Trio nursery (
trio.open_nursery()) and launch a coroutine for each module vialaunch_module.
The relevant source snippet from holehe/core.py (lines 18‑24) demonstrates the concurrent launch:
# https://github.com/megadose/holehe/blob/master/holehe/core.py#L18-L21
client = httpx.AsyncClient(timeout=timeout)
# https://github.com/megadose/holehe/blob/master/holehe/core.py#L23-L24
async with trio.open_nursery() as nursery:
for website in websites:
# https://github.com/megadose/holehe/blob/master/holehe/core.py#L20-L22
nursery.start_soon(launch_module, website, email, client, out)
Each launch_module coroutine (lines 66‑78) awaits the module’s specific async function and captures exceptions, appending structured results to the shared out list. Because all coroutines share the same httpx.AsyncClient, the underlying TCP connections are reused, dramatically reducing latency when querying hundreds of services.
Running All Modules Concurrently
To run every available Holehe check in parallel, use the same helper functions that power the CLI. The following script imports all modules, gathers their check functions, and executes them within a single Trio nursery:
import httpx
import trio
from holehe.core import import_submodules, get_functions, launch_module
EMAIL = "test@example.com"
# 1️⃣ Import every module under `holehe.modules`
modules = import_submodules("holehe.modules")
# 2️⃣ Turn the modules into callable check functions
websites = get_functions(modules)
# 3️⃣ Shared async HTTP client (timeout = 10s)
client = httpx.AsyncClient(timeout=10)
# 4️⃣ Container for results
out = []
async def run_all():
async with trio.open_nursery() as nursery:
for site in websites:
nursery.start_soon(launch_module, site, EMAIL, client, out)
# All tasks finished – sort and print
out.sort(key=lambda r: r["name"])
for r in out:
print(r)
trio.run(run_all)
Key implementation details:
import_submodulesdynamically loads every Python file underholehe/modules/.nursery.start_soonschedules each check without awaiting, allowing all network I/O to proceed in parallel.- Results populate the shared
outlist; once the nursery exits, you can process them for CSV export, database insertion, or JSON serialization.
Running a Specific Subset of Modules
You can limit concurrency to specific services while still benefiting from Trio’s parallel execution. Filter the function list by __name__ before launching the nursery:
import httpx
import trio
from holehe.core import import_submodules, get_functions, launch_module
EMAIL = "user@example.org"
TARGETS = {"github", "gmail", "instagram"} # Select specific module names
modules = import_submodules("holehe.modules")
all_funcs = get_functions(modules)
# Filter only the desired modules
selected = [fn for fn in all_funcs if fn.__name__ in TARGETS]
client = httpx.AsyncClient(timeout=8)
out = []
async def run_selected():
async with trio.open_nursery() as nursery:
for mod in selected:
nursery.start_soon(launch_module, mod, EMAIL, client, out)
for r in sorted(out, key=lambda x: x["name"]):
print(r)
trio.run(run_selected)
This approach reduces noise and execution time when you only need to verify accounts on high-value platforms rather than the entire module catalog.
Integrating Holehe into Larger Async Applications
For production environments using FastAPI, Sanic, or other async frameworks, wrap the checking routine in a reusable coroutine. This pattern ensures proper client lifecycle management and clean nursery scoping:
import httpx
import trio
from holehe.core import import_submodules, get_functions, launch_module
async def check_email(email: str) -> list[dict]:
"""Run all Holehe modules concurrently and return sorted results."""
modules = import_submodules("holehe.modules")
funcs = get_functions(modules)
client = httpx.AsyncClient(timeout=12)
results = []
async with trio.open_nursery() as nursery:
for fn in funcs:
nursery.start_soon(launch_module, fn, email, client, results)
await client.aclose()
results.sort(key=lambda r: r["name"])
return results
# Example usage inside an async FastAPI endpoint
# async def post_email(payload: EmailPayload):
# data = await check_email(payload.email)
# return {"checks": data}
By encapsulating the logic in check_email, you create an awaitable interface that other async functions can call directly, maintaining the efficiency of concurrent execution without blocking the event loop.
Summary
- Trio nurseries manage the concurrent execution of Holehe modules, spawning a task for every service check simultaneously.
- Shared
httpx.AsyncClientinstances enable connection pooling, reducing overhead when querying hundreds of endpoints. import_submodulesandget_functionsfromholehe/core.pyprovide programmatic access to the same module registry used by the CLI.launch_modulehandles exception isolation, ensuring that a single failed request does not crash the entire batch.- The API supports both full-catalog and targeted execution patterns, allowing flexibility between comprehensive scans and focused investigations.
Frequently Asked Questions
What is the difference between using Trio and asyncio for Holehe concurrency?
Trio provides structured concurrency through nurseries, which guarantees that all spawned tasks complete before the context manager exits. While Holehe could theoretically use asyncio, the codebase specifically implements trio.open_nursery() in holehe/core.py to enforce stricter task lifetime management and cleaner exception propagation across hundreds of simultaneous network requests.
How does the shared HTTP client improve performance when running multiple modules?
According to the implementation in holehe/core.py, all coroutines receive the same httpx.AsyncClient instance. This allows HTTP/1.1 connection reuse and HTTP/2 multiplexing across different service modules, eliminating the TCP handshake overhead that would occur if each module instantiated its own client.
Can I integrate Holehe into an existing asyncio application?
Yes, but you must bridge the two async worlds. Since Holehe relies on Trio, use trio.lowlevel.start_guest_run or run Trio in a separate thread if your main loop uses asyncio. Alternatively, structure your application to call trio.run() inside an executor thread when invoking check_email, then await the future in your asyncio code.
What happens if one module raises an exception during concurrent execution?
The launch_module wrapper in holehe/core.py (lines 66‑78) catches exceptions internally and appends a failure result to the output list. This isolation prevents a single malformed response or network timeout from crashing the entire nursery, ensuring that all other modules continue executing until completion.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →