How User-Agent Strings Are Managed in Holehe Modules: Centralized JSON and Per-Module Randomization

Holehe manages User-Agent strings through a centralized JSON dictionary in holehe/localuseragent.py that modules import via wildcard and sample randomly to mimic realistic browser fingerprints.

The open-source OSINT tool Holehe (maintained by megadose) takes a deliberate approach to HTTP header management. Rather than hardcoding User-Agent strings across its ~800 modules, the project centralizes all values in a single source file. This design ensures consistency while allowing each module to randomize requests and blend in with legitimate traffic.

Centralized User-Agent Storage in localuseragent.py

All User-Agent strings live in holehe/localuseragent.py, which defines a JSON object named ua containing browser-family groupings.


# holehe/localuseragent.py

import json

ua = json.loads('''{
  "browsers": {
    "chrome": [
      "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36",
      "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36",
      …
    ],
    "firefox": [
      "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:55.0) Gecko/20100101 Firefox/55.0",
      …
    ],
    "safari": [
      "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Safari/605.1.15",
      …
    ],
    "iOS": [
      "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Mobile/15E148 Safari/604.1",
      …
    ]
  }
}''')

The structure supports four browser families: chrome, firefox, safari, and iOS. Each contains multiple realistic strings representing different platforms and versions.

Module-Level Implementation Pattern

Every Holehe module follows the same three-step pattern for User-Agent management.

Step 1: Wildcard Import

Modules import the entire localuseragent namespace:


# Example: holehe/modules/transport/blablacar.py

from holehe.localuseragent import *
import random

This wildcard approach gives direct access to ua without repetitive import statements across hundreds of files.

Step 2: Random Selection

Modules use random.choice() to select from their preferred browser family:


# holehe/modules/transport/blablacar.py (line 12)

headers = {
    'User-Agent': random.choice(ua["browsers"]["firefox"]),
    ...
}

Each request thus carries a different User-Agent, preventing signature-based blocking.

Step 3: Request Construction

The selected string joins other headers in the final request configuration.

Browser Family Selection Strategy

Holehe modules strategically choose browser families to match expected client behavior on target services:

Module Category Typical Browser Choice Rationale
Software/tech services Chrome Matches dominant desktop browser market share
Social media platforms Firefox Diversifies fingerprint; avoids over-representing Chrome
Mobile-centric services iOS Mimics authentic mobile app traffic

Advanced Pattern: Persistent User-Agent Variables

Some modules deviate from inline random.choice() calls to reuse a single User-Agent across multiple related requests. The Tumblr module demonstrates this technique:


# holehe/modules/social_media/tumblr.py (lines 11-20)

usrag = random.choice(ua["browsers"]["iOS"])
...
"User-Agent": usrag

By storing the randomized iOS UA in usrag, the module maintains session consistency—critical for services that correlate consecutive requests from the same "device."

Complete Implementation Examples

Standard Chrome Selection

from holehe.localuseragent import *
import random, requests

def check_service(email):
    url = "https://example.com/api"
    headers = {
        "User-Agent": random.choice(ua["browsers"]["chrome"]),
        "Accept": "application/json",
    }
    resp = requests.get(url, headers=headers)
    return resp.json()

iOS Session Persistence (Tumblr-Style)

from holehe.localuseragent import *
import random, requests

def tumblr_profile(username):
    usrag = random.choice(ua["browsers"]["iOS"])
    url = f"https://www.tumblr.com/api/read/json?type=post&filter=raw&num=10&username={username}"
    headers = {"User-Agent": usrag}
    resp = requests.get(url, headers=headers)
    return resp.json()

Key Source Files

Summary

  • Single source of truth: The ua dictionary in localuseragent.py centralizes all User-Agent strings
  • Wildcard imports provide clean module-level access without import proliferation
  • Per-request randomization via random.choice() prevents fingerprinting
  • Strategic browser selection matches target service expectations
  • Session persistence variables support multi-request workflows when needed

Frequently Asked Questions

Can I add custom User-Agent strings to Holehe?

Yes. Edit holehe/localuseragent.py and append strings to the appropriate browser list within the ua JSON structure. All modules will automatically access new entries through the existing wildcard import pattern.

Why does Holehe use wildcard imports instead of explicit imports?

Wildcard imports from localuseragent eliminate repetitive import statements across ~800 modules. The trade-off—namespace pollution—is acceptable because ua is the only exported name and collision risk is minimal in this controlled codebase.

How does random User-Agent selection improve OSINT effectiveness?

Services tracking request signatures see varied browser fingerprints instead of a single automated pattern. This reduces detection likelihood and prevents IP-based blocking triggered by consistent header signatures.

What happens if a module doesn't specify a browser family?

All modules explicitly select a family (chrome, firefox, safari, or iOS). The codebase contains no fallback default; each module author deliberately chooses based on the target service's typical user base.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →