How to Embed Holehe in a Python Application and Call Individual Modules Like `snapchat`
You can embed holehe in a Python application by importing async module functions directly from holehe.modules and calling them with an httpx.AsyncClient and a mutable results list, bypassing the CLI entirely.
Holehe is an email reconnaissance tool that checks whether an address exists on hundreds of platforms. While the command-line interface runs all checks by default, you can embed specific modules—such as snapchat—into your own Python codebase for targeted, asynchronous lookups.
How Holehe's Module System Works
The megadose/holehe codebase is architected around dynamic module discovery and uniform async function signatures. Understanding this structure lets you call individual services programmatically.
Dynamic Loading Architecture
Holehe discovers services through two key functions in holehe/core.py:
import_submodules()(lines 37–48): Usespkgutil.walk_packagesto traverseholehe.modulesand returns a dictionary of all loaded submodules.get_functions()(lines 50–60): Extracts callable service functions from those modules, producing the list that the CLI iterates over.
Each service module contains an async function matching this signature:
async def <service_name>(email: str, client: httpx.AsyncClient, out: list) -> None
The function performs HTTP requests and appends a standardized result dictionary to out. No return value—the result is always appended to the mutable list.
Embedding Individual Modules
Import and Call a Single Service
The simplest approach imports a specific module—like snapchat—and invokes it directly:
import asyncio
import httpx
from holehe.modules.social_media.snapchat import snapchat
async def check_snapchat(email: str):
async with httpx.AsyncClient(timeout=10) as client:
out = []
await snapchat(email, client, out)
return out[0]
result = asyncio.run(check_snapchat("example@example.com"))
print(result)
Key requirements:
- Import the concrete module from its path (e.g.,
holehe.modules.social_media.snapchat). - Pass an
httpx.AsyncClient—synchronous clients will fail. - Provide a list (
out) for the result; the module always appends exactly one dictionary.
Constraining Multiple Services with Shared Client
Reuse the same httpx.AsyncClient across multiple modules for efficient connection pooling:
import asyncio
import httpx
from holehe.modules.social_media.snapchat import snapchat
from holehe.modules.social_media.instagram import instagram
from holehe.modules.social_media.twitter import twitter
async def check_multiple(email: str, targets: set):
async with httpx.AsyncClient(timeout=10) as client:
out = []
# Map target names to imported functions
module_map = {
"snapchat": snapchat,
"instagram": instagram,
"twitter": twitter,
}
selected = [module_map[t] for t in targets if t in module_map]
await asyncio.gather(*(
fn(email, client, out) for fn in selected
))
return {r["name"]: r for r in out}
results = asyncio.run(check_multiple(
"example@example.com",
{"snapchat", "instagram"}
))
This pattern enables concurrent execution with asyncio.gather, reducing total latency versus sequential calls.
Bridging to Synchronous Code
Wrapper for Blocking Contexts
When your application isn't async-native, use asyncio.run() as a bridge:
import asyncio
import httpx
from holehe.modules.social_media.snapchat import snapchat
def check_snapchat_sync(email: str, timeout: float = 10.0):
"""Synchronous wrapper for the snapchat module."""
async def _run():
async with httpx.AsyncClient(timeout=timeout) as client:
out = []
await snapchat(email, client, out)
return out[0] if out else None
return asyncio.run(_run())
# Use from ordinary Python code
result = check_snapchat_sync("example@example.com")
print(f"Snapchat: {result.get('exists') if result else 'error'}")
Performance note: Creating a new event loop per call adds overhead. For high-throughput scenarios, consider restructuring to async-native code or using an executor pool.
Leveraging Holehe's Discovery for Filtered Scans
If you want holehe's automatic module discovery but need to restrict execution, combine import_submodules and get_functions:
import asyncio
import httpx
from holehe.core import import_submodules, get_functions
async def filtered_scan(email: str, allowlist: set[str]):
"""
Run only specified modules using holehe's built-in discovery.
"""
modules = import_submodules("holehe.modules")
all_functions = get_functions(modules)
selected = [
fn for fn in all_functions
if fn.__name__ in allowlist
]
async with httpx.AsyncClient(timeout=10) as client:
out = []
await asyncio.gather(*(
fn(email, client, out) for fn in selected
))
return out
# Check only snapchat through discovery
results = asyncio.run(filtered_scan(
"example@example.com",
{"snapchat"}
))
This approach future-proofs your code: new modules appear automatically, but you control which execute.
Understanding the Result Format
Every holehe module returns a dictionary with consistent fields:
| Field | Type | Description |
|---|---|---|
name |
str |
Service identifier (e.g., "snapchat") |
domain |
str |
Service domain |
method |
str |
HTTP method used for verification |
frequent_rate_limit |
bool |
Whether the service aggressively rate-limits |
rateLimit |
bool |
Whether this call hit a rate limit |
exists |
bool |
Whether the email is registered |
emailrecovery |
str | None |
Partial recovery email if exposed |
phoneNumber |
str | None |
Partial phone if exposed |
others |
dict | None |
Additional metadata |
Access fields directly: result["exists"] indicates registration status.
Source Code References
Critical files in megadose/holehe for embedded usage:
holehe/core.py— Dynamic loading (import_submodules,get_functions), CLI argument parsing, and result formatting.holehe/modules/social_media/snapchat.py— Concrete implementation of the Snapchat check, showing the expected async signature.holehe/localuseragent.py— User-agent rotation used by modules includingsnapchat.
Summary
- Import directly from
holehe.modules.<category>.<service>to bypass CLI overhead. - Use
httpx.AsyncClient—modules expect async HTTP clients exclusively. - Pass a mutable list as
out; results are appended, not returned. - Share clients across multiple module calls for connection efficiency.
- Bridge with
asyncio.run()when embedding in synchronous codebases. - Reuse discovery logic via
import_submodulesandget_functionsfor dynamic but filtered execution.
Frequently Asked Questions
Can I use requests instead of httpx with holehe modules?
No. All holehe modules are implemented as async def functions expecting httpx.AsyncClient. The source code in files like holehe/modules/social_media/snapchat.py uses client.get() and client.post() methods specific to httpx. To use requests, you would need to rewrite the module or run it in a separate process.
Why does the module append to a list instead of returning a value?
This design enables concurrent execution with asyncio.gather. Since gather collects return values in order, but modules run at unpredictable speeds, the shared out list allows thread-safe result collection without complex synchronization. The CLI in holehe/core.py relies on this same pattern.
How do I handle rate limits when embedding holehe?
Check the rateLimit and frequent_rate_limit fields in the result dictionary. Services marked frequent_rate_limit=True (visible in each module's source) require careful throttling. Implement delays between calls or use asyncio.Semaphore to limit concurrency: semaphore = asyncio.Semaphore(5) then async with semaphore: around each module call.
Can I modify holehe modules to add proxy support?
Yes. The httpx.AsyncClient accepts a proxies parameter. When embedding, instantiate your client with httpx.AsyncClient(proxies={"https://": "http://proxy:8080"}) and pass it to the module. The module will use your configured client for all requests without source modification.
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 →