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

> Learn how to use Holehe for API checks with this complete guide. Programmatically verify email registrations across 120+ services using Python async functions for efficient OSINT.

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

---

**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.

```python
from holehe.core import import_submodules

modules = import_submodules("holehe.modules")

```

This implementation in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/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.

```python
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`](https://github.com/megadose/holehe/blob/main/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`.

```python
from holehe.core import launch_module

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

```

Found in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/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:

```python

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

```python
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`](https://github.com/megadose/holehe/blob/main/holehe/modules/software/office365.py). A valid service function signature is:

```python
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`](https://github.com/megadose/holehe/blob/main/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:

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

```python
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`](https://github.com/megadose/holehe/blob/main/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.