How to Use Holehe for API Checks: A Complete Guide to Programmatic Email OSINT
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.
from holehe.core import import_submodules
modules = import_submodules("holehe.modules")
This implementation in 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.
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 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.
from holehe.core import launch_module
results = []
await launch_module(service_func, email, client, results)
Found in 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:
# 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:
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. A valid service function signature is:
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) 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:
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:
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.CapacityLimiterto throttle if hitting connection limits. - Timeouts: Individual service timeouts default to httpx defaults (5s connect, read). Pass custom
timeouttohttpx.AsyncClientfor 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.MemorySendChannelinstead.
Summary
- Holehe's API centers on three functions:
import_submodules,get_functions, andlaunch_moduleinholehe/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.
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 →