# How to Configure a Custom HTTP Client for Holehe Programmatically

> Learn to programmatically configure a custom HTTP client for Holehe. Inject httpx.AsyncClient into maincore() to manage proxies, timeouts, headers, and retry logic for enhanced control.

- Repository: [Palenath/holehe](https://github.com/megadose/holehe)
- Tags: how-to-guide
- Published: 2026-08-31

---

**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`](https://github.com/megadose/holehe/blob/main/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`](https://github.com/megadose/holehe) source code.

## Where Holehe Creates Its Default Client

Holehe instantiates its HTTP client in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) at lines 13-14:

```python

# 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.

```python
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()`.

```python
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:**

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

```

**Python example:**

```python
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`](https://github.com/megadose/holehe/blob/main/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`](https://github.com/megadose/holehe/blob/main/holehe/instruments.py) | Progress reporting | `TrioProgress` class for monitoring execution |
| [`holehe/localuseragent.py`](https://github.com/megadose/holehe/blob/main/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`](https://github.com/megadose/holehe/blob/main/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`](https://github.com/megadose/holehe/blob/main/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`](https://github.com/megadose/holehe/blob/main/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.