How Holehe Exports Scan Results to CSV: Complete Implementation Guide
Holehe's CSV export functionality is implemented in the export_csv function in holehe/core.py, which writes OSINT scan results to a timestamped UTF‑8 CSV file when the -C/--csv flag is provided.
Holehe's CSV export functionality allows security researchers and OSINT investigators to persist email reconnaissance results for further analysis. This article examines the complete implementation details of how the megadose/holehe repository converts scan data into structured CSV output, including the data schema, file naming conventions, and programmatic usage patterns.
Where CSV Export Is Implemented
The entire CSV export pipeline lives in holehe/core.py, specifically lines 54–64 for the write logic and lines 190–194 for argument parsing. The export_csv function serves as the single entry point for all CSV operations, making the codebase maintainable and predictable.
The export_csv Function Signature and Behavior
The export_csv function accepts three parameters: the scan results list (data), the parsed arguments object (args), and the target email address. It conditionally executes only when args.csvoutput evaluates to True.
# From holehe/core.py lines 54-64
def export_csv(data, args, email):
if args.csvoutput:
filename = "holehe_" + str(int(time.time())) + "_" + email + "_results.csv"
with open(filename, "w", newline='', encoding='utf-8') as f:
fc = csv.DictWriter(f, fieldnames=data[0].keys())
fc.writeheader()
fc.writerows(data)
print("All results have been exported to " + filename)
exit()
Key implementation decisions in this function include:
- Timestamped filenames: Uses
time.time()converted to integer seconds to prevent collisions - Windows-compatible line endings: Opens file with
newline=''to suppress extra blank lines on Windows - Dynamic headers: Derives column names from
data[0].keys()ensuring schema flexibility - UTF‑8 encoding: Explicit encoding declaration for international character support
How the -C/--csv Flag Triggers Export
The command-line interface defines the CSV flag through argparse at lines 192–194 in holehe/core.py:
# From holehe/core.py lines 192-194
parser.add_argument("-C","--csv", default=False, required=False,
action="store_true", dest="csvoutput",
help="Create a CSV with the results")
When invoked, this flag sets args.csvoutput = True, which the main execution flow passes to export_csv. The action="store_true" pattern ensures the flag requires no additional value—its mere presence enables the feature.
CSV Data Schema and Field Definitions
Each row in the exported CSV represents a single module's execution result. The csv.DictWriter serializes Python dictionaries into columns, with the following standardized fields across all modules:
| Field | Type | Description |
|---|---|---|
name |
string | Internal module identifier (e.g., twitter, gmail, adobe) |
domain |
string | Target service domain (e.g., twitter.com, gmail.com) |
rateLimit |
boolean | True if the request encountered rate limiting |
exists |
boolean | True when an account exists for the queried email |
emailrecovery |
string or None |
Partially obfuscated recovery email from the service |
phoneNumber |
string or None |
Partially obfuscated recovery phone number |
others |
string or None |
Additional metadata such as full name, account creation date, or country |
The others field demonstrates significant variance across modules—some services return rich metadata while others provide none. This is represented as None in the CSV when absent.
Command-Line Usage Examples
Basic CSV Export
Generate a CSV report from the command line:
holehe victim@example.com -C
Output:
All results have been exported to holehe_1723862123_victim@example.com_results.csv
Combined with Other Flags
The CSV flag composes with standard Holehe options:
holehe target@domain.com -C --only-used --timeout 15
This exports only services where accounts exist (exists=True), with extended timeout.
Programmatic CSV Export
For integration into larger Python workflows, instantiate the Args class and call export_csv directly:
import httpx
import trio
from holehe.core import export_csv, get_functions, import_submodules
async def run_email_reconnaissance(email: str):
"""Execute Holehe scan and export to CSV programmatically."""
# Load all OSINT modules
modules = import_submodules("holehe.modules")
functions = get_functions(modules)
# Collect results from each module
results = []
async with httpx.AsyncClient(timeout=10) as client:
for func in functions:
await func(email, client, results)
# Configure and trigger CSV export
class Args:
csvoutput = True
export_csv(results, Args(), email)
# Execute
trio.run(run_email_reconnaissance, "victim@example.com")
This pattern enables:
- Custom filtering before export
- Batch processing of multiple emails
- Integration with async application frameworks
- Automated reporting pipelines
File Naming and Output Location
Holehe writes CSV files to the current working directory using this pattern:
holehe_<unix_timestamp>_<email>_results.csv
Example: holehe_1723862123_victim@example.com_results.csv
The timestamp provides chronological ordering and prevents accidental overwrites when scanning the same email repeatedly. Note that special characters in email addresses are preserved in filenames, which may require quoting when processing in shell environments.
Integration with Module Result Structure
Each module in holehe/modules/ returns a dictionary conforming to the CSV schema. For example, the Adobe module constructs results like:
# Simplified from holehe/modules/software/adobe.py
return {
"name": "adobe",
"domain": "adobe.com",
"rateLimit": False,
"exists": True,
"emailrecovery": "v***@gmail.com",
"phoneNumber": "+1 5***12",
"others": {"fullName": "John Doe", "createdDate": "2015-03-12"}
}
The export_csv function's use of data[0].keys() for fieldnames means modules can theoretically extend the schema, though consistency across modules ensures predictable CSV output.
Error Handling and Edge Cases
The current implementation has specific behaviors worth noting:
- Empty results: If
datais empty,data[0].keys()raisesIndexError—the function assumes at least one result - Exit on completion:
export_csvcallsexit()after writing, terminating the entire process - No append mode: Each invocation creates a new file; no mechanism exists to append to existing CSVs
Summary
- Primary location:
holehe/core.pylines 54–64 contain theexport_csvfunction that implements CSV export functionality - Trigger mechanism: The
-C/--csvcommand-line flag, parsed at lines 192–194, activates export viaargs.csvoutput - File format: UTF‑8 encoded CSV with headers dynamically generated from result dictionary keys
- Output filename pattern:
holehe_<timestamp>_<email>_results.csvin the current working directory - Data schema: Seven standard fields (
name,domain,rateLimit,exists,emailrecovery,phoneNumber,others) populated by each OSINT module - Implementation library: Python's standard
csv.DictWriterwithnewline=''for cross-platform compatibility
Frequently Asked Questions
What information does Holehe include in CSV exports?
Holehe CSV exports contain seven fields per service checked: the module name, domain, rate limit status, account existence boolean, recovery email and phone (obfuscated), and miscellaneous metadata. These correspond exactly to the internal result dictionaries generated by each OSINT module in holehe/modules/.
How do I disable CSV export when running Holehe programmatically?
Set csvoutput = False in your arguments object. The export_csv function returns immediately without file operations when this attribute is falsy, allowing normal continuation of program execution without the exit() call.
Can I customize the CSV filename or location?
The current implementation in holehe/core.py hardcodes the filename pattern and current working directory. To customize, modify the filename assignment at line 58 or wrap export_csv with your own function that renames the file post-creation.
Why does Holehe exit after CSV export?
The exit() call at line 64 of holehe/core.py ensures clean termination after successful export in CLI contexts. For programmatic use where continuation is required, either avoid calling export_csv directly or catch SystemExit and suppress it.
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 →