Domain Contact Scraper – Email, Phone & Social Extractor
Pricing
from $2.00 / 1,000 domains
Domain Contact Scraper – Email, Phone & Social Extractor
Extract public emails, phone numbers, WhatsApp numbers, and social profiles from websites. Priority-first crawling returns one clean, consolidated result per submitted URL, with status and crawl diagnostics for automation.
Pricing
from $2.00 / 1,000 domains
Rating
5.0
(1)
Developer
Emiliano Mastragostino
Maintained by CommunityActor stats
5
Bookmarked
938
Total users
87
Monthly active users
7 days ago
Last modified
Categories
Share
🔎 Extract contact information from company websites
Domain Contact Scraper extracts public email addresses, phone numbers, WhatsApp numbers, and social profiles from websites.
Submit one website or a large list. The Actor prioritizes pages likely to contain contact information and returns one consolidated result for each valid submitted website URL.
- One website, one consolidated result
- Filtered and deduplicated contact data
- Priority-first contact discovery
- Progressive output for bulk runs
- Automation-ready status and diagnostics
Use the Actor to enrich company lists, prepare CRM imports, add contacts to directory data, or build automated lead-enrichment workflows.
No additional configuration is required for a basic run.
📊 What contact data can the Actor extract?
| Data | Output |
|---|---|
| Emails | Filtered, normalized, and deduplicated public email addresses |
| Phone numbers | Parsed numbers, normalized to E.164 when sufficient country context is available |
| WhatsApp numbers | Phone numbers extracted from supported WhatsApp links |
| Social profiles | Canonicalized LinkedIn, Facebook, Instagram, X, YouTube, TikTok, and other supported profiles |
| Website metadata | Submitted start URL and redirected start-page URL |
| Crawl diagnostics | Status, page counters, timing, termination reason, and sanitized errors |
✨ Why use Domain Contact Scraper?
One website, one consolidated result
Generic website scrapers usually return one record per visited page.
Domain Contact Scraper combines contacts found across the crawled pages into one structured record. Each valid submitted start URL normally produces one result.
This makes it easier to:
- join contacts back to an existing lead or company record;
- avoid merging page-level results manually;
- import data into a CRM or database;
- build predictable automation workflows.
Priority-first contact discovery
The Actor prioritizes pages likely to contain contacts, including:
- contact pages;
- about and company pages;
- legal, privacy, and imprint pages;
- multilingual equivalents such as
kontakt,contacto,contato, andimpressum.
You control the page budget, crawl depth, URL scope, concurrency, proxy use, and optional early stopping.
Built for bulk automation
Results are written as soon as each website finishes, so faster websites do not wait for the slowest one in the run.
Every record includes machine-readable information for routing results, identifying retryable failures, and monitoring crawl coverage.
Compared with a generic website scraper
| Generic website scraper | Domain Contact Scraper |
|---|---|
| One result per page | One consolidated result per submitted website URL |
| Raw HTML or text | Structured contact fields |
| Manual extraction rules | Built-in contact discovery and validation |
| Duplicate candidates | Normalization and deduplication |
| Limited crawl context | Status, counters, timing, and errors |
⚙️ How it works
For every valid submitted start URL, the Actor:
- opens the website;
- identifies and prioritizes likely contact pages;
- crawls within the configured URL scope, depth, and page budget;
- extracts email, phone, WhatsApp, and social-profile candidates;
- validates, normalizes, and deduplicates the contacts;
- writes one consolidated result when the website finishes.
🚀 How to scrape website contacts
- Add one representative URL per website, preferably the homepage.
- Keep the defaults or adjust the crawl settings.
- Run the Actor.
- Review the consolidated records.
- Export the data or consume it through the Apify API, SDKs, webhooks, or schedules.
Basic input Example
{"startUrls": [{"url": "https://apify.com"}]}
Full Input Example
{"startUrls": [{"url": "https://example.com"},{"url": "https://another-example.com"}],"maxPagesPerStartUrl": 12,"maxDepth": 2,"defaultPhoneCountry": "US","enqueueStrategy": "same-domain","allowedSubdomains": ["www", "support"],"earlyStopMode": "balanced","maxRequestsPerCrawl": 500,"maxConcurrency": 10,"checkpointIntervalSec": 60,"proxyConfigurationOptions": {"useApifyProxy": true}}
This example shows the full available configuration. For most runs, you can start with the defaults and change only the fields you actually need.
Output example
Each result contains the consolidated contacts together with crawl status and diagnostics:
{"schemaVersion": 1,"status": "completed","hasContacts": true,"startUrl": "https://example.org","finalUrl": "https://example.org/contact","contacts": {"emails": ["info@example.org"],"phones": ["+15551234567"],"socials": [{"type": "linkedin","value": "https://linkedin.com/company/example"}],"whatsapps": ["+15551234567"]},"crawl": {"pagesVisited": 4,"pagesAttempted": 4,"pagesSucceeded": 4,"pagesFailed": 0,"visitedSample": ["https://example.org/", "https://example.org/contact"],"stoppedReason": "early-stop","terminationReason": "early_stop","startPageFinalUrl": "https://www.example.org/","startPageHttpStatusCode": 200,"startedAt": "2026-01-01T12:00:00.000Z","finishedAt": "2026-01-01T12:00:04.200Z","durationMs": 4200,"error": null,"updatedAt": "2026-01-01T12:00:04.200Z"}}
🎯 Common use cases
Enrich company and lead lists
Append public emails, phone numbers, WhatsApp numbers, and social profiles to existing website records.
Prepare CRM-ready records
Produce one consolidated result per submitted website and join it to your source data through startUrl.
Enrich directory and marketplace data
Use the Actor after collecting company websites from directories, maps, marketplaces, or other scrapers.
Build and audit business datasets
Add contact channels to supplier, agency, dealership, local-business, property, ecommerce, or service-provider lists.
You can also distinguish websites that:
- expose supported contacts;
- were processed successfully but contain no supported contacts;
- failed because of blocking, invalid URLs, or network errors.
💳 Pricing
You are charged only for records successfully written to the final dataset.
In a normally completed run, each valid submitted start URL produces one billable result. This includes records with status: "failed", because they still contain failure details and diagnostics.
Run cost is therefore normally predictable from the number of valid start URLs:
100 valid start URLs↓Normally 100 dataset records↓Normally 100 billable result events
If a run is aborted or times out, websites that do not produce a dataset record are not charged as results.
Check the Actor’s pricing page for the current price per result and available pricing tiers.
🛠️ Input configuration
Input reference
| Field | Required | Default | Description |
|---|---|---|---|
startUrls | Yes | — | Website URLs processed independently |
maxPagesPerStartUrl | No | 8 | Maximum pages per start URL |
maxDepth | No | 1 | Maximum link depth |
defaultPhoneCountry | No | Empty | Optional fallback country for ambiguous local numbers |
enqueueStrategy | No | same-hostname | Controls which links may be followed |
allowedSubdomains | No | — | Optional subdomain allowlist for same-domain |
earlyStopMode | No | off | Controls adaptive early stopping |
maxRequestsPerCrawl | No | — | Optional global request limit |
maxConcurrency | No | Runtime default | Maximum concurrent requests |
checkpointIntervalSec | No | — | Optional checkpoint interval |
proxyConfigurationOptions | No | Proxy disabled | Apify proxy configuration |
Start URLs
Submit one representative URL per website, preferably the homepage.
Page budget and depth
maxPagesPerStartUrl controls the page budget for each start URL.
maxDepth controls link traversal:
0— only the submitted page;1— also follow eligible links from the submitted page;2— also follow eligible links discovered at depth one.
The page budget applies regardless of depth.
Crawl scope
| Strategy | Behavior |
|---|---|
same-hostname | Same hostname only |
same-domain | Registrable domain and subdomains |
same-origin | Same protocol, hostname, and port |
all | External links that pass the remaining filters |
Use same-hostname for most runs.
Use same-domain when relevant content may exist on subdomains. Combine it with allowedSubdomains to restrict which subdomains may be visited.
Early stopping
off— use the configured page and depth budgets;balanced— stop when enough contact signals are found or exploration becomes unproductive;aggressive— stop earlier to prioritize speed.
Phone country fallback
defaultPhoneCountry has no default value. Set it only when one fallback country is appropriate for ambiguous local numbers.
For example:
{"defaultPhoneCountry": "US"}
Country precedence is:
- explicit international number;
- reliable website country context;
defaultPhoneCountry;- unresolved local-number handling.
For mixed international lists, leave this field empty.
Proxy and advanced controls
A proxy may improve access to websites that restrict direct requests, but it does not guarantee access or render JavaScript-only content.
Advanced controls include:
maxRequestsPerCrawl— global request cap;maxConcurrency— concurrent request limit;checkpointIntervalSec— periodic state checkpointing.
Checkpointing improves resilience but does not guarantee exactly-once execution across platform restarts.
Suggested configurations
| Goal | Pages | Depth | Early stop |
|---|---|---|---|
| Faster bulk run | 4 | 1 | balanced |
| Standard enrichment | 8 | 1 | off |
| Broader exploration | 20 | 2 | off |
These are starting points. Performance depends on website structure, response time, blocking, proxies, and Actor resources.
📦 Output semantics and result delivery
Core fields
| Field | Meaning |
|---|---|
schemaVersion | Output contract version |
status | Website-level execution outcome |
hasContacts | Whether at least one supported contact was found |
startUrl | Sanitized submitted URL associated with the result |
finalUrl | Legacy field containing the last successfully processed page |
contacts | Consolidated contact data |
crawl | Counters, timing, redirects, termination reason, and errors |
Use crawl.startPageFinalUrl for the redirected final URL of the original start page. finalUrl is retained for backward compatibility.
Status values
| Status | Meaning |
|---|---|
completed | At least one page succeeded and the crawl reached a normal configured end |
partial | Some pages succeeded, but execution ended before normal completion |
failed | No page was successfully processed |
Contact presence does not determine status.
{"status": "completed","hasContacts": false}
This means the website was processed successfully, but no supported contact was found.
Automation example
if (result.status === 'completed') {await saveResult(result);if (result.hasContacts) {await sendToCrm(result);}} else if (result.crawl.error?.retryable) {await queueForRetry(result.startUrl);} else {await queueForManualReview(result);}
Termination reasons and errors
crawl.terminationReason may contain:
crawl_completedearly_stoppage_limit_reachedglobal_request_limit_reachedstart_url_unreachablestart_url_blockedrequest_failuresrun_timed_outuser_abortedinternal_error
A failure may include:
{"code": "DNS_ERROR","message": "The URL hostname could not be resolved.","retryable": true,"url": "https://does-not-resolve.invalid/"}
Supported error codes include:
DNS_ERRORCONNECTION_ERRORTLS_ERRORHTTP_BLOCKEDHTTP_ERRORREQUEST_TIMEOUTMAX_RETRIES_EXCEEDEDRUN_TIMEOUTINTERNAL_ERROR
Error messages are sanitized before being written to the dataset.
Page counters
pagesVisited = pagesSucceededpagesAttempted = pagesSucceeded + pagesFailed
Retries and redirect hops do not inflate these counters.
Ordering, interruptions, and duplicates
Dataset rows follow completion order, not input order. Always match records through startUrl, not row position.
A normal completed run produces one result per valid submitted start URL.
Results already written remain available if a run is aborted or times out. Websites still incomplete may not produce a result.
Each start URL is emitted at most once within a single Actor process attempt. In rare cases, a platform restart or migration may produce a duplicate. Deduplicate by startUrl when downstream uniqueness is required.
Compatibility
The current output contract uses schemaVersion: 1.
crawl.stoppedReason is retained for backward compatibility. New integrations should use crawl.terminationReason.
✅ Contact extraction and validation
Emails
The Actor extracts emails from visible content and mailto: links, including links with query parameters or multiple recipients.
Candidates are syntax-checked, normalized, deduplicated, and filtered for malformed values, placeholder domains, asset filenames, and other common false positives.
The Actor does not perform SMTP verification, mailbox existence checks, deliverability verification, catch-all detection, or contact-owner identification.
Phone numbers
Phone candidates are parsed with libphonenumber-js.
When sufficient country context is available, valid numbers are normalized to E.164:
+14155552671
Equivalent local and international formats are deduplicated.
If the country cannot be determined, a plausible local number may remain in normalized local form without being considered fully country-validated.
WhatsApp numbers
The Actor detects supported WhatsApp links and returns the associated phone numbers:
{"whatsapps": ["+14155552671"]}
Social profiles
Supported platforms include LinkedIn, Facebook, Instagram, Twitter/X, YouTube, TikTok, and other recognized services.
URLs are canonicalized to normalize common variants, remove tracking parameters, and exclude share, login, and hashtag URLs.
⚠️ Limitations and FAQ
Does the Actor execute client-side JavaScript?
No. It uses lightweight HTML parsing with Cheerio.
This keeps bulk runs efficient, but contacts rendered only through client-side JavaScript may be missed.
Why did a completed website return no contacts?
status: "completed" with hasContacts: false means the website was processed successfully, but no supported contact was found.
Try increasing the page budget or depth, and check whether the contacts require JavaScript rendering.
Are failed records billed?
Yes, when the failed record is written to the dataset. A website that produces no dataset record does not create a result charge.
Can the Actor process large website lists?
Yes. It supports bounded concurrency and progressive output.
Throughput depends on crawl settings, website speed, blocking, proxies, and Actor resources. Test a representative sample before a very large run.
Can I use the Actor through an API?
Yes. Results are available through:
- Apify API;
- JavaScript and Python SDKs;
- webhooks and schedules;
- JSON, CSV, Excel, XML, and HTML exports.
🆘 Support
This Actor is actively maintained. Need help or want to customize it?
- 📬 Contact the developer
- 🐞 Report a bug (Apify login required)
- ✨ Request a feature