Website Contact Page Email & Phone Crawler
Crawl a website's contact, about and team pages and collect its public emails, phone numbers, and social media profile links in one normalized record per site.
Input schema
Input fields, types, required flags, descriptions, defaults, and prefills come from the published definition. Prefill examples are distinct from defaults.
| Field | Type | Required | Description |
|---|---|---|---|
urls | array | required | Websites to scan for public contact details. You can paste full URLs or bare domains; duplicates are removed. |
maxPagesPerSite | integer | optional | Maximum number of pages to fetch per website. Contact, about, team and imprint pages are prioritised. |
maxConcurrency | integer | optional | How many pages of the same website may be fetched in parallel. |
Show all input fields
| Field | Type | Required | Description |
|---|---|---|---|
urls | array | required | Websites to scan for public contact details. You can paste full URLs or bare domains; duplicates are removed. |
maxPagesPerSite | integer | optional | Maximum number of pages to fetch per website. Contact, about, team and imprint pages are prioritised. |
maxConcurrency | integer | optional | How many pages of the same website may be fetched in parallel. |
maxSitesConcurrency | integer | optional | How many websites are scanned in parallel. Lower this if you hit rate limits. |
requestTimeoutSecs | integer | optional | Maximum time to wait for one page before trying the next proxy tier. |
defaultCountry | string | optional | ISO 3166-1 alpha-2 country used to interpret phone numbers written without an international prefix. Leave empty to infer from the website domain. |
useProxy | boolean | optional | Start with the Apify proxy ladder instead of trying direct requests first. The actor always retries blocked requests through the proxy ladder automatically. |
smartPrioritise | boolean | optional | Try common contact, about, team, imprint and support paths first, and queue contact-like links ahead of other internal links. |
respectRobotsTxt | boolean | optional | Skip pages that the website's robots.txt disallows. Turn this off only when you own the site or have permission to crawl it. |
useSitemap | boolean | optional | Read sitemap.xml and robots.txt sitemap entries to find contact and about pages that are not linked from the homepage. |
renderFallback | boolean | optional | If plain HTML contains no email or phone, render up to two pages in a headless browser to capture contacts loaded by JavaScript. |
onlyWithContacts | boolean | optional | Drop records for websites where no email, phone, social profile or contact form was found. |
includeNamedEmails | boolean | optional | Include personal addresses such as jane.smith@company.com. When off, only role addresses such as info@, sales@ and support@ are kept. |
Download the published input schema
View complete JSON
{
"properties": {
"defaultCountry": {
"default": "",
"description": "ISO 3166-1 alpha-2 country used to interpret phone numbers written without an international prefix. Leave empty to infer from the website domain.",
"editor": "select",
"enum": [
"",
"AC",
"AD",
"AE",
"AF",
"AG",
"AI",
"AL",
"AM",
"AO",
"AR",
"AS",
"AT",
"AU",
"AW",
"AX",
"AZ",
"BA",
"BB",
"BD",
"BE",
"BF",
"BG",
"BH",
"BI",
"BJ",
"BL",
"BM",
"BN",
"BO",
"BQ",
"BR",
"BS",
"BT",
"BW",
"BY",
"BZ",
"CA",
"CC",
"CD",
"CF",
"CG",
"CH",
"CI",
"CK",
"CL",
"CM",
"CN",
"CO",
"CR",
"CU",
"CV",
"CW",
"CX",
"CY",
"CZ",
"DE",
"DJ",
"DK",
"DM",
"DO",
"DZ",
"EC",
"EE",
"EG",
"EH",
"ER",
"ES",
"ET",
"FI",
"FJ",
"FK",
"FM",
"FO",
"FR",
"GA",
"GB",
"GD",
"GE",
"GF",
"GG",
"GH",
"GI",
"GL",
"GM",
"GN",
"GP",
"GQ",
"GR",
"GT",
"GU",
"GW",
"GY",
"HK",
"HN",
"HR",
"HT",
"HU",
"ID",
"IE",
"IL",
"IM",
"IN",
"IO",
"IQ",
"IR",
"IS",
"IT",
"JE",
"JM",
"JO",
"JP",
"KE",
"KG",
"KH",
"KI",
"KM",
"KN",
"KP",
"KR",
"KW",
"KY",
"KZ",
"LA",
"LB",
"LC",
"LI",
"LK",
"LR",
"LS",
"LT",
"LU",
"LV",
"LY",
"MA",
"MC",
"MD",
"ME",
"MF",
"MG",
"MH",
"MK",
"ML",
"MM",
"MN",
"MO",
"MP",
"MQ",
"MR",
"MS",
"MT",
"MU",
"MV",
"MW",
"MX",
"MY",
"MZ",
"NA",
"NC",
"NE",
"NF",
"NG",
"NI",
"NL",
"NO",
"NP",
"NR",
"NU",
"NZ",
"OM",
"PA",
"PE",
"PF",
"PG",
"PH",
"PK",
"PL",
"PM",
"PR",
"PS",
"PT",
"PW",
"PY",
"QA",
"RE",
"RO",
"RS",
"RU",
"RW",
"SA",
"SB",
"SC",
"SD",
"SE",
"SG",
"SH",
"SI",
"SJ",
"SK",
"SL",
"SM",
"SN",
"SO",
"SR",
"SS",
"ST",
"SV",
"SX",
"SY",
"SZ",
"TA",
"TC",
"TD",
"TG",
"TH",
"TJ",
"TK",
"TL",
"TM",
"TN",
"TO",
"TR",
"TT",
"TV",
"TW",
"TZ",
"UA",
"UG",
"US",
"UY",
"UZ",
"VA",
"VC",
"VE",
"VG",
"VI",
"VN",
"VU",
"WF",
"WS",
"XK",
"YE",
"YT",
"ZA",
"ZM",
"ZW"
],
"enumTitles": [
"Auto-detect from domain",
"AC",
"Andorra",
"United Arab Emirates",
"Afghanistan",
"Antigua and Barbuda",
"Anguilla",
"Albania",
"Armenia",
"Angola",
"Argentina",
"American Samoa",
"Austria",
"Australia",
"Aruba",
"Åland Islands",
"Azerbaijan",
"Bosnia and Herzegovina",
"Barbados",
"Bangladesh",
"Belgium",
"Burkina Faso",
"Bulgaria",
"Bahrain",
"Burundi",
"Benin",
"Saint Barthélemy",
"Bermuda",
"Brunei Darussalam",
"Bolivia, Plurinational State of",
"Bonaire, Sint Eustatius and Saba",
"Brazil",
"Bahamas",
"Bhutan",
"Botswana",
"Belarus",
"Belize",
"Canada",
"Cocos (Keeling) Islands",
"Congo, The Democratic Republic of the",
"Central African Republic",
"Congo",
"Switzerland",
"Côte d'Ivoire",
"Cook Islands",
"Chile",
"Cameroon",
"China",
"Colombia",
"Costa Rica",
"Cuba",
"Cabo Verde",
"Curaçao",
"Christmas Island",
"Cyprus",
"Czechia",
"Germany",
"Djibouti",
"Denmark",
"Dominica",
"Dominican Republic",
"Algeria",
"Ecuador",
"Estonia",
"Egypt",
"Western Sahara",
"Eritrea",
"Spain",
"Ethiopia",
"Finland",
"Fiji",
"Falkland Islands (Malvinas)",
"Micronesia, Federated States of",
"Faroe Islands",
"France",
"Gabon",
"United Kingdom",
"Grenada",
"Georgia",
"French Guiana",
"Guernsey",
"Ghana",
"Gibraltar",
"Greenland",
"Gambia",
"Guinea",
"Guadeloupe",
"Equatorial Guinea",
"Greece",
"Guatemala",
"Guam",
"Guinea-Bissau",
"Guyana",
"Hong Kong",
"Honduras",
"Croatia",
"Haiti",
"Hungary",
"Indonesia",
"Ireland",
"Israel",
"Isle of Man",
"India",
"British Indian Ocean Territory",
"Iraq",
"Iran, Islamic Republic of",
"Iceland",
"Italy",
"Jersey",
"Jamaica",
"Jordan",
"Japan",
"Kenya",
"Kyrgyzstan",
"Cambodia",
"Kiribati",
"Comoros",
"Saint Kitts and Nevis",
"Korea, Democratic People's Republic of",
"Korea, Republic of",
"Kuwait",
"Cayman Islands",
"Kazakhstan",
"Lao People's Democratic Republic",
"Lebanon",
"Saint Lucia",
"Liechtenstein",
"Sri Lanka",
"Liberia",
"Lesotho",
"Lithuania",
"Luxembourg",
"Latvia",
"Libya",
"Morocco",
"Monaco",
"Moldova, Republic of",
"Montenegro",
"Saint Martin (French part)",
"Madagascar",
"Marshall Islands",
"North Macedonia",
"Mali",
"Myanmar",
"Mongolia",
"Macao",
"Northern Mariana Islands",
"Martinique",
"Mauritania",
"Montserrat",
"Malta",
"Mauritius",
"Maldives",
"Malawi",
"Mexico",
"Malaysia",
"Mozambique",
"Namibia",
"New Caledonia",
"Niger",
"Norfolk Island",
"Nigeria",
"Nicaragua",
"Netherlands",
"Norway",
"Nepal",
"Nauru",
"Niue",
"New Zealand",
"Oman",
"Panama",
"Peru",
"French Polynesia",
"Papua New Guinea",
"Philippines",
"Pakistan",
"Poland",
"Saint Pierre and Miquelon",
"Puerto Rico",
"Palestine, State of",
"Portugal",
"Palau",
"Paraguay",
"Qatar",
"Réunion",
"Romania",
"Serbia",
"Russian Federation",
"Rwanda",
"Saudi Arabia",
"Solomon Islands",
"Seychelles",
"Sudan",
"Sweden",
"Singapore",
"Saint Helena, Ascension and Tristan da Cunha",
"Slovenia",
"Svalbard and Jan Mayen",
"Slovakia",
"Sierra Leone",
"San Marino",
"Senegal",
"Somalia",
"Suriname",
"South Sudan",
"Sao Tome and Principe",
"El Salvador",
"Sint Maarten (Dutch part)",
"Syrian Arab Republic",
"Eswatini",
"TA",
"Turks and Caicos Islands",
"Chad",
"Togo",
"Thailand",
"Tajikistan",
"Tokelau",
"Timor-Leste",
"Turkmenistan",
"Tunisia",
"Tonga",
"Türkiye",
"Trinidad and Tobago",
"Tuvalu",
"Taiwan, Province of China",
"Tanzania, United Republic of",
"Ukraine",
"Uganda",
"United States",
"Uruguay",
"Uzbekistan",
"Holy See (Vatican City State)",
"Saint Vincent and the Grenadines",
"Venezuela, Bolivarian Republic of",
"Virgin Islands, British",
"Virgin Islands, U.S.",
"Viet Nam",
"Vanuatu",
"Wallis and Futuna",
"Samoa",
"XK",
"Yemen",
"Mayotte",
"South Africa",
"Zambia",
"Zimbabwe"
],
"prefill": "",
"title": "Default phone country",
"type": "string"
},
"includeNamedEmails": {
"default": true,
"description": "Include personal addresses such as jane.smith@company.com. When off, only role addresses such as info@, sales@ and support@ are kept.",
"prefill": true,
"title": "Include Personal Emails",
"type": "boolean"
},
"maxConcurrency": {
"default": 5,
"description": "How many pages of the same website may be fetched in parallel.",
"editor": "number",
"maximum": 20,
"minimum": 1,
"prefill": 5,
"title": "Page Concurrency Per Site",
"type": "integer"
},
"maxPagesPerSite": {
"default": 20,
"description": "Maximum number of pages to fetch per website. Contact, about, team and imprint pages are prioritised.",
"editor": "number",
"maximum": 200,
"minimum": 1,
"prefill": 5,
"title": "Max Pages Per Site",
"type": "integer"
},
"maxSitesConcurrency": {
"default": 3,
"description": "How many websites are scanned in parallel. Lower this if you hit rate limits.",
"editor": "number",
"maximum": 10,
"minimum": 1,
"prefill": 3,
"title": "Website Concurrency",
"type": "integer"
},
"onlyWithContacts": {
"default": false,
"description": "Drop records for websites where no email, phone, social profile or contact form was found.",
"prefill": false,
"title": "Only Output Sites With Contacts",
"type": "boolean"
},
"renderFallback": {
"default": true,
"description": "If plain HTML contains no email or phone, render up to two pages in a headless browser to capture contacts loaded by JavaScript.",
"prefill": true,
"title": "Render JavaScript Pages When Needed",
"type": "boolean"
},
"requestTimeoutSecs": {
"default": 15,
"description": "Maximum time to wait for one page before trying the next proxy tier.",
"editor": "number",
"maximum": 60,
"minimum": 5,
"prefill": 15,
"title": "Request Timeout (seconds)",
"type": "integer"
},
"respectRobotsTxt": {
"default": true,
"description": "Skip pages that the website's robots.txt disallows. Turn this off only when you own the site or have permission to crawl it.",
"prefill": true,
"title": "Respect robots.txt",
"type": "boolean"
},
"smartPrioritise": {
"default": true,
"description": "Try common contact, about, team, imprint and support paths first, and queue contact-like links ahead of other internal links.",
"prefill": true,
"title": "Smart Page Prioritisation",
"type": "boolean"
},
"urls": {
"description": "Websites to scan for public contact details. You can paste full URLs or bare domains; duplicates are removed.",
"editor": "stringList",
"prefill": [
"https://apify.com"
],
"title": "Website URLs",
"type": "array"
},
"useProxy": {
"default": false,
"description": "Start with the Apify proxy ladder instead of trying direct requests first. The actor always retries blocked requests through the proxy ladder automatically.",
"prefill": false,
"title": "Use Proxy First",
"type": "boolean"
},
"useSitemap": {
"default": true,
"description": "Read sitemap.xml and robots.txt sitemap entries to find contact and about pages that are not linked from the homepage.",
"prefill": true,
"title": "Discover Pages From Sitemap",
"type": "boolean"
}
},
"required": [
"urls"
],
"schemaVersion": 1,
"title": "Website Contact Finder Input",
"type": "object"
}
Output schema
Published result links and storage locations. This schema is not an exhaustive contract for every field in a dataset record.
| Field | Type | Required | Description |
|---|---|---|---|
contacts | string | optional | Dataset containing one contact record per scanned website. |
Download the published output schema
View complete JSON
{
"actorOutputSchemaVersion": 1,
"description": "One record per scanned website with source-backed emails, E.164 phone numbers, social profiles, company name, address, contact forms, crawl diagnostics and status.",
"properties": {
"contacts": {
"description": "Dataset containing one contact record per scanned website.",
"template": "{{links.apiDefaultDatasetUrl}}/items",
"title": "Website contact records",
"type": "string"
}
},
"title": "Website Contact Page Email & Phone Crawler Output"
}
Dataset schema
Published dataset views and any declared field definitions. A view can select only a subset of the returned record fields.
This definition is not present in the default published build. See the published README for available documentation.
Key-value schema
Published key-value store definitions, when provided by this build.
This definition is not present in the default published build. See the published README for available documentation.
Published README
Read the full published documentation
Website Contact Page Email & Phone Crawler
Turn a list of company websites into one clean contact record per site. The actor crawls the homepage plus contact, about, team, support and imprint-style pages, then extracts public emails, normalised phone numbers, social profiles, the company name, postal address and contact forms — with the page each value came from.
It starts with explicit mailto:/tel: links, then reads visible text, then JSON-LD and microdata, and finally decodes common obfuscation such as [at]/[dot], HTML entities, full-width characters and Cloudflare email protection. Inline script bundles are never scanned, so analytics keys and image filenames do not end up in your dataset.
What this actor does
- Crawls the registrable domain, so
www.example.com,example.comandshop.example.comare treated as one site - Prioritises contact, about, team, support, imprint and legal-notice pages in English, German, French, Spanish, Italian, Portuguese and Dutch
- Discovers unlinked contact pages from
robots.txtsitemap entries andsitemap.xml - Extracts emails from
mailto:links, visible text, JSON-LD, microdata, Cloudflaredata-cfemail//cdn-cgi/l/email-protection, and common obfuscated forms - Normalises phone numbers to E.164 with country and line type, using the website's country domain or your
defaultCountry - Suppresses dates, prices, VAT/company-registry numbers, opening hours, fax labels and placeholder numbers
- Keeps up to 19 social platforms, filtering share buttons, login pages and generic platform links
- Renders up to two pages in a headless browser only when plain HTML contains no email or phone, blocking images, fonts and media
- Omits empty fields instead of writing
null,"",[]or{}
Output per website
One dataset item per input website. Fields are omitted when empty.
| Field | Description |
|---|---|
type |
Always website_contacts. |
websiteUrl |
The normalised input URL. |
status |
success when an email, phone, social profile or contact form was found; no_contacts when pages loaded but none was found; failed when nothing could be fetched. |
failureReason |
On failed rows: invalid_url, blocked, no_pages_fetched or crawl_error. |
error |
Human-readable explanation on failed rows. |
companyName |
Organisation name from JSON-LD, og:site_name, the page title or microdata. |
companyNameSource |
jsonld, og_site_name, title or microdata. |
emails |
Sorted unique email addresses. |
primaryEmail |
Best role address on the site's own domain, preferring contact, info, hello, sales, support. |
roleEmails |
Addresses such as info@, sales@, support@. |
namedEmails |
Personal addresses such as jane.smith@. |
emailDetails |
Per address: email, type, sourceUrl, method, confidence. |
phones |
Sorted unique E.164 numbers. |
primaryPhone |
Best number for the site's country, preferring fixed lines over mobiles. |
phoneDetails |
Per number: phone, raw, country, type, sourceUrl, method, confidence. |
socialLinks |
Object keyed by platform with one profile URL per platform. |
socialProfiles |
Every profile: platform, url, sourceUrl, method. |
address |
Postal address from the site's own organisation JSON-LD/microdata: streetAddress, addressLocality, addressRegion, postalCode, addressCountry. |
contactPageUrl |
Highest-scoring contact-like page found. |
contactForms |
Up to five contact forms: actionUrl, method, fieldCount, sourceUrl. |
pagesCrawled |
Successfully fetched HTML pages. |
pagesCrawledUrls |
URLs of those pages. |
maxDepthReached |
Deepest link level reached from the start page. |
skippedByRobots |
URLs skipped because robots.txt disallowed them. |
browserUsed |
true when the JavaScript fallback rendered a page. |
crawledAt |
ISO 8601 UTC timestamp. |
emailDetails and phoneDetails carry provenance: the exact page and the extraction method (mailto, tel, visible_text, jsonld, microdata, cfemail, obfuscated) plus a confidence score from 0.75 to 0.98.
Input
| Field | Type | Default | Description |
|---|---|---|---|
urls |
array | – | Websites to scan. Required. Bare domains are accepted. |
maxPagesPerSite |
integer | 20 |
Pages fetched per site (1–200). |
maxConcurrency |
integer | 5 |
Pages fetched in parallel per site (1–20). |
maxSitesConcurrency |
integer | 3 |
Websites scanned in parallel (1–10). |
requestTimeoutSecs |
integer | 15 |
Per-page timeout in seconds (5–60). |
defaultCountry |
select | auto | ISO 3166-1 alpha-2 country used for phone numbers without an international prefix. Empty means infer from the domain ending. |
useProxy |
boolean | false |
Start with the Apify proxy ladder instead of direct requests. The actor retries blocked requests through the ladder automatically either way. |
smartPrioritise |
boolean | true |
Try common contact/about/team/imprint paths first. |
respectRobotsTxt |
boolean | true |
Skip pages disallowed by robots.txt. |
useSitemap |
boolean | true |
Discover contact pages from sitemaps. |
renderFallback |
boolean | true |
Render up to two pages in a headless browser when plain HTML has no email or phone. |
onlyWithContacts |
boolean | false |
Drop records with no email, phone, social profile or contact form. |
includeNamedEmails |
boolean | true |
Keep personal addresses; turn off to keep only role addresses. |
Example: scan a single company site
{
"urls": ["https://apify.com"],
"maxPagesPerSite": 10,
"defaultCountry": "US"
}
Example: several sites, only rows with contacts
{
"urls": ["company-a.com", "https://company-b.co.uk", "https://company-c.de"],
"maxPagesPerSite": 15,
"onlyWithContacts": true,
"includeNamedEmails": false
}
Example: a JavaScript-heavy site through the proxy ladder
{
"urls": ["https://example-spa.com"],
"maxPagesPerSite": 8,
"renderFallback": true,
"useProxy": true
}
Use cases
- Sales outreach — turn a list of target company websites into verified emails and phone numbers.
- Lead enrichment — fill missing email, phone, address or social links in an existing CRM export.
- Partnership scouting — find the right contact page and social presence before reaching out.
- Directory building — seed a business directory with the contact details each company publishes itself.
- Vendor research — compare how a set of suppliers presents contact channels.
FAQ
Do I need a proxy?
No. Direct requests work for most sites. When a site blocks the datacenter IP, the actor retries through the Apify proxy ladder automatically; useProxy only makes it start there.
Does this render JavaScript? Only when needed. If plain HTML has no email or phone, the actor renders up to two pages (usually the homepage and the best contact page) in a headless browser, with images, fonts and media blocked. Content that requires a login, a captcha solve, or several clicks is not reached.
Why are some emails excluded?
Placeholder domains (example.com, yourdomain.com), noreply-style addresses, image/font filename lookalikes, sentry/analytics addresses and addresses on asset CDNs are dropped. Inline script bundles are never scanned.
Why is a phone number missing?
A number must be a valid, dialable number with 7–15 digits. Dates, prices, VAT and registry numbers, opening hours, fax labels and repeated-digit placeholders are rejected. National-format numbers need either a country-code domain or defaultCountry; otherwise only international + numbers are accepted.
Why are some phone numbers not in the format shown on the site?
phones and phoneDetails[].phone are E.164 (+49374222388). The original text is kept in phoneDetails[].raw.
Why is socialLinks missing a platform?
Only profiles linked from crawled pages or declared in JSON-LD sameAs are included. Share buttons, intent links, login pages and generic platform pages are skipped.
Why did a site return fewer pages than maxPagesPerSite?
The crawl stops when it runs out of same-site links, reaches maxPagesPerSite, or hits robots.txt restrictions. Non-HTML responses and error pages are not counted.
Why can an output URL return 403?
A site that blocked the crawler may still be recovered through the browser fallback, while the same URL returns 403 to a clean HTTP client. Some social platforms return 400/403 to non-browser clients. These are anti-bot responses, not broken URLs; the record states how the page was reached with browserUsed/proxyUsed.
Is this affiliated with any social platform? No. This is an independent, third-party actor that reads publicly available website content.
Limitations
- Contact forms are detected, not submitted; the actor does not send messages.
- Emails shown only inside images or a logged-in area cannot be extracted. A 30-site OCR trial found zero true image-only contacts and produced false positives, so OCR is intentionally not shipped.
- Some sites serve a Cloudflare/Akamai/AWS WAF challenge to every datacenter and proxy IP; those rows are returned as
failedwithfailureReason: "blocked"rather than guessed. The browser fallback tries direct first, then AUTO, then RESIDENTIAL only after a real block. - The JavaScript fallback is capped at two pages per site; a 20-site trial showed a 2.4× runtime increase for almost no additional contacts, so this is a deliberate design limit.
- Pages larger than 5 MB are still downloaded in full before being skipped. Streaming the first 5 MB is a possible future optimization, not a shipped guarantee. Sitemap discovery is capped at three sitemaps, five sitemap-index children and twenty contact-like URLs;
.xml.gzsitemaps are decompressed. Emails carryownDomainanddomainType(own,free_provider,third_party) so legal-page regulator/partner addresses can be filtered. - A company with several branches may publish several addresses;
addressuses the organisation's own JSON-LD address and omits branchPlaceentries when an organisation address exists.
Python quickstart
Use a schema-derived input and the Python standard library to start a run. An Apify account and token are required for execution.
Source build Tlb3yWd5vt2YU7382. Documentation is regenerated from the default published build; private drafts are not included.