# How Holehe Rotates User-Agent Strings to Avoid Detection

> Discover how Holehe rotates User-Agent strings using a randomized pool of browser fingerprints from its localuseragent.py file to evade detection effectively.

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

---

**Holehe rotates User-Agent strings by maintaining a centralized JSON pool of browser fingerprints in [`holehe/localuseragent.py`](https://github.com/megadose/holehe/blob/main/holehe/localuseragent.py) and selecting entries at random for each HTTP request.**

The open-source OSINT tool **Holehe** relies on mimicking legitimate browser traffic to check whether email addresses are registered across hundreds of online services. Its User-Agent (UA) rotation mechanism is lightweight, deterministic, and implemented consistently across all service modules. This article examines the implementation details based on the megadose/holehe source code.

## Where Holehe Stores User-Agent Strings

The foundation of Holehe's rotation system lives in a single file:

- **[`holehe/localuseragent.py`](https://github.com/megadose/holehe/blob/main/holehe/localuseragent.py)** — Contains a JSON-encoded dictionary of realistic browser User-Agent strings loaded at import time.

When Python imports this module, it immediately parses the JSON blob into a dictionary named `ua`:

```python

# holehe/localuseragent.py#L3

ua = json.loads('''{ … }''')

```

This `ua` dictionary organizes strings by browser family. The structure includes keys like `"browsers"` with nested categories for `"chrome"`, `"firefox"`, `"safari"`, and others. Each category holds dozens of current and legacy UA strings that appear authentic to target services.

## How Holehe Implements User-Agent Rotation

Holehe's rotation follows a **data-driven, import-and-select pattern** repeated across every service module:

1. **Import the pool** — Each module pulls the `ua` dictionary via `from holehe.localuseragent import *` or `from holehe.localuseragent import ua`.

2. **Random selection** — At request time, code calls `random.choice()` on the desired browser category.

3. **Header injection** — The selected string populates the `User-Agent` header.

Because selection happens per-request rather than per-session, consecutive checks against the same service may present completely different browser fingerprints. This distribution helps evade simple rate-limiting rules that flag repetitive UA patterns.

### Code Example: Basic UA Selection

```python

# Selecting a random Chrome User-Agent

import random
from holehe.localuseragent import ua

def random_chrome_ua():
    return random.choice(ua["browsers"]["chrome"])

# Applied to an HTTP request

import requests

def fetch(url):
    headers = {"User-Agent": random_chrome_ua()}
    resp = requests.get(url, headers=headers)
    return resp.text

```

## User-Agent Handling in Service Modules

Every service check under `holehe/modules/` implements the same pattern. Here is how the Facebook module constructs its request headers:

```python

# Simplified excerpt demonstrating the pattern

import random
from holehe.localuseragent import ua
import requests

def check_facebook(email):
    headers = {
        "User-Agent": random.choice(ua["browsers"]["chrome"]),
        "Accept": "text/html,application/xhtml+xml",
    }
    r = requests.get(
        f"https://www.facebook.com/login.php?email={email}",
        headers=headers
    )
    # Response analysis logic follows...

    return r.status_code

```

The identical pattern appears in [`holehe/modules/social_media/twitter.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/social_media/twitter.py) and [`holehe/modules/social_media/instagram.py`](https://github.com/megadose/holehe/blob/main/holehe/modules/social_media/instagram.py). This consistency ensures that no module becomes a detection weak point due to static or missing UA rotation.

## Key Design Characteristics

| Aspect | Implementation |
|--------|----------------|
| **Storage format** | JSON string embedded in Python module |
| **Loading strategy** | Parse-on-import (eager, once per process) |
| **Selection method** | `random.choice()` from browser-specific lists |
| **Scope of rotation** | Per-request, not per-session or per-module |
| **Browser coverage** | Chrome, Firefox, Safari, and others |

The eager loading approach avoids repeated JSON parsing overhead. Once imported, the `ua` dictionary resides in memory for all subsequent requests across the application's lifetime.

## Summary

- **Centralized data**: All User-Agent strings live in [`holehe/localuseragent.py`](https://github.com/megadose/holehe/blob/main/holehe/localuseragent.py) as a JSON-encoded dictionary.

- **Random per-request selection**: Each HTTP request calls `random.choice()` on a browser category, producing varied fingerprints.

- **Consistent module pattern**: Every service module under `holehe/modules/` imports and uses the same `ua` pool, preventing inconsistent detection exposure.

- **Zero external dependencies**: The mechanism relies only on Python's standard `random` and `json` modules.

## Frequently Asked Questions

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

Yes. Each service module selects a fresh User-Agent string via `random.choice()` when building request headers. This happens independently for every HTTP request, not just once per session or target.

### Can I customize the User-Agent pool in Holehe?

The pool is hardcoded in [`holehe/localuseragent.py`](https://github.com/megadose/holehe/blob/main/holehe/localuseragent.py) as a JSON string. To modify available UAs, edit this file directly and restart the application. There is no runtime configuration interface for UA management.

### Why does Holehe use `random.choice` instead of a round-robin approach?

Random selection better mimics organic traffic patterns and avoids predictable sequences that sophisticated rate-limiting systems might detect. The genuine browser strings in the pool ensure each selection remains plausible.

### Which browsers does Holehe's User-Agent pool include?

According to the `ua` dictionary structure in [`localuseragent.py`](https://github.com/megadose/holehe/blob/main/localuseragent.py), the pool includes strings for Chrome, Firefox, Safari, and other major browsers. Most service modules default to the Chrome category, though the full dictionary is available to any module.