# How a Holehe Module Makes HTTP Requests: Complete Async Architecture Guide

> Discover how Holehe modules make async HTTP requests using httpx AsyncClient for efficient email verification across 100+ services. Learn about connection reuse and architecture.

- Repository: [Palenath/holehe](https://github.com/megadose/holehe)
- Tags: internals
- Published: 2026-08-23

---

**Holehe modules perform HTTP requests through a shared `httpx.AsyncClient` passed as an argument, enabling connection reuse and efficient asynchronous email verification across 100+ services.**

The open-source OSINT tool [Holehe](https://github.com/megadose/holehe) by megadose checks whether an email address is registered on hundreds of websites. Understanding how Holehe modules make HTTP requests reveals a clean async design pattern built on Python's `httpx` library. This guide examines the actual source code to explain the request flow, error handling, and result collection mechanisms.

## The Shared Async Client Architecture

Holehe avoids the overhead of creating new HTTP connections for every service check. Instead, it follows a **single-client, multi-module** pattern.

### Central Client Initialization

In [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py), the main execution flow instantiates one `httpx.AsyncClient` that all modules share:

```python

# holehe/core.py lines 12-14 (simplified)

client = httpx.AsyncClient(timeout=args.timeout)

# ...

for website in websites:
    nursery.start_soon(launch_module, website, email, client, out)

```

This design provides three key benefits:

- **Connection pooling** – TCP connections are reused across requests to the same host
- **Configurable timeout** – Controlled via CLI argument (`--timeout`)
- **Clean resource management** – Single `async with` context manager handles cleanup

### Module Function Signature

Every Holehe module exports a coroutine with this exact signature:

```python

# holehe/modules/social_media/facebook.py lines 6-9

async def facebook(email, client, out):
    # email: str — the target email address

    # client: httpx.AsyncClient — shared async client

    # out: list — mutable result collection list

    ...

```

The `client` parameter is the same `httpx.AsyncClient` instance created in [`core.py`](https://github.com/megadose/holehe/blob/main/core.py). Modules never instantiate their own HTTP clients.

## Constructing Requests with Browser Headers

Holehe modules masquerade as legitimate browsers to avoid trivial detection. The header construction process follows a consistent pattern.

### User-Agent Rotation

Modules import random browser User-Agents from [`holehe/localuseragent.py`](https://github.com/megadose/holehe/blob/main/holehe/localuseragent.py):

```python

# holehe/modules/social_media/facebook.py lines 10-11

from holehe.localuseragent import ua
random.choice(list(ua.data_browsers.keys()))

```

The `ua` object provides authentic browser strings for Chrome, Firefox, Safari, and Edge across multiple operating systems.

### Header Assembly Example

Here's how the Facebook module builds its request headers:

```python

# holehe/modules/social_media/facebook.py lines 12-19

headers = {
    'User-Agent': random.choice(list(ua.data_browsers.keys())),
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en-US,en;q=0.5',
    'Accept-Encoding': 'gzip, deflate',
    'DNT': '1',
    'Connection': 'keep-alive',
}

```

Each module customizes headers based on the target service's expectations while maintaining realistic browser fingerprints.

## The Complete Request Lifecycle: GET then POST

Holehe modules typically follow a **two-phase request pattern**: initial probe followed by submission with extracted tokens.

### Phase 1: Initial GET Request

Modules first fetch a page to obtain session state:

```python

# holehe/modules/social_media/facebook.py lines 22-27

try:
    response = await client.get(
        "https://www.facebook.com/login/identify",
        headers=headers,
        follow_redirects=True
    )

```

This GET request serves multiple purposes:

- Establishes session cookies
- Retrieves CSRF tokens or other anti-authentication measures
- Confirms the endpoint is reachable
- Detects rate-limiting or blocking responses

### Token Extraction

After the GET response, modules parse HTML or JavaScript to extract required tokens:

```python

# Extract csrftoken from response (typical pattern)

token = re.search(r'"csrf_token":"([^"]+)"', response.text).group(1)

```

### Phase 2: POST Request with Payload

With tokens acquired, modules submit the email address:

```python

# holehe/modules/social_media/facebook.py lines 47-50

post_response = await client.post(
    "https://www.facebook.com/login/identify",
    data=payload,
    headers=headers
)

```

The POST payload typically includes:

- The target email address
- Extracted CSRF tokens
- Additional form data required by the service

## Response Processing and Result Reporting

Modules parse responses to determine registration status and handle edge cases.

### JSON and Text Parsing

Depending on the service, modules use different parsing strategies:

```python

# JSON API response

data = response.json()

# HTML/text response

text = response.text

```

### Error Handling Pattern

All network operations are wrapped in comprehensive exception handling:

```python

# holehe/modules/social_media/facebook.py lines 30-36 and 84-90

try:
    # ... request code ...

except Exception:
    out.append({
        "name": "facebook",
        "rateLimit": True,
        "exists": False,
        "emailrecovery": None,
        "phoneNumber": None,
        "others": None
    })
    return

```

On any exception—timeout, connection error, or parsing failure—the module reports `rateLimit: True` and returns immediately. This prevents one failing module from crashing the entire scan.

### Result Dictionary Structure

Every module appends a standardized result dictionary to the shared `out` list:

| Key | Type | Description |
|-----|------|-------------|
| `name` | str | Service identifier (e.g., "facebook") |
| `exists` | bool | Whether the email is registered |
| `rateLimit` | bool | Whether the check was blocked or failed |
| `emailrecovery` | str \| None | Partial recovery email if exposed |
| `phoneNumber` | str \| None | Partial phone number if exposed |
| `others` | dict \| None | Additional service-specific data |

## Complete Working Example

### Running a Single Module Programmatically

```python
import asyncio
import httpx
from holehe.modules.social_media.facebook import facebook
from holehe.localuseragent import ua

async def verify_email_on_facebook(email: str):
    """Manually run a single Holehe module with explicit client control."""
    async with httpx.AsyncClient(timeout=10) as client:
        results = []
        await facebook(email, client, results)
        return results

# Execute

results = asyncio.run(verify_email_on_facebook("target@example.com"))
print(results)

# Output: [{'name': 'facebook', 'exists': True, 'rateLimit': False, ...}]

```

### Using the Official CLI

```bash

# Install Holehe

pip install holehe

# Run full scan with default settings

holehe target@example.com

# Customize timeout and output

holehe target@example.com --timeout 15 --only-used

```

## Key Source Files Reference

| File | Purpose | Lines of Interest |
|------|---------|-------------------|
| [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) | Client creation and orchestration | 12-14 (client setup), nursery spawning |
| [`holehe/localuseragent.py`](https://github.com/megadose/holehe/blob/main/holehe/localuseragent.py) | Browser User-Agent database | Full file—dictionary of realistic agents |
| [`holehe/modules/social_media/facebook.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/social_media/facebook.py) | Exemplary module implementation | 6-11 (signature), 22-31 (GET), 47-52 (POST) |

## Summary

- **Single shared client**: [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) creates one `httpx.AsyncClient` reused by all modules
- **Async coroutine signature**: Every module accepts `(email, client, out)` parameters
- **Browser emulation**: Headers built from [`holehe/localuseragent.py`](https://github.com/megadose/holehe/blob/main/holehe/localuseragent.py) rotate realistic User-Agents
- **Two-phase pattern**: GET to extract tokens, then POST with email payload
- **Defensive error handling**: All requests wrapped in `try/except` with `rateLimit` reporting
- **Uniform results**: Standardized dictionary appended to shared `out` list

## Frequently Asked Questions

### What HTTP library does Holehe use instead of requests?

Holehe uses **httpx** rather than the synchronous `requests` library. The `httpx.AsyncClient` enables concurrent checking of hundreds of services without blocking, significantly improving performance over sequential requests.

### Can I customize the timeout for slow connections?

Yes. The timeout is configurable via the `--timeout` CLI argument. Internally, this value is passed directly to `httpx.AsyncClient(timeout=args.timeout)` in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) line 13. The default is typically 10 seconds.

### How does Holehe avoid being blocked by services?

Modules employ multiple evasion techniques: rotating browser User-Agents from [`holehe/localuseragent.py`](https://github.com/megadose/holehe/blob/main/holehe/localuseragent.py), realistic header sets including `Accept-Encoding` and `DNT` flags, and mimicking legitimate browser request sequences (GET before POST). However, aggressive use may still trigger rate limits.

### Why do modules use a shared `out` list instead of returning values?

The shared mutable list pattern allows [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) to collect results from hundreds of concurrently executing coroutines without complex synchronization primitives. Each module appends its result dictionary; the main loop awaits all tasks then processes the complete `out` list uniformly.