Skills hunter-io
install
source · Clone the upstream repo
git clone https://github.com/TerminalSkills/skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/TerminalSkills/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/hunter-io" ~/.claude/skills/terminalskills-skills-hunter-io && rm -rf "$T"
manifest:
skills/hunter-io/SKILL.mdsafety · automated scan (medium risk)
This is a pattern-based risk scan, not a security review. Our crawler flagged:
- pip install
- references API keys
Always read a skill's source content before installing. Patterns alone don't mean the skill is malicious — but they warrant attention.
source content
Hunter.io
Overview
Hunter.io is a professional email discovery service that indexes publicly available email addresses from websites, LinkedIn, corporate directories, and other sources. Its API provides domain-level email search, individual email lookup, email pattern detection, and deliverability verification. In OSINT contexts, Hunter is invaluable for building contact lists during pre-engagement recon and identifying employee email formats.
Requires: Hunter.io API key (free tier: 25 searches/month; paid plans for bulk use).
Instructions
Step 1: Setup
pip install requests
import requests import time import json from typing import Optional HUNTER_API_KEY = "YOUR_HUNTER_IO_API_KEY" BASE_URL = "https://api.hunter.io/v2" def hunter_request(endpoint, params): """Make an authenticated request to the Hunter.io API.""" params["api_key"] = HUNTER_API_KEY response = requests.get(f"{BASE_URL}/{endpoint}", params=params) response.raise_for_status() return response.json() # Check your account status and remaining credits def check_account(): data = hunter_request("account", {}) account = data["data"] print(f"Plan: {account['plan_name']}") print(f"Searches used: {account['requests']['searches']['used']} / {account['requests']['searches']['available']}") print(f"Verifications used: {account['requests']['verifications']['used']} / {account['requests']['verifications']['available']}") check_account()
Step 2: Domain search — find all emails for a domain
def domain_search(domain, limit=10, offset=0, seniority=None, department=None): """ Search for all email addresses associated with a domain. seniority: junior, senior, executive department: it, finance, management, sales, legal, communication, marketing, hr, engineering """ params = { "domain": domain, "limit": limit, "offset": offset, } if seniority: params["seniority"] = seniority if department: params["department"] = department data = hunter_request("domain-search", params) result = data["data"] print(f"\n=== Domain Search: {domain} ===") print(f"Total emails indexed: {result['meta']['total']}") print(f"Email pattern: {result.get('pattern', 'Unknown')}") print(f"Organization: {result.get('organization', 'N/A')}") print(f"Domain type: {result.get('type', 'N/A')}") emails = result.get("emails", []) print(f"\nFound {len(emails)} emails (showing up to {limit}):") for email in emails: confidence = email.get("confidence", 0) name = f"{email.get('first_name', '')} {email.get('last_name', '')}".strip() position = email.get("position", "") dept = email.get("department", "") sources = len(email.get("sources", [])) print(f" {email['value']:<40} {confidence}% confidence | {name} | {position} | {dept} | {sources} sources") return result # Basic domain search result = domain_search("example.com", limit=20) # Filter by department result = domain_search("example.com", department="engineering", limit=10) # Filter by seniority result = domain_search("example.com", seniority="executive", limit=5)
Step 3: Paginate through all emails
def get_all_emails(domain, delay_seconds=1.0): """Retrieve all emails for a domain, handling pagination.""" all_emails = [] offset = 0 limit = 100 # Max per request # Get total count first first_page = hunter_request("domain-search", {"domain": domain, "limit": 1, "offset": 0}) total = first_page["data"]["meta"]["total"] pattern = first_page["data"].get("pattern", "unknown") print(f"Fetching {total} emails for {domain} (pattern: {pattern})") while offset < total: page_data = hunter_request("domain-search", { "domain": domain, "limit": limit, "offset": offset, }) emails = page_data["data"].get("emails", []) all_emails.extend(emails) offset += limit print(f" Retrieved {len(all_emails)}/{total}") if len(emails) < limit: break time.sleep(delay_seconds) # Respect rate limits print(f"Total collected: {len(all_emails)}") return all_emails, pattern emails, pattern = get_all_emails("example.com") # Save to JSON with open("emails_example_com.json", "w") as f: json.dump({"pattern": pattern, "emails": emails}, f, indent=2)
Step 4: Email finder — generate a specific person's email
def find_email(first_name, last_name, domain): """ Find the email address for a specific person at a company. Returns the most likely email and confidence score. """ params = { "first_name": first_name, "last_name": last_name, "domain": domain, } data = hunter_request("email-finder", params) result = data["data"] email = result.get("email") confidence = result.get("score", 0) sources = result.get("sources", []) if email: print(f"Found: {email} (confidence: {confidence}%)") print(f"Sources: {len(sources)} public references") for source in sources[:3]: print(f" - {source.get('uri', 'N/A')}") else: print(f"No email found for {first_name} {last_name} @ {domain}") print(f"Pattern: {result.get('pattern', 'unknown')}") return result find_email("John", "Smith", "example.com") find_email("Jane", "Doe", "example.com")
Step 5: Email verifier — check if an email is deliverable
def verify_email(email): """ Verify whether an email address is deliverable. Result statuses: - valid: email exists and is deliverable - invalid: email does not exist - accept_all: server accepts all emails (can't verify) - webmail: free email provider (Gmail, Yahoo, etc.) - disposable: temporary/disposable email service - unknown: could not be determined """ data = hunter_request("email-verifier", {"email": email}) result = data["data"] status = result.get("status") score = result.get("score", 0) mx_records = result.get("mx_records", False) smtp_check = result.get("smtp_server", False) disposable = result.get("disposable", False) print(f"\nVerification: {email}") print(f" Status: {status}") print(f" Score: {score}/100") print(f" MX Records: {'✓' if mx_records else '✗'}") print(f" SMTP Check: {'✓' if smtp_check else '✗'}") print(f" Disposable: {'⚠ Yes' if disposable else 'No'}") return result verify_email("john.smith@example.com") verify_email("test@mailinator.com") # Disposable email example
Step 6: Bulk operations and reporting
def bulk_verify_emails(email_list, output_file="verification_results.json", delay=0.5): """Verify multiple email addresses and save results.""" results = [] for i, email in enumerate(email_list): print(f"[{i+1}/{len(email_list)}] Verifying {email}...") result = verify_email(email) results.append({"email": email, **result}) time.sleep(delay) # Avoid hitting rate limits # Summary statuses = {} for r in results: s = r.get("status", "unknown") statuses[s] = statuses.get(s, 0) + 1 print(f"\nVerification Summary:") for status, count in sorted(statuses.items(), key=lambda x: -x[1]): print(f" {status}: {count}") with open(output_file, "w") as f: json.dump(results, f, indent=2) print(f"Saved to {output_file}") return results def infer_email_pattern(pattern_str, first_name, last_name, domain): """Generate an email from the Hunter pattern for a person.""" f = first_name.lower() l = last_name.lower() fi = f[0] # First initial li = l[0] # Last initial replacements = { "{first}": f, "{last}": l, "{f}": fi, "{l}": li, "{f}.{last}": f"{fi}.{l}", "{first}.{last}": f"{f}.{l}", "{first}{last}": f"{f}{l}", "{first}_{last}": f"{f}_{l}", "{f}{last}": f"{fi}{l}", } email = pattern_str for placeholder, value in replacements.items(): email = email.replace(placeholder, value) return f"{email}@{domain}" # Usage: generate emails from the discovered pattern pattern = "{first}.{last}" names = [("Alice", "Johnson"), ("Bob", "Williams"), ("Carol", "Davis")] generated = [infer_email_pattern(pattern, fn, ln, "example.com") for fn, ln in names] bulk_verify_emails(generated)
Hunter.io API Endpoints Reference
| Endpoint | Method | Description |
|---|---|---|
| GET | All emails for a domain |
| GET | Find email for a person |
| GET | Verify deliverability |
| GET | Count emails for domain (free) |
| GET | Account status and credits |
Guidelines
- Rate limits: Free plan allows 25 searches/month and 50 verifications/month. Add delays between requests to avoid 429 errors. Paid plans have higher limits.
- Confidence scores: A score above 90% indicates high confidence the email is correct. Below 50% means it was inferred from pattern, not directly observed.
- Pattern detection: The email pattern (e.g.,
) is the most valuable single piece of data — use it to generate emails for known employees.{first}.{last}@domain.com - Ethical use: Hunter.io data comes from publicly indexed sources. Use it only for legitimate purposes such as authorized penetration testing scoping, sales prospecting, or journalism.
- Combine with verification: Always verify generated emails before using them to avoid bounces and reduce waste of phishing simulation scope.