How Holehe Detects and Handles Rate Limiting Across Different Services

Holehe uses a simple exception-based approach where any failed HTTP request sets rateLimit: True, combined with static frequent_rate_limit flags to warn about services known for aggressive throttling.

Holehe is an open-source OSINT tool by megadose that checks whether an email address is registered across hundreds of online services. Understanding how Holehe handles rate limiting is essential for anyone running large-scale email investigations or integrating the tool into automated workflows.

Two-Layer Rate Limiting System in Holehe

Holehe implements rate limiting detection through two complementary mechanisms: a static metadata flag that documents known restrictive services, and a runtime exception handler that catches any request failure.

Static Flag: frequent_rate_limit

Every service module in holehe/modules/ builds a result dictionary containing standardized fields. The frequent_rate_limit boolean is hardcoded by module authors to indicate services that typically enforce strict request throttling.


# From holehe/modules/social_media/wattpad.py

frequent_rate_limit = True  # Wattpad aggressively limits requests

This flag appears in modules like wattpad.py, blablacar.py, and taringa.py. It is informational only—Holehe does not modify behavior based on this value. Users can inspect it programmatically to identify potentially problematic services before scanning.

Runtime Detection: The rateLimit Field

The actual detection happens at runtime through exception handling. Each service module wraps its HTTP request in a try/except block that treats any exception as a rate-limiting event.

How Service Modules Detect Rate Limits

The detection pattern is consistent across all modules. When a request succeeds, rateLimit is set to False. When any exception occurs, it becomes True.


# Example from holehe/modules/social_media/twitter.py

async def twitter(email, client, out):
    frequent_rate_limit = False
    try:
        req = await client.get(
            "https://api.twitter.com/i/users/email_available.json",
            params={"email": email})
        if req.json()["taken"]:
            out.append({
                "name": "twitter",
                "domain": "twitter.com",
                "method": "register",
                "frequent_rate_limit": frequent_rate_limit,
                "rateLimit": False,      # Success

                "exists": True,
                # ... other fields

            })
        else:
            out.append({... "rateLimit": False, "exists": False, ...})
    except Exception:                    # Any network/HTTP error

        out.append({
            ...
            "rateLimit": True,           # Throttled or failed

            "exists": False,
            ...
        })

This pattern repeats in holehe/modules/social_media/xing.py, holehe/modules/transport/blablacar.py, and every other service module. The approach deliberately avoids parsing service-specific error codes, HTML responses, or HTTP status codes, making it resilient to variations between platforms.

Core Launcher Fallback Handling

When a module crashes outside its own exception handling—for example, due to a programming error—the central orchestrator prevents scan termination. In holehe/core.py (lines 66-78), the launch_module function catches unhandled exceptions:


# Simplified from holehe/core.py

async def launch_module(module, email, client, out):
    try:
        await module(email, client, out)
    except Exception:
        # Module crashed completely

        out.append({
            "name": module.__name__,
            "error": True,           # Distinct from rateLimit

            "rateLimit": False,
            ...
        })

This ensures that a single failing module does not abort the entire multi-service scan.

User-Facing Rate Limit Indicators

After all modules complete, print_result in holehe/core.py (lines 22-48) renders results with color-coded output:


# From holehe/core.py

if results["rateLimit"] and args.onlyused == False:
    websiteprint = print_color("[x] " + results["domain"], "yellow", args)
    print(websiteprint)
  • Green [+]: Email found (account exists)
  • Red [-]: Email not found
  • Yellow [x]: Rate limited—verification impossible

Practical Examples

Command-Line Usage

$ holehe -NP -C user@example.com

The -NP flag disables password-recovery checks. Rate-limited services appear with yellow markers:


[+] github.com
[x] twitter.com
[-] netflix.com

Programmatic Detection

import httpx
import trio
from holehe.modules.social_media import twitter

async def check_with_rate_limit_handling():
    async with httpx.AsyncClient(timeout=10) as client:
        out = []
        await twitter("target@example.com", client, out)
        
        result = out[0]
        
        if result["rateLimit"]:
            print(f"Service {result['domain']} throttled request")
            # Implement retry logic or proxy rotation here

        elif result["exists"]:
            print(f"Account found on {result['domain']}")
        else:
            print(f"No account on {result['domain']}")

trio.run(check_with_rate_limit_handling)

Building Retry Logic

async def check_with_retry(module, email, max_retries=3):
    for attempt in range(max_retries):
        async with httpx.AsyncClient() as client:
            out = []
            await module(email, client, out)
            
            if not out[0]["rateLimit"]:
                return out[0]
            
            await trio.sleep(2 ** attempt)  # Exponential backoff

    
    return {"error": "Max retries exceeded", "rateLimit": True}

Why This Design Works

  • Uniform handling: No service-specific parsing required
  • Resilience: Catches network timeouts, connection errors, HTTP 429s, and unusual responses equally
  • Simplicity: Module authors focus on success paths; failure handling is automatic
  • Transparency: Users always know when a result is uncertain due to throttling

Key Source Files

File Purpose
holehe/core.py Orchestrates concurrent module execution, aggregates results, and prints [x] markers for rate-limited services
holehe/modules/social_media/twitter.py Example module implementing try/except rate limit detection
holehe/modules/social_media/wattpad.py Module with frequent_rate_limit = True documentation flag
holehe/modules/transport/blablacar.py Transportation service using both flag types

Summary

  • Static frequent_rate_limit flag documents known restrictive services without affecting runtime behavior
  • Runtime rateLimit detection triggers on any exception in the HTTP request path
  • Exception-based design avoids parsing heterogeneous error responses across hundreds of services
  • Visual feedback prints yellow [x] markers for throttled services
  • Core fallback handling prevents scan crashes from unhandled module errors

Frequently Asked Questions

How can I tell if a result is unreliable due to rate limiting?

Check the rateLimit boolean in the result dictionary. When True, the exists field defaults to False not because the account was verified absent, but because the check could not complete. In command-line output, this appears as a yellow [x] instead of green [+] or red [-].

Why doesn't Holehe implement automatic retries or proxy rotation?

The tool prioritizes simplicity and transparency. Retry logic and proxy management are left to users or wrapper scripts, allowing customized strategies based on specific operational requirements. The consistent rateLimit flag enables easy integration with external retry mechanisms.

Does the frequent_rate_limit flag change how Holehe behaves?

No. This flag is purely informational, set by module authors to document services historically prone to throttling. It helps users interpret results and plan scan timing, but Holehe does not throttle itself or skip these services automatically.

What exceptions trigger the rateLimit flag?

Any exception during the HTTP request—including network timeouts, DNS failures, connection resets, SSL errors, and HTTP 429 responses—sets rateLimit: True. This broad catch ensures no throttling event goes unreported, though it may occasionally misclassify permanent failures as temporary rate limits.

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 →