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:
-
import_submodules("holehe.modules")walks the package tree usingpkgutil.walk_packagesand dynamically loads every service module. -
get_functions()inspects each loaded module and extracts the public async function—the last callable object defined in the file. -
launch_moduleschedules each service inside a Trio nursery, passing a sharedhttpx.AsyncClientand a sharedoutlist where results are appended. -
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 holehegives you the full public API. -
Import three functions:
import_submodules,get_functions, andlaunch_modulefromholehe.coreare 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.AsyncClientto alllaunch_modulecalls for connection efficiency. -
Consume standard dicts: Results use a fixed schema with
exists,rateLimit,error,emailrecovery, andphoneNumberkeys. -
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →