Email Finder API in Python: Find & Verify Emails with requests (2026)
Python is the most popular language for data enrichment scripts, CRM automation, and prospecting pipelines. This guide has working code for every emailfinder.dev endpoint — from a single person lookup to a CSV enrichment script with parallel processing.
All code uses the standard requests library. No SDK required.
Prerequisites
Install the requests library if you do not have it already: pip install requests
Get your API key by creating a free account at emailfinder.dev/register. Your API key is available in the dashboard under Settings.
Person email lookup
The most common lookup: find a verified email from a full name and company domain.
import requests API_KEY = 'your_api_key_here' BASE_URL = 'https://www.emailfinder.dev/api' def find_person_email(full_name, domain): response = requests.get( f'{BASE_URL}/find-email/person', params={'full_name': full_name, 'domain': domain}, headers={'Authorization': f'Bearer {API_KEY}'} ) data = response.json() if data.get('status') == 'found': return data['email'] return None email = find_person_email('Jane Doe', 'acme.com') print(email) # jane.doe@acme.com or None
The response includes status (found or not_found), email, full_name, title, and linkedin_url when available. You are only charged for found results.
LinkedIn lookup
Resolve a verified email from any LinkedIn profile URL:
def find_linkedin_email(linkedin_url): response = requests.get( f'{BASE_URL}/find-email/linkedin', params={'linkedin_url': linkedin_url}, headers={'Authorization': f'Bearer {API_KEY}'} ) data = response.json() return data.get('email') if data.get('status') == 'found' else None
The linkedin_url parameter accepts both full URLs (https://www.linkedin.com/in/username) and short forms (linkedin.com/in/username).
Company emails
Find up to 20 verified email addresses at a company domain:
def find_company_emails(domain): response = requests.get( f'{BASE_URL}/find-email/company', params={'domain': domain}, headers={'Authorization': f'Bearer {API_KEY}'} ) data = response.json() return data.get('emails', []) emails = find_company_emails('stripe.com') for contact in emails: print(contact['email'], contact.get('full_name'))
Company lookups cost 5 credits per successful result. Returns a list of verified contacts at the domain.
Decision maker lookup
Find the verified email of a specific role at a company — no name needed:
def find_decision_maker(domain, role): response = requests.get( f'{BASE_URL}/find-email/decision-maker', params={'domain': domain, 'decision_maker_category': role}, headers={'Authorization': f'Bearer {API_KEY}'} ) data = response.json() if data.get('status') == 'found': return {'email': data['email'], 'name': data.get('full_name'), 'title': data.get('title')} return None ceo = find_decision_maker('stripe.com', 'ceo') print(ceo)
Batch processing with ThreadPoolExecutor
For enriching a list of contacts efficiently, use ThreadPoolExecutor to run lookups in parallel:
from concurrent.futures import ThreadPoolExecutor, as_completed contacts = [ {'name': 'Jane Doe', 'domain': 'acme.com'}, {'name': 'John Smith', 'domain': 'example.com'}, {'name': 'Alice Brown', 'domain': 'techcorp.io'}, ] def enrich(contact): email = find_person_email(contact['name'], contact['domain']) return {**contact, 'email': email} with ThreadPoolExecutor(max_workers=10) as executor: futures = {executor.submit(enrich, c): c for c in contacts} results = [] for future in as_completed(futures): results.append(future.result()) for r in results: print(r['name'], r.get('email'))
The API supports 1,000 requests per minute, so max_workers=10 is conservative and safe. Increase to 20 or 50 for faster throughput on large batches.
CSV enrichment example
Read a CSV with name and domain columns, enrich each row with a verified email, and write the results back:
import csv def enrich_csv(input_path, output_path): with open(input_path, newline='') as infile, open(output_path, 'w', newline='') as outfile: reader = csv.DictReader(infile) fieldnames = reader.fieldnames + ['email'] writer = csv.DictWriter(outfile, fieldnames=fieldnames) writer.writeheader() for row in reader: email = find_person_email(row['full_name'], row['domain']) row['email'] = email or '' writer.writerow(row) enrich_csv('contacts.csv', 'enriched.csv')
Error handling
Handle the two most common error cases: not_found (no verified email found) and 429 rate limit (too many requests):
import time def find_email_with_retry(full_name, domain, max_retries=3): for attempt in range(max_retries): response = requests.get( f'{BASE_URL}/find-email/person', params={'full_name': full_name, 'domain': domain}, headers={'Authorization': f'Bearer {API_KEY}'} ) if response.status_code == 429: wait = 2 ** attempt # exponential backoff time.sleep(wait) continue data = response.json() if data.get('status') == 'found': return data['email'] return None # not_found return None
A 404 status with status=not_found means no verified email exists — this is a normal result, not an error. Only 429 and 5xx responses warrant retries.