How to Configure a Custom HTTP Client for Holehe Programmatically

Replace or inject a custom httpx.AsyncClient into Holehe's maincore() function to control proxies, timeouts, headers, and retry logic.

Holehe is an open-source email reconnaissance tool that performs account lookups across hundreds of websites. By default, it uses a plain httpx.AsyncClient created inside holehe/core.py. If you need to route traffic through proxies, customize TLS settings, or add retry policies, you must configure a custom HTTP client for Holehe programmatically. This guide covers three proven approaches based on the megadose/holehe source code.

Where Holehe Creates Its Default Client

Holehe instantiates its HTTP client in holehe/core.py at lines 13-14:


# holehe/core.py (lines 13-14)

client = httpx.AsyncClient(timeout=timeout)  # default client

This happens inside maincore(), which is invoked by the CLI entry point holehe.main(). Because the client is hardcoded at the method level, you cannot pass a pre-configured instance through standard CLI arguments. The following three methods solve this limitation.

Method 1: Monkey-Patch httpx.AsyncClient

The fastest way to inject a custom client without modifying Holehe's source is to replace the httpx.AsyncClient class with a factory that returns your pre-configured instance.

import httpx
import trio
from holehe import core as holehe

# ----- Build your custom client -----

my_client = httpx.AsyncClient(
    timeout=30,                                   # extended timeout

    headers={"User-Agent": "MyHoleheBot/1.0"},    # custom User-Agent

    proxies="http://localhost:8888",              # proxy (optional)

    transport=httpx.AsyncHTTPTransport(retries=3) # retry logic (optional)

)

# ----- Monkey-patch the constructor -----

original_async_client = httpx.AsyncClient

def async_client_factory(*args, **kwargs):
    # Ignore arguments passed by holehe.core.maincore()

    return my_client

httpx.AsyncClient = async_client_factory

# ----- Run holehe -----

holehe.main()  # uses my_client internally

# ----- Restore original (recommended) -----

httpx.AsyncClient = original_async_client

Key insight: maincore() calls httpx.AsyncClient() with only a timeout argument. By intercepting the constructor, your factory returns a fully configured client regardless of what arguments Holehe passes.

Method 2: Bypass the CLI and Call launch_module Directly

For production integrations where monkey-patching feels fragile, invoke launch_module directly with your own client. This requires replicating the module-loading logic from maincore().

import httpx
import trio
from holehe import core as holehe
from holehe.core import launch_module, get_functions, import_submodules

async def run_holehe(email: str, client: httpx.AsyncClient):
    # Replicate CLI initialization (holehe/core.py logic)

    modules = import_submodules("holehe.modules")
    websites = get_functions(modules)
    
    # Optional: attach progress instrumentation

    instrument = holehe.TrioProgress(len(websites))
    trio.lowlevel.add_instrument(instrument)
    
    results = []
    async with trio.open_nursery() as nursery:
        for site in websites:
            nursery.start_soon(launch_module, site, email, client, results)
    
    trio.lowlevel.remove_instrument(instrument)
    await client.aclose()
    
    return sorted(results, key=lambda x: x["name"])

# ----- Execute with custom client -----

custom_client = httpx.AsyncClient(
    timeout=20,
    headers={"User-Agent": "MyHoleheBot/2.0"},
    proxies={"https": "http://proxy.example:3128"},
)

email = "target@example.com"
results = trio.run(run_holehe, email, custom_client)
print(results)

What this unlocks: Complete control over httpx.AsyncClient parameters including http1/http2, verify (TLS certificates), limits (connection pooling), and event_hooks (request/response middleware).

Method 3: Environment Variables for Quick Tweaks

When you only need proxy routing or timeout adjustments, httpx respects standard environment variables without code changes:

Variable Effect
HTTP_PROXY / HTTPS_PROXY Route requests through specified proxy
NO_PROXY Comma-separated list of hosts to bypass
HTTPX_TIMEOUT Override default timeout in seconds

Shell example:

export HTTPS_PROXY="http://proxy.mycorp:3128"
export HTTPX_TIMEOUT="45"
holehe victim@example.com

Python example:

import os
os.environ["HTTPS_PROXY"] = "http://proxy.mycorp:3128"
os.environ["HTTPX_TIMEOUT"] = "45"

from holehe import core as holehe
holehe.main()

Limitation: Environment variables cannot configure headers, retry logic, or transport-layer options. Use Method 1 or 2 for advanced customization.

Key Source Files to Understand

File Purpose Relevant Content
holehe/core.py Entry point and orchestration maincore(), launch_module(), client instantiation
holehe/modules/ Site-specific check functions Imported dynamically by import_submodules()
holehe/instruments.py Progress reporting TrioProgress class for monitoring execution
holehe/localuseragent.py Default User-Agent string ua variable used when headers aren't overridden

Performance and Concurrency Considerations

Holehe uses Trio for structured concurrency. When providing a custom client:

  • Connection limits: Set limits=httpx.Limits(max_keepalive_connections=20, max_connections=100) to prevent port exhaustion during large scans
  • Timeouts: Individual modules may hang; pair timeout=httpx.Timeout(10.0, connect=5.0) with your retry transport
  • Client lifecycle: Always await client.aclose() when bypassing maincore() (Method 2) to avoid unclosed connection warnings

Summary

  • Default behavior: Holehe creates a minimal httpx.AsyncClient in holehe/core.py with only timeout configuration
  • Three customization paths:
    • Monkey-patch httpx.AsyncClient for quick injection without source edits
    • Invoke launch_module directly with a bespoke client for full programmatic control
    • Environment variables (HTTP_PROXY, HTTPX_TIMEOUT) for zero-code proxy and timeout adjustments
  • Critical files: holehe/core.py for client creation, holehe/core.py::launch_module for the execution hook you can override

Frequently Asked Questions

Can I pass a custom client through Holehe's CLI arguments?

No. The holehe.main() CLI entry point does not expose client configuration flags. You must use one of the three programmatic approaches described above: monkey-patching, direct launch_module invocation, or environment variables.

Does Holehe support synchronous HTTP clients?

No. According to the source code in holehe/core.py, Holehe is built on Trio and requires httpx.AsyncClient. The launch_module coroutine explicitly awaits asynchronous HTTP requests. Using requests or httpx.Client would require refactoring the module architecture.

Will environment variables override my monkey-patched client settings?

No. Environment variables affect only newly instantiated clients. If you monkey-patch httpx.AsyncClient to return a pre-built instance, that instance's configuration takes precedence. However, if you rely on HTTPX_TIMEOUT without patching, it influences the default client created in maincore().

Is monkey-patching safe for production use?

Monkey-patching is acceptable for isolated scripts but carries risks: it affects all httpx.AsyncClient instantiations in the process, and must be carefully restored. For production services, prefer Method 2 (direct launch_module invocation) which provides clean separation of concerns and explicit resource management.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →