How Long Does It Take to Run All Holehe Modules on One Email? A Performance Deep Dive
Running all ~120 Holehe modules against a single email address typically completes in 5–12 seconds on a standard broadband connection, with the exact duration printed at the end of each scan.
Holehe is an open-source OSINT tool that checks whether an email address is registered across more than 120 online services. According to the megadose/holehe source code, the tool's performance characteristics are determined by its concurrent async architecture and network-bound request pattern rather than CPU processing.
How Holehe Measures and Executes a Full Scan
The Module Loading Pipeline
Before any network requests fire, Holehe performs three setup steps defined in holehe/core.py:
-
Dynamic submodule import —
import_submodules()(lines 37–47) recursively imports every Python file underholehe/modules/, discovering approximately 120 service-specific checkers. -
Function extraction —
get_functions()(lines 50–63) filters these imports to collect only async callables matching the signatureasync def service_name(email, client, out). -
Concurrent task spawning —
launch_module()(lines 66–78) wraps each callable as a Trio task, enabling parallel execution across all services.
This architecture means all ~120 modules start simultaneously rather than running sequentially.
Timing Implementation
The elapsed time calculation is straightforward and visible in holehe/core.py lines 210–215:
start_time = time.time()
# ... trio nursery runs all modules ...
elapsed = time.time() - start_time
print(f"{len(modules)} websites checked in {elapsed:.2f} seconds")
The timer starts before module execution and stops after every task completes or times out. No per-module timing is captured—only total wall-clock duration.
What Determines Total Runtime
Holehe's performance is network-bound, not CPU-bound. Each module performs a single HTTP request (typically to a "forgot password" or account recovery endpoint). The dominant factors are:
-
Per-request latency — Most services respond in 200ms–800ms, though global CDNs and server locations vary this widely.
-
Default timeout — Set to 10 seconds via
parser.add_argument("-T","--timeout", default=10, type=float)(lines 194–196). Slow or unresponsive services consume up to this limit. -
Concurrent connection limits — Holehe uses
httpx.AsyncClientwith Trio; practical TCP limits and remote server rate-limiting cap true parallelism. -
Rate-limit detection — When a service returns HTTP 429 or similar, Holehe skips further processing for that module (
rateLimit=Truein response dict), which can reduce total time.
Practical Runtime Ranges
| Scenario | Expected Duration |
|---|---|
| Fast, stable connection; most services responsive | 5–7 seconds |
| Typical residential broadband with moderate latency | 8–12 seconds |
| High-latency or congested network | 12–20 seconds |
| Multiple timeouts at 10s default | Up to ~30 seconds (rare, as most fail faster) |
The worst-case theoretical runtime would be ~120 × timeout if every service hung, but in practice connection timeouts and TCP failures resolve in 3–5 seconds rather than the full 10-second window.
Running Holehe and Observing Performance
Command-Line Usage
# Default execution with timing output
holehe user@example.com
# Example output:
# [+] twitter.com
# [-] facebook.com
# ...
# 120 websites checked in 9.34 seconds
Adjusting Performance via Timeout
# Faster failure for unresponsive services
holehe -T 5 user@example.com
# More patience for slow international endpoints
holehe -T 15 user@example.com
Programmatic Usage with Timing
import time
import trio
import httpx
from holehe.core import get_functions, import_submodules
async def timed_scan(email: str, timeout: float = 10.0):
start = time.time()
# Load all modules
modules = import_submodules("holehe.modules")
functions = get_functions(modules)
# Execute concurrently
client = httpx.AsyncClient(timeout=timeout)
nursery_results = []
async with trio.open_nursery() as nursery:
for func in functions:
nursery.start_soon(
launch_module, func, email, client, nursery_results
)
await client.aclose()
elapsed = time.time() - start
print(f"Scanned {len(functions)} services in {elapsed:.2f}s")
return nursery_results, elapsed
# Run
results, duration = trio.run(timed_scan, "user@example.com")
Key Implementation Files
| File | Lines | Purpose |
|---|---|---|
holehe/core.py |
37–47 | import_submodules() — dynamic module discovery |
holehe/core.py |
50–63 | get_functions() — callable extraction |
holehe/core.py |
66–78 | launch_module() — async task wrapper |
holehe/core.py |
194–196 | Timeout argument parsing |
holehe/core.py |
210–215 | Elapsed time calculation and output |
holehe/instruments.py |
Full file | Trio progress bar for live feedback |
Summary
-
Holehe runs all ~120 modules concurrently using Trio, not sequentially.
-
Total runtime is measured in
holehe/core.pylines 210–215 as wall-clock time from start to completion of all async tasks. -
Expect 5–12 seconds for typical broadband; the 10-second default timeout bounds worst-case per-module delays.
-
Network latency dominates; CPU usage remains minimal throughout execution.
-
Adjust with
-T/--timeoutto trade failure speed for resilience on slow connections.
Frequently Asked Questions
Why doesn't Holehe show per-module timing?
The codebase only implements total elapsed measurement. Individual module duration would require instrumenting each launch_module() call separately—possible to patch locally, but not present in the upstream source.
Can I speed up Holehe by reducing the timeout?
Yes. Using -T 3 or -T 5 forces faster failures on unresponsive services, though you may miss results from legitimately slow international endpoints. The default 10 seconds balances completeness against wait time.
Does Holehe cache results between runs?
No. Every execution performs fresh HTTP requests. No disk or memory caching exists in core.py or module implementations. Repeated scans of the same email incur full network cost each time.
What happens if my connection drops during a scan?
Active Trio tasks raise httpx.ConnectError or trio.BrokenResourceError, which modules catch and report as error: True in result dicts. The remaining concurrent tasks continue; total runtime reflects completed plus failed requests.
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 →