Holehe CLI vs Programmatic API: Key Differences for OSINT Email Checks
The Holehe CLI provides a ready-to-run terminal interface with formatted output and CSV export, while the programmatic API returns raw Python data structures for custom integration.
Holehe is an open-source email OSINT tool by megadose that checks for account registrations across hundreds of websites. When deciding how to run Holehe, you'll choose between its command-line interface for quick investigations or its Python API for embedded workflows. Both approaches ultimately execute the same async core logic in holehe.core, but they differ significantly in input handling, output formatting, and control flow.
Entry Points and Execution Flow
CLI: Console Script to Trio Runner
The CLI installs as a console script named holehe, defined in setup.py as holehe=holehe.core:main. When invoked, this calls holehe.core.main(), which immediately executes trio.run(maincore).
In holehe/core.py lines 32-34, the main() function serves as the synchronous entry point that bridges to the async world.
# From holehe/core.py
def main():
trio.run(maincore)
The maincore() function handles argument parsing via argparse starting at line 179, then orchestrates module execution through a Trio nursery.
API: Direct Async Function Imports
The programmatic API skips the console script entirely. You import async functions directly from holehe.core and manage execution yourself.
from holehe.core import import_submodules, get_functions, launch_module
import httpx
async def custom_check(email):
modules = import_submodules("holehe.modules")
class Args:
csvoutput = False
nopasswordrecovery = False
websites = get_functions(modules, Args())
async with httpx.AsyncClient(timeout=10) as client:
out = []
for website in websites:
await launch_module(website, email, client, out)
return out
This approach gives you fine-grained control over which modules run, concurrency strategies, and result handling.
Argument Handling: Parsed Flags vs Direct Parameters
The CLI uses argparse with flags defined in maincore() at lines 179-196. Key options include:
--only-used— Show only sites where the email is registered--no-color— Disable terminal colors--csv— Export results to a timestamped CSV file--timeout— Set request timeout in seconds
The API bypasses argparse completely. You construct parameter objects directly, as shown in the Args class above. This eliminates string parsing overhead and enables dynamic configuration at runtime.
Output Formats: Terminal Tables vs Raw Data Structures
CLI Output Characteristics
The CLI produces multiple side effects after the async run completes:
- Formatted table: Color-coded results printed via
print_result - Progress bar: Visual feedback through
TrioProgressinstrument - CSV export: Optional file creation via
export_csv - Terminal clearing: Screen management for clean display
These features are implemented in holehe/core.py lines 27-31.
API Output Characteristics
The programmatic API returns a plain Python list of dictionaries containing raw findings. Each dictionary includes standard fields like name, domain, method, rateLimit, exists, emailrecovery, phoneNumber, and others.
# Example returned structure
[
{
"name": "twitter",
"domain": "twitter.com",
"method": "register",
"rateLimit": False,
"exists": True,
"emailrecovery": None,
"phoneNumber": "+1-xxx-xxx-xxxx",
"others": None
}
]
No formatting, no files, no terminal manipulation — just structured data for your application to process.
Concurrency and Control Flow
CLI: Full Module Sweep with Fixed Orchestration
The CLI discovers all modules and launches them together inside a Trio nursery. From holehe/core.py lines 18-22:
async with trio.open_nursery() as nursery:
for website in websites:
nursery.start_soon(launch_module, website, email, client, out)
This approach maximizes throughput but offers no granularity. Every discovered module runs, and the CLI blocks until completion.
API: Selective Execution and Custom Strategies
With the API, you control exactly which modules execute and how:
# Run only specific modules
target_modules = ["github", "twitter", "instagram"]
filtered = [w for w in websites if w.__name__.split(".")[-1] in target_modules]
# Custom concurrency: sequential with delays
import asyncio
for website in filtered:
await launch_module(website, email, client, out)
await asyncio.sleep(1) # Rate limiting
Error Handling Behavior
Both interfaces use the same launch_module function, which catches exceptions internally. In holehe/core.py lines 168-176, errors are recorded in the output list rather than raised:
try:
await module(email, client, out)
except Exception as e:
name = str(module).split('<function ')[1].split(' ')[0]
out.append({"name": name, "error": str(e)})
The CLI continues execution and prints a summary showing any failures. The API returns these error entries in the list, allowing your code to detect them via the "error" key and respond appropriately — retry, log, or raise as needed.
When to Use Each Interface
| Scenario | Recommended Approach |
|---|---|
| One-off email investigation | CLI — fastest path to visual results |
| Automated security scanning | API — integrate with existing Python tooling |
| Custom output formatting | API — raw data for your own reports |
| CI/CD pipeline integration | API — programmatic error handling and exit codes |
| Large-scale batch processing | API — control rate limiting and resource usage |
| Quick verification with CSV export | CLI with --csv flag |
Working Code Examples
Complete CLI Usage
# Basic check with formatted output
holehe target@example.com
# Export to CSV, show only confirmed accounts
holehe target@example.com --only-used --csv --timeout 15
# No color for pipe processing
holehe target@example.com --no-color | grep "True"
Complete API Integration
import asyncio
import httpx
from holehe.core import import_submodules, get_functions, launch_module
async def holehe_api_check(email, timeout=10, modules=None):
"""
Programmatic Holehe check with optional module filtering.
Returns structured results for integration.
"""
# Load module discovery
module_root = import_submodules("holehe.modules")
# Minimal args namespace
class Config:
csvoutput = False
nopasswordrecovery = False
# Get target functions
all_websites = get_functions(module_root, Config())
# Apply module filter if specified
if modules:
targets = [w for w in all_websites
if any(m in w.__name__ for m in modules)]
else:
targets = all_websites
# Execute with controlled client
results = []
async with httpx.AsyncClient(timeout=timeout) as client:
for site in targets:
await launch_module(site, email, client, results)
# Separate successes and errors
successes = [r for r in results if "error" not in r]
failures = [r for r in results if "error" in r]
return {"found": successes, "errors": failures, "total": len(results)}
# Usage
output = asyncio.run(holehe_api_check(
"target@example.com",
modules=["github", "gitlab", "twitter"]
))
Key Source Files
| File | Purpose |
|---|---|
holehe/core.py |
Main orchestration: main(), maincore(), launch_module(), argument parsing, and result formatting |
holehe/instruments.py |
TrioProgress class for CLI progress bars |
holehe/modules/__init__.py |
Module discovery and loading utilities |
setup.py |
Console script entry point definition |
Summary
- The CLI wraps Holehe's core in a user-friendly layer with
argparse, colored terminal output, progress indicators, and CSV export — ideal for standalone investigations. - The API exposes
launch_module,import_submodules, andget_functionsdirectly, returning raw Python lists for custom processing and integration. - Both interfaces execute identical verification logic in
holehe.modules; the difference is presentation and control. - Choose the CLI for immediate visual results, the API for embedded automation and custom workflows.
Frequently Asked Questions
Can I use the CLI and API together in the same project?
Yes. Import holehe.core functions for programmatic use while still invoking the holehe command for manual checks. They share no global state and can coexist safely.
Does the API support CSV export like the CLI?
Not automatically. The API returns raw data; you implement CSV export using Python's csv module or pandas. The CLI's export_csv function in holehe/core.py demonstrates the expected format if you want to match it.
Is there a performance difference between CLI and API?
No meaningful difference in core execution speed. Both use the same Trio-based async engine and httpx.AsyncClient. The CLI adds minimal overhead for progress rendering and terminal output.
How do I filter specific modules when using the API?
Filter the list returned by get_functions() before calling launch_module(). Match against function.__name__ or maintain an allowlist of target modules, as shown in the complete API example 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 →