How to Use the holehe API in a Python Script
The holehe library exposes its email investigation capabilities through the holehe.core module, allowing developers to programmatically query hundreds of online services using native Python async patterns without invoking the command-line interface.
The megadose/holehe repository is primarily known as an OSINT tool for detecting where an email address has been registered, but its internal architecture is designed for library usage. By importing functions from holehe.core, you can dynamically load service modules, execute concurrent checks via Trio, and process results—all from within your Python applications.
Understanding the holehe Architecture
The library’s design separates service discovery from execution, making it ideal for programmatic integration.
Dynamic Module Loading
In holehe/core.py, the import_submodules() function recursively imports every Python file within the holehe.modules package. This discovers hundreds of service-specific checkers (e.g., Instagram, Amazon, GitHub) without hardcoding imports. According to the source, this function walks the package directory and returns a dictionary of loaded modules, enabling the library to support new services automatically as they are added to the codebase.
Function Extraction
Once modules are loaded, get_functions() extracts the async checking functions from each module. Every service module in holehe contains a single async function that accepts an email address, an HTTP client, and a shared results list. The get_functions() helper filters the module namespace to return only these callable check functions, providing a clean list of targets for execution.
Concurrent Execution with Trio
The launch_module() helper in holehe/core.py standardizes error handling and result formatting when calling individual checkers. The library uses Trio (a structured concurrency library) rather than asyncio for its internal orchestration. The core logic opens a Trio nursery and spawns a task for each service function, allowing hundreds of HTTP requests to run concurrently while respecting backpressure and cancellation semantics.
Implementing the holehe API Programmatically
Below are three production-ready patterns for embedding holehe functionality in your scripts.
Minimal Synchronous Wrapper
For scripts that require a blocking interface, wrap the async core in asyncio.run(). This pattern uses httpx.AsyncClient for connection pooling and returns sorted results.
import asyncio
import httpx
import trio
from holehe.core import import_submodules, get_functions, launch_module
async def _run_check(email: str):
# Dynamically load all service modules
modules = import_submodules("holehe.modules")
check_functions = get_functions(modules)
# Shared HTTP client for connection reuse
client = httpx.AsyncClient(timeout=10)
results = []
async with trio.open_nursery() as nursery:
for func in check_functions:
nursery.start_soon(launch_module, func, email, client, results)
await client.aclose()
return sorted(results, key=lambda r: r["name"])
def check_email(email: str):
"""Blocking entry point for synchronous scripts."""
return asyncio.run(_run_check(email))
# Usage
if __name__ == "__main__":
data = check_email("target@example.com")
for entry in data:
print(f"{entry['name']}: {entry['exists']}")
Pure Async Integration
For applications already using Trio or asyncio, call the check functions directly within your async event loop. This avoids the overhead of asyncio.run() and integrates with existing async contexts.
import httpx
import trio
from holehe.core import import_submodules, get_functions, launch_module
async def check_email_async(email: str):
modules = import_submodules("holehe.modules")
functions = get_functions(modules)
async with httpx.AsyncClient(timeout=10) as client:
results = []
async with trio.open_nursery() as nursery:
for func in functions:
nursery.start_soon(launch_module, func, email, client, results)
return sorted(results, key=lambda r: r["name"])
# Trio entry point
if __name__ == "__main__":
results = trio.run(check_email_async, "user@example.org")
for r in results:
print(r)
Exporting Results to CSV
To replicate the CLI’s CSV export functionality, call export_csv() from holehe.core. This requires mocking a minimal configuration object that the function expects.
import httpx
import trio
from holehe.core import import_submodules, get_functions, launch_module, export_csv
async def check_and_export(email: str):
modules = import_submodules("holehe.modules")
functions = get_functions(modules)
async with httpx.AsyncClient(timeout=10) as client:
results = []
async with trio.open_nursery() as nursery:
for func in functions:
nursery.start_soon(launch_module, func, email, client, results)
# Stub configuration object for export_csv
class ExportConfig:
csvoutput = True
export_csv(results, ExportConfig(), email)
return results
if __name__ == "__main__":
trio.run(check_and_export, "admin@example.net")
The export_csv() function generates a timestamped file named holehe_<timestamp>_<email>_results.csv in the current working directory, identical to the command-line tool’s -C flag behavior.
Key Source Files and Functions
| File Path | Purpose |
|---|---|
holehe/core.py |
Contains import_submodules(), get_functions(), launch_module(), and export_csv()—the primary API surface for programmatic use. |
holehe/modules/ |
Directory hierarchy where each .py file implements an async function checking a specific service (e.g., social_media/instagram.py). |
holehe/__init__.py |
Declares package metadata including __version__. |
When importing from holehe.core, you are interacting with the same functions that power the CLI’s main execution loop, ensuring behavioral parity between script-based and command-line usage.
Summary
- Dynamic Discovery: Use
import_submodules("holehe.modules")to automatically load all available service checkers without manual imports. - Async Core: The library relies on Trio for structured concurrency; wrap calls in
trio.run()or useasyncio.run()for synchronous compatibility. - Standardized Interface:
launch_module()ensures consistent error handling and result formatting across all services. - Data Export: The
export_csv()function provides built-in persistence matching the CLI’s output format. - Client Reuse: Pass a shared
httpx.AsyncClientinstance tolaunch_module()for efficient connection pooling across hundreds of requests.
Frequently Asked Questions
Can I use holehe as a Python library instead of installing the CLI?
Yes. While holehe is packaged with a command-line entry point, its core functionality resides in holehe.core. By importing import_submodules, get_functions, and launch_module, you can execute email checks entirely within Python without spawning subprocesses or parsing shell output.
Why does holehe use Trio instead of asyncio?
The holehe/core.py implementation uses Trio nurseries for structured concurrency, providing stricter control over task cancellation and error propagation compared to asyncio.gather(). When using the API, you should drive the event loop with trio.run() or use Trio’s guest mode if integrating with an existing asyncio application.
How do I filter which services to check?
The get_functions() function returns a list of all available checker functions. You can filter this list by inspecting func.__module__ or function names before passing them to the nursery. For example, exclude modules containing "shopping" by checking if "shopping" is in func.__module__.
What format does the holehe API return?
Each service checker appends a dictionary to the results list with standardized keys including "name" (service identifier), "exists" (boolean indicating registration status), and "email" (the queried address). Results are typically sorted by the "name" key before return, as shown in the code examples above.
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 →