Understanding the Holehe Architecture: A Deep Dive into the Modular OSINT Toolkit
Holehe utilizes a layered, asynchronous architecture built on Python's trio and asyncio that separates CLI handling, core orchestration, and modular service checking, enabling hundreds of email verification modules to execute concurrently through a shared httpx.AsyncClient.
Holehe is a Python-based OSINT (Open Source Intelligence) toolkit developed by megadose that checks whether an email address is registered across hundreds of online platforms without alerting the target. The holehe architecture follows a modular, plugin-based design that makes it trivial to add new service checks while maintaining high performance through shared asynchronous HTTP clients.
Five-Layer Architectural Stack
The codebase organizes functionality into distinct layers, each with specific responsibilities and well-defined interfaces.
CLI and Entry Point
The command-line interface resides in holehe/__main__.py, which parses arguments and delegates to the core engine. The setup.py file registers this as the console entry point (holehe = holehe.__main__:main), allowing users to invoke the tool directly after installation.
holehe test@example.com
This layer handles argument parsing, output formatting flags (such as --json), and initializes the async event loop before calling holehe.core.run.
Core Orchestrator
The holehe/core.py file contains the central execution engine. Its primary function, run(), dynamically discovers all available service modules by walking the holehe/modules package tree. It imports every submodule that implements the public API signature: async def <service_name>(email, client, out).
The core instantiates a single shared httpx.AsyncClient configured with rotating user agents from holehe/localuseragent.py. It then spawning each service module as a separate concurrent task using trio (with asyncio support), aggregating results into a standardized output format.
Service Modules
Individual service implementations live in holehe/modules/ organized by category (e.g., social_media/, porn/, shopping/). Each file exports exactly one coroutine named after the service. For example, holehe/modules/social_media/snapchat.py contains the snapchat() coroutine, while holehe/modules/porn/xvideos.py contains the xvideos() coroutine.
Every module follows a strict contract:
- Parameters:
email(str),client(httpx.AsyncClient),out(list) - Output: Appends a dictionary to the
outlist containing:name: Service identifierexists: Boolean indicating account presenceemailrecovery: Recovery email if exposedphoneNumber: Phone number if exposedrateLimit: Boolean indicating throttlingothers: Additional metadata dictionary
Utilities Layer
Helper functions for operational concerns reside in holehe/localuseragent.py. This module generates rotating user-agent strings to prevent fingerprinting during concurrent requests. The architecture allows modules to focus purely on service-specific logic while shared concerns like HTTP headers and rate-limit handling remain in the core or utility layers.
Packaging Configuration
The setup.py file declares dependencies (httpx, trio, bs4, etc.) and establishes the package structure. It defines the holehe command as an entry point mapping to holehe.__main__:main, ensuring proper module path resolution when the tool is installed via pip.
The Async Execution Pipeline
The holehe architecture implements a specific workflow for concurrent email checking that maximizes throughput while respecting service constraints.
Dynamic Module Discovery
When core.run() executes, it programmatically inspects the holehe.modules namespace package. It imports every Python file found in subdirectories automatically, checking for the presence of a callable matching the required async signature. This eliminates manual registration—adding a new service requires only dropping a file into the appropriate holehe/modules/ subdirectory.
Shared Client Concurrency
Rather than instantiating separate HTTP clients per service, the core creates one httpx.AsyncClient instance and passes it to all coroutines. This connection pooling reduces overhead and ensures consistent timeout and retry policies across all checks. The client configuration includes randomized user agents sourced from localuseragent.py to mitigate blocking.
Standardized Result Aggregation
Each module executes independently and appends its result dictionary to the shared out list passed by reference. The core awaits all tasks using trio nurseries (or asyncio.gather), then processes the aggregated list. This approach prevents race conditions while allowing services with varying response times to complete without blocking faster checks.
Rate Limit Awareness
Modules detect throttling (HTTP 429, CAPTCHA challenges, or timing anomalies) and set rateLimit=True in their output. While the current implementation primarily reports these states, the architecture supports future extensions for automatic retry logic with exponential backoff or proxy rotation between attempts.
Practical Implementation Examples
Running from Command Line
The simplest interaction uses the packaged CLI:
holehe test@example.com --json
The --json flag outputs structured data rather than the default colored table format.
Embedding Individual Modules
You can import and run specific modules directly for targeted checks:
import trio
import httpx
from holehe.modules.social_media.snapchat import snapchat
async def check_snapchat():
email = "target@example.com"
results = []
client = httpx.AsyncClient()
await snapchat(email, client, results)
print(results)
# Output: [{'name': 'snapchat', 'exists': True, 'emailrecovery': None, ...}]
await client.aclose()
trio.run(check_snapchat)
Using the High-Level Core API
For comprehensive checks across all registered services:
import trio
from holehe.core import run
async def full_scan():
email = "target@example.com"
results = await run(email)
for service in results:
if service.get('exists'):
print(f"Account found on {service['name']}")
trio.run(full_scan)
This invokes the full holehe architecture, including module discovery and parallel execution.
Summary
- Layered Design: The architecture separates CLI parsing (
holehe/__main__.py), core orchestration (holehe/core.py), service implementations (holehe/modules/), and utilities (holehe/localuseragent.py). - Plugin Architecture: New services require only a single Python file implementing the
async def service(email, client, out)signature—no registration or configuration files needed. - Shared Resources: A single
httpx.AsyncClientserves all concurrent modules, reducing connection overhead while maintaining isolation through async task boundaries. - Standardized Contracts: Every module returns a dictionary with
name,exists,rateLimit,emailrecovery,phoneNumber, andothersfields, ensuring consistent output regardless of service complexity. - Async-First: Built on
trioandasyncio, the codebase achieves true parallelism suitable for I/O-bound OSINT operations across hundreds of endpoints.
Frequently Asked Questions
What async library does holehe use for concurrency?
Holehe primarily uses Trio for structured concurrency, though it maintains compatibility with asyncio. The holehe/core.py file implements task spawning through Trio nurseries, allowing modules to run in parallel while providing clean cancellation semantics and error propagation across hundreds of concurrent HTTP requests.
How do I add a new service module to holehe?
Create a Python file in the appropriate holehe/modules/ subdirectory (e.g., holehe/modules/shopping/newstore.py) and implement an async function matching the service name. The function must accept three parameters: email (string), client (httpx.AsyncClient), and out (list). Append a dictionary with the required keys (name, exists, rateLimit, etc.) to out. The core automatically discovers and executes the module on the next run.
What information does each holehe module return?
Every module returns a standardized dictionary containing: name (the service identifier), exists (boolean indicating if the email is registered), emailrecovery (any leaked recovery email), phoneNumber (any leaked phone digits), rateLimit (boolean indicating if the service throttled the request), and others (a dictionary for additional service-specific metadata such as profile URLs or account creation dates).
Why does holehe use a shared HTTP client across all modules?
The architecture shares a single httpx.AsyncClient instance across all service modules to enable connection pooling, reduce memory overhead, and ensure consistent request handling (timeouts, headers, redirects). This design, implemented in holehe/core.py, allows hundreds of concurrent checks to run efficiently without establishing separate TCP connections for each service, significantly improving performance during large-scale OSINT operations.
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 →