Waterfall Contact Enrichment
Pricing
Pay per usage
Waterfall Contact Enrichment
Find business emails, phones, and social profiles from a name + company domain. Cascades through MX validation, website scraping, pattern detection, and SMTP verification. Free Clay alternative.
Pricing
Pay per usage
Rating
0.0
(0)
Developer

ryan clinton
Actor stats
1
Bookmarked
9
Total users
4
Monthly active users
2 days ago
Last modified
Categories
Share
Clay alternative — find anyone's business email, phone number, and social profiles from their name + company domain. Zero external API costs.
Cascades through 4 data sources until contact info is found:
- Email pattern generation — 15+ common B2B email patterns (first.last, flast, etc.)
- Website scraping — finds emails, phones, and social links on the company website
- Email pattern detection — identifies the company's actual email format
- Social profile discovery — generates likely LinkedIn, GitHub, and Twitter URLs
Each result includes a confidence score (0-100) and status: found, likely, or not_found.
Key features
- Zero external API costs — no Hunter, Clearbit, or ZoomInfo subscription needed
- Waterfall enrichment — cascades through multiple free sources for maximum coverage
- Email + phone + socials — full contact enrichment, not just email
- 15+ email patterns — covers first.last, firstlast, flast, f.last, last.first, and more
- SMTP verification (deep mode) — confirms the exact mailbox exists on the mail server
- Website scraping — leverages our Contact Scraper to find real data on company sites
- Pattern detection — detects the company's email format, then applies it to the person
- Per-domain caching — efficient for bulk lists (multiple people at the same company)
- Batch processing — configurable concurrency for speed
How the waterfall works
Input: {name, domain} → Source 1 → Source 2 → Source 3 → Source 4 → Best matchSource 1: Generate 15+ email candidates from person's nameSource 2: Scrape company website for real emails, phones, socialsSource 3: Detect company's email pattern, apply to personSource 4: Generate likely social profile URLsCross-reference all sources → Score by confidence → Return best email
Why waterfall? Single sources miss 40-50% of emails. By combining multiple approaches, we boost match rates to 70-90%.
Example output
{"firstName": "Jan","lastName": "Curn","domain": "apify.com","company": null,"email": "jan.curn@apify.com","emailConfidence": 85,"emailSource": "pattern_detection","phone": "+420 234 567 890","phoneSource": "website","socialProfiles": {"linkedin": "https://www.linkedin.com/in/jan-curn/","github": "https://github.com/jancurn","twitter": "https://twitter.com/jancurn"},"status": "found","sources": {"patternGeneration": {"candidates": ["jan.curn@apify.com", "jan@apify.com", "jcurn@apify.com"],"topPattern": "first.last"},"websiteScraping": {"emailsFound": ["hello@apify.com"],"phonesFound": ["+420 234 567 890"],"socialsFound": ["linkedin.com/in/jancurn"]},"patternDetection": {"detectedPattern": "first.last","patternConfidence": 85,"generatedEmail": "jan.curn@apify.com"}},"allCandidates": [{"email": "jan.curn@apify.com", "pattern": "first.last", "confidence": 85, "sources": ["pattern_generation", "pattern_detection"]},{"email": "jan@apify.com", "pattern": "first", "confidence": 65, "sources": ["pattern_generation"]}],"domainValid": true,"mxHost": "aspmx.l.google.com","verifiedAt": "2026-02-07T12:00:00.000Z"}
Input
| Field | Type | Default | Description |
|---|---|---|---|
people | array of objects | required | Each: {firstName, lastName, domain} or {fullName, domain}. Optional: company |
enrichFromWebsite | boolean | true | Scrape company website for real contact data (calls Contact Scraper) |
detectPattern | boolean | true | Detect company's email pattern (calls Email Pattern Finder) |
verificationLevel | enum | "standard" | standard (MX only) or deep (MX + SMTP) |
smtpTimeout | integer (3-30) | 10 | SMTP timeout in seconds (deep mode) |
maxConcurrency | integer (1-5) | 3 | Parallel person lookups |
Person object format
Each person in the people array needs:
{"firstName": "Jan", "lastName": "Curn", "domain": "apify.com"}
Or with full name:
{"fullName": "Jan Curn", "domain": "apify.com"}
Optional fields: company (string) for display purposes.
Verification levels
| Level | What it checks | Speed | Accuracy |
|---|---|---|---|
| Standard | MX records + pattern generation + website | ~5-15s/person | ~80% |
| Deep | + SMTP verification + catch-all detection | ~15-30s/person | ~95% |
Standard mode is recommended for most use cases. Deep mode connects to the mail server to verify each candidate individually.
Confidence scoring
| Scenario | Score |
|---|---|
| Direct match on company website + SMTP confirmed | 98 |
| SMTP confirmed + not catch-all domain | 95 |
| Direct match found on company website | 90 |
| Pattern matches detected company email format | 75-80 |
| Most common pattern (first.last) + MX valid | 70 |
| Common pattern (first, flast) + MX valid | 65 |
| Less common pattern + MX valid | 60 |
| Catch-all domain (accepts all addresses) | 50 |
| Domain has no MX records | 0 |
Status values
| Status | Meaning | Confidence |
|---|---|---|
found | High confidence email discovered | 70-98 |
likely | Best guess, moderate confidence | 40-69 |
not_found | Domain invalid or no viable candidates | 0-39 |
Email patterns generated
The actor tries 15+ common B2B email patterns for each person:
| Pattern | Example (Jan Curn) |
|---|---|
| first.last | jan.curn@domain.com |
| firstlast | jancurn@domain.com |
| first | jan@domain.com |
| flast | jcurn@domain.com |
| f.last | j.curn@domain.com |
| first_last | jan_curn@domain.com |
| first-last | jan-curn@domain.com |
| firstl | janc@domain.com |
| first.l | jan.c@domain.com |
| last.first | curn.jan@domain.com |
| lastfirst | curnjan@domain.com |
| last | curn@domain.com |
| last.f | curn.j@domain.com |
| lastf | curnj@domain.com |
| f_last | j_curn@domain.com |
International names are transliterated automatically (e.g., Rene -> rene, Munoz -> munoz).
Use cases
- Sales prospecting — find decision-maker emails from a list of names + companies
- Lead enrichment — add contact details to CRM records with only name + domain
- Outreach campaigns — build verified email lists for cold outreach
- Recruiting — find candidate contact info from LinkedIn profiles (name + company)
- Market research — enrich company databases with key contact information
- PR & media — find journalist and editor emails at publications
Pipeline integration
Combine with our other B2B actors for a complete lead generation pipeline:
- Google Maps Lead Enricher or B2B Lead Gen Suite — find businesses and basic contacts
- Waterfall Contact Enrichment — find specific decision-maker emails
- Bulk Email Verifier — verify deliverability before sending
- HubSpot Lead Pusher — push enriched contacts to your CRM
Cost structure
The actor itself has minimal compute cost. When enrichFromWebsite and detectPattern are enabled, it calls two sub-actors:
- Website Contact Scraper — ~$0.001 per domain (one call per unique domain)
- Email Pattern Finder — ~$0.001 per domain (one call per unique domain)
Sub-actor results are cached per domain, so 50 people at the same company only trigger one sub-actor call.
Deep mode adds SMTP connections which take 2-10 seconds per candidate (rate-limited to protect mail servers).
API usage
Python
from apify_client import ApifyClientclient = ApifyClient("YOUR_API_TOKEN")run = client.actor("ryanclinton/waterfall-contact-enrichment").call(run_input={"people": [{"firstName": "Jan", "lastName": "Curn", "domain": "apify.com"},{"firstName": "Elon", "lastName": "Musk", "domain": "tesla.com"},],"enrichFromWebsite": True,"detectPattern": True,"verificationLevel": "standard",})for item in client.dataset(run["defaultDatasetId"]).iterate_items():print(f"{item['firstName']} {item['lastName']}: {item['email']} ({item['emailConfidence']}%)")
JavaScript
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_API_TOKEN' });const run = await client.actor('ryanclinton/waterfall-contact-enrichment').call({people: [{ firstName: 'Jan', lastName: 'Curn', domain: 'apify.com' },{ firstName: 'Elon', lastName: 'Musk', domain: 'tesla.com' },],enrichFromWebsite: true,detectPattern: true,verificationLevel: 'standard',});const { items } = await client.dataset(run.defaultDatasetId).listItems();items.forEach(item => {console.log(`${item.firstName} ${item.lastName}: ${item.email} (${item.emailConfidence}%)`);});
How it works
- Domain validation — DNS MX lookup confirms the domain can receive email
- Pattern generation — creates 15+ email candidates from the person's name
- Website scraping — calls our Contact Scraper to find real emails, phones, and social links on the company website
- Pattern detection — calls our Email Pattern Finder to identify the company's email format (e.g., first.last@domain.com)
- Cross-referencing — matches website-found emails against the person's name patterns
- SMTP verification (deep mode) — connects to the mail server to verify the top candidates
- Confidence scoring — scores each candidate based on all signals
- Result ranking — returns the best email with confidence score, plus phone and social profiles
Limitations
- Website scraping requires the company to have publicly listed contact information
- SMTP verification (deep mode) may be blocked by some corporate mail servers
- Catch-all domains cannot be reliably verified (flagged as "unknown" with lower confidence)
- Social profile URLs are generated guesses unless found on the company website
- Phone numbers can only be found via website scraping (no phone pattern generation)
- Free-tier Apify accounts may hit memory limits when running sub-actors concurrently