# How Holehe Handles User-Agent Rotation: A Deep Dive into the Source Code

> Discover how Holehe achieves effective user-agent rotation by randomly selecting realistic browser strings from its source code. Learn the technique behind its dynamic fingerprinting for enhanced privacy.

- Repository: [Palenath/holehe](https://github.com/megadose/holehe)
- Tags: deep-dive
- Published: 2026-09-08

---

**Holehe rotates User-Agent headers by randomly selecting from a curated catalogue of realistic browser strings stored in [`holehe/localuseragent.py`](https://github.com/megadose/holehe/blob/main/holehe/localuseragent.py), applying a different fingerprint on every HTTP request via `random.choice()`.**

Holehe is an open-source OSINT tool that checks for account registration across multiple platforms. Unlike tools that rely on static headers, the megadose/holehe repository implements dynamic User-Agent rotation to avoid detection and rate limiting during reconnaissance operations.

## The User-Agent Catalogue Architecture

### Storage Location and Data Structure

The tool maintains a JSON-encoded dictionary of authentic browser strings in **[`holehe/localuseragent.py`](https://github.com/megadose/holehe/blob/main/holehe/localuseragent.py)**. This file exports a single `ua` object that organizes User-Agent strings by browser family.

The structure groups strings under the `browsers` key, partitioning them into categories such as:
- `chrome` – Desktop Chrome browsers
- `firefox` – Mozilla Firefox variants  
- `ios` – Mobile Safari on iPhone
- `safari` – macOS Safari browsers

This centralized storage ensures consistency across all modules while simplifying updates.

### Import Pattern Across Modules

Every HTTP request module in the codebase follows an identical import pattern to access the rotation pool:

```python
from holehe.localuseragent import ua
import random

```

This lightweight dependency chain avoids external libraries, keeping the footprint minimal while providing cryptographically adequate randomization through Python's standard library.

## Per-Request Rotation Implementation

### Random Selection Strategy

Instead of setting a User-Agent at session initialization, individual modules invoke `random.choice()` at the moment of request construction. This guarantees that successive calls to the same service present different client fingerprints.

The Facebook module ([`holehe/modules/social_media/facebook.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/social_media/facebook.py)) demonstrates Chrome-family rotation:

```python

# holehe/modules/social_media/facebook.py

from holehe.localuseragent import ua
import random

headers = {
    "User-Agent": random.choice(ua["browsers"]["chrome"]),
    "Accept": "application/json, text/plain, */*",
    "Accept-Language": "en-US,en;q=0.0",
    "Accept-Encoding": "gzip, deflate",
    "Connection": "keep-alive",
}

```

The Instagram module ([`holehe/modules/social_media/instagram.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/social_media/instagram.py)) utilizes Firefox strings for its requests:

```python

# holehe/modules/social_media/instagram.py

headers = {
    "User-Agent": random.choice(ua["browsers"]["firefox"]),
    "Accept": "*/*",
    "Accept-Language": "en-US,en;q=0.5",
    "Accept-Encoding": "gzip, deflate, br",
    "DNT": "1",
    "Connection": "keep-alive",
}

```

### Execution Timing and Performance

The `random.choice()` call executes **per request**, not per session. This design choice ensures maximum entropy in the header fingerprint but remains computationally trivial—Python's `random` module selects from the list in O(1) time with negligible overhead.

## Extending the Browser Pool

Adding support for new browser families requires only modifying the JSON structure in **[`holehe/localuseragent.py`](https://github.com/megadose/holehe/blob/main/holehe/localuseragent.py)**. For example, to add Microsoft Edge support:

```python

# In holehe/localuseragent.py

{
    "browsers": {
        "chrome": [
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...",
            # ... existing Chrome strings

        ],
        "firefox": [
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0)...",
            # ... existing Firefox strings

        ],
        "edge": [
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.0 Edg/120.0.0.0"
        ]
    }
}

```

Once defined, any module can immediately utilize the new category:

```python
headers = {
    "User-Agent": random.choice(ua["browsers"]["edge"]),
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
}

```

This centralized architecture eliminates the need to patch individual modules when updating User-Agent strings.

## Summary

- **Holehe stores** its User-Agent catalogue in **[`holehe/localuseragent.py`](https://github.com/megadose/holehe/blob/main/holehe/localuseragent.py)** as a JSON-structured dictionary accessed via the `ua` object.
- **Randomization occurs** at request time through `random.choice(ua["browsers"][family])`, ensuring per-request rotation rather than per-session consistency.
- **Multiple modules** like [`facebook.py`](https://github.com/megadose/holehe/blob/main/facebook.py) and [`instagram.py`](https://github.com/megadose/holehe/blob/main/instagram.py) import the same `ua` dictionary but select from different browser families (Chrome vs. Firefox) based on service requirements.
- **Extending the pool** requires only editing the JSON in [`localuseragent.py`](https://github.com/megadose/holehe/blob/main/localuseragent.py), automatically propagating new strings to all modules without code changes.

## Frequently Asked Questions

### Does Holehe rotate User-Agents automatically for every request?

Yes. Each HTTP request module calls `random.choice()` immediately before sending the request, selecting a fresh User-Agent string from the appropriate browser family. This provides automatic rotation without requiring configuration flags or session management.

### Where does Holehe store its User-Agent strings?

The complete catalogue resides in **[`holehe/localuseragent.py`](https://github.com/megadose/holehe/blob/main/holehe/localuseragent.py)**, which exports a `ua` dictionary containing browser strings organized by family (Chrome, Firefox, iOS, Safari). This single-file architecture simplifies maintenance and ensures consistency across all OSINT modules.

### Can I add custom User-Agent strings to Holehe?

Yes. Modify the JSON object in [`holehe/localuseragent.py`](https://github.com/megadose/holehe/blob/main/holehe/localuseragent.py) to include additional entries under existing browser families or create new categories (e.g., "edge" or "android"). All modules importing `ua` will immediately have access to the expanded rotation pool without requiring individual updates.

### Why does Holehe use random.choice instead of a sequential rotation?

The `random.choice()` method provides stochastic distribution that mimics organic traffic patterns better than predictable sequential cycling. Since the selection executes in constant time O(1), it introduces no measurable latency while maximizing fingerprint entropy to evade rate limiting and detection systems.