Domain Contact Scraper – Email, Phone & Social Extractor avatar

Domain Contact Scraper – Email, Phone & Social Extractor

Pricing

from $2.00 / 1,000 domains

Go to Apify Store
Domain Contact Scraper – Email, Phone & Social Extractor

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

Emiliano Mastragostino

Maintained by Community

Actor stats

5

Bookmarked

938

Total users

87

Monthly active users

7 days ago

Last modified

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?

DataOutput
EmailsFiltered, normalized, and deduplicated public email addresses
Phone numbersParsed numbers, normalized to E.164 when sufficient country context is available
WhatsApp numbersPhone numbers extracted from supported WhatsApp links
Social profilesCanonicalized LinkedIn, Facebook, Instagram, X, YouTube, TikTok, and other supported profiles
Website metadataSubmitted start URL and redirected start-page URL
Crawl diagnosticsStatus, 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, and impressum.

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 scraperDomain Contact Scraper
One result per pageOne consolidated result per submitted website URL
Raw HTML or textStructured contact fields
Manual extraction rulesBuilt-in contact discovery and validation
Duplicate candidatesNormalization and deduplication
Limited crawl contextStatus, counters, timing, and errors

⚙️ How it works

For every valid submitted start URL, the Actor:

  1. opens the website;
  2. identifies and prioritizes likely contact pages;
  3. crawls within the configured URL scope, depth, and page budget;
  4. extracts email, phone, WhatsApp, and social-profile candidates;
  5. validates, normalizes, and deduplicates the contacts;
  6. writes one consolidated result when the website finishes.

🚀 How to scrape website contacts

  1. Add one representative URL per website, preferably the homepage.
  2. Keep the defaults or adjust the crawl settings.
  3. Run the Actor.
  4. Review the consolidated records.
  5. 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

FieldRequiredDefaultDescription
startUrlsYesWebsite URLs processed independently
maxPagesPerStartUrlNo8Maximum pages per start URL
maxDepthNo1Maximum link depth
defaultPhoneCountryNoEmptyOptional fallback country for ambiguous local numbers
enqueueStrategyNosame-hostnameControls which links may be followed
allowedSubdomainsNoOptional subdomain allowlist for same-domain
earlyStopModeNooffControls adaptive early stopping
maxRequestsPerCrawlNoOptional global request limit
maxConcurrencyNoRuntime defaultMaximum concurrent requests
checkpointIntervalSecNoOptional checkpoint interval
proxyConfigurationOptionsNoProxy disabledApify 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

StrategyBehavior
same-hostnameSame hostname only
same-domainRegistrable domain and subdomains
same-originSame protocol, hostname, and port
allExternal 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:

  1. explicit international number;
  2. reliable website country context;
  3. defaultPhoneCountry;
  4. 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

GoalPagesDepthEarly stop
Faster bulk run41balanced
Standard enrichment81off
Broader exploration202off

These are starting points. Performance depends on website structure, response time, blocking, proxies, and Actor resources.


📦 Output semantics and result delivery

Core fields

FieldMeaning
schemaVersionOutput contract version
statusWebsite-level execution outcome
hasContactsWhether at least one supported contact was found
startUrlSanitized submitted URL associated with the result
finalUrlLegacy field containing the last successfully processed page
contactsConsolidated contact data
crawlCounters, 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

StatusMeaning
completedAt least one page succeeded and the crawl reached a normal configured end
partialSome pages succeeded, but execution ended before normal completion
failedNo 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_completed
  • early_stop
  • page_limit_reached
  • global_request_limit_reached
  • start_url_unreachable
  • start_url_blocked
  • request_failures
  • run_timed_out
  • user_aborted
  • internal_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_ERROR
  • CONNECTION_ERROR
  • TLS_ERROR
  • HTTP_BLOCKED
  • HTTP_ERROR
  • REQUEST_TIMEOUT
  • MAX_RETRIES_EXCEEDED
  • RUN_TIMEOUT
  • INTERNAL_ERROR

Error messages are sanitized before being written to the dataset.

Page counters

pagesVisited = pagesSucceeded
pagesAttempted = 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?