How Holehe Rotates User-Agent Strings to Avoid Detection
Holehe rotates User-Agent strings by maintaining a centralized JSON pool of browser fingerprints in 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— 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:
# 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:
-
Import the pool — Each module pulls the
uadictionary viafrom holehe.localuseragent import *orfrom holehe.localuseragent import ua. -
Random selection — At request time, code calls
random.choice()on the desired browser category. -
Header injection — The selected string populates the
User-Agentheader.
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
# 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:
# 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 and 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.pyas 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 sameuapool, preventing inconsistent detection exposure. -
Zero external dependencies: The mechanism relies only on Python's standard
randomandjsonmodules.
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 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, 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →