# How to Run Multiple Holehe Modules Concurrently Using the Python API

> Learn to run multiple Holehe modules concurrently with Python API. Discover how Holehe's nursery pattern enables simultaneous checks and optimal performance.

- Repository: [Palenath/holehe](https://github.com/megadose/holehe)
- Tags: how-to-guide
- Published: 2026-08-24

---

**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`](https://github.com/megadose/holehe/blob/main/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:
1. **Import every submodule** under `holehe.modules` via `import_submodules`.
2. **Extract check functions** from those modules using `get_functions`.
3. **Create a shared `httpx.AsyncClient`** (lines 13‑14 in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py)) to enable TCP connection reuse across all tasks.
4. **Open a Trio nursery** (`trio.open_nursery()`) and launch a coroutine for each module via `launch_module`.

The relevant source snippet from [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) (lines 18‑24) demonstrates the concurrent launch:

```python

# 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:

```python
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_submodules` dynamically loads every Python file under `holehe/modules/`.
- `nursery.start_soon` schedules each check **without awaiting**, allowing all network I/O to proceed in parallel.
- Results populate the shared `out` list; 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:

```python
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:

```python
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.AsyncClient`** instances enable connection pooling, reducing overhead when querying hundreds of endpoints.
- **`import_submodules`** and **`get_functions`** from [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) provide programmatic access to the same module registry used by the CLI.
- **`launch_module`** handles 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`](https://github.com/megadose/holehe/blob/main/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`](https://github.com/megadose/holehe/blob/main/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`](https://github.com/megadose/holehe/blob/main/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.