How to use it: 3 steps
- Make a free Apify accountApify is the cloud platform where our tools run. The free plan gives you $5 of usage every month, no credit card needed. You pay Apify, not us.
- Paste your input and press StartNo code needed: you fill in a form (the input). It already has a working example. For example:
{"url": "https://www.iana.org/"}in Websites. Each start is one run. - Download the resultsWhen the run ends, the results are in a table (the dataset). Open Output, press Export and pick Excel or CSV. JSON and the API are there too.
This tool is an Apify Actor: a small program that runs on Apify. You can also run it on a schedule, for example every Monday. New to it? Read how it works, step by step and the 8 words you need first.
What it does
Website Contact Scraper finds public emails, phone numbers, and social links on company websites. Add 1–100 website URLs or domains. Each result shows the source pages and whether the small scan finished or stopped early. A run costs $0.00065 to start, plus $0.00060 per chargeable website result. Contacts are not guaranteed.
- Public emails found in visible text,
mailto:links, supported page attributes, JSON-LD, and conservative(at)/(dot)obfuscation. - Phone numbers found in
tel:links and page text, normalized when the number can be represented in a canonical international format. - Organization-like social links for LinkedIn, X/Twitter, Facebook, Instagram, YouTube, TikTok, GitHub, Threads, Telegram, WhatsApp, and Pinterest when those profile URLs are declared by the website.
- Source provenance for each accepted email, phone, and social link, including the page URL and extraction method.
- Scan evidence such as attempted pages, HTTP status, bytes read, per-page extraction counts, elapsed time, and safe error codes.
- Finding and coverage fields that distinguish “contacts found” from “the configured scan finished completely.”
Who uses it
Sales and outreach teams
Add public company contacts to a domain list.
PR and partnership teams
Find press, sales or support inboxes fast.
Data teams
Enrich company records with source-backed contact data.
Price
Pay per event: you pay for each result the tool saves, plus a small start fee for each run. The price includes Apify platform costs. Apify bills you, not us. Prices below are for the Apify free plan.
| What you pay for | Price | When |
|---|---|---|
| Start fee | $0.00065 | Charged once per run. This tool uses 1 GB of memory or less by default; more memory costs more. |
| Website result | $0.0006 | One terminal result for a website after the public-page scan completes. |
Examples
- One website: about $0.0012.
- 1,000 websites: about $0.61 (10 runs of up to 100).
- Every day 100 websites, for 30 days: about $1.82 a month.
- 100 websites once a week (4 runs a month): about $0.24 a month.
Free to try: the Apify free plan gives you $5 of usage every month, no credit card needed. That is enough for about 8,200 websites.
Prices checked on 24 October 2026. See the current price on Apify.
Questions
Does it crawl the whole website?
No. It checks up to 10 pages per site, picked by rank.
Does it run JavaScript?
No. Contacts that appear only after JavaScript can be missed.
Will I always find an email?
No. Some sites publish no contacts on the pages it checks.
Which social links does it find?
Company profiles on LinkedIn, X, Facebook, Instagram, YouTube, TikTok, GitHub and more.
Can I enter domains without https://?
Yes. A bare public domain such as iana.org is normalized to HTTPS first. The Actor can fall back to HTTP for that bare-domain case when the HTTPS fetch fails and the fallback remains safe and allowed.
Does it verify email addresses?
No. An email means the address was observed on a public page through a supported extraction method. It does not mean that the mailbox exists or accepts mail.
Why can CONTACTS_FOUND also be PARTIAL?
Because a finding and scan coverage are different facts. The Actor can find a valid public email on the first pages and then stop at a page/time/source limit. The contact remains useful, while scanCompleteness=PARTIAL avoids claiming the configured scan finished completely.
What happens when no contact is found?
If all discovered pages inside the configured bounds are checked successfully, the row is NO_CONTACTS_FOUND. If a limit or page/source failure stopped more work, the row uses partial/limit semantics instead of presenting an empty result as complete.
Can I export results?
Yes. Apify Dataset output can be consumed through the Console, API, or standard Dataset export formats. The Actor also exposes a direct output link to the default Dataset items endpoint.
Does it respect robots.txt?
By default, yes. respectRobotsTxt=true checks the selected pages against the site’s robots policy. You can disable that option, but network-safety controls remain active regardless.
Details
Step by step on Apify
- Open the Actor's Input tab. The new form suggests IANA, a small public site with contact details.
- Keep that URL for your first run. No login or source credentials are needed.
- Keep the other input settings, then click Start.
- Open Output and the default Dataset. Check
contacts, theirsources,status, andscanCompleteness.
Use cases
- Add public website contact fields to a company or domain list.
- Find a company’s published support, sales, press, or general email inboxes.
- Collect public phones and declared social profiles with source-page evidence.
- Audit where a previously collected public contact appears on a company site.
- Prepare structured website-contact data for CRM, research, or internal enrichment workflows that need explicit empty/partial/error semantics.
Tips for better results
- You can enter bare domains like example.com. The tool adds https.
- Raise pages per website to 10 for sites with deep contact pages.
- Check emails with a verifier before you send. Found does not mean working.
Limits and good to know
The Actor uses direct HTTP and deliberately avoids browser automation. That keeps the scan bounded and inexpensive, but JavaScript-only contacts may not be visible. Login walls, CAPTCHAs, anti-bot challenges, rate limits, and server blocks can produce blocked or partial results.
- maximum 100 submitted websites per run
- maximum 10 selected pages per website
- maximum 10 websites processed concurrently
- per-website internal deadline of 10–90 seconds
- configured per-page body cap up to 4 MiB and an additional bounded per-site byte budget
- same-site navigation only, with safe redirects and DNS revalidation
- no form submission, login, CAPTCHA bypass, arbitrary path guessing, or social-profile fetching
- no claim of whole-site completeness beyond the pages actually checked
Troubleshooting
| Symptom | What to check | Safe action |
|---|---|---|
| Empty Dataset or failed run | RUN_SUMMARY, input list, resource error, and run cost cap | Provide 1–100 URL objects, use 512 MB, and allow the start plus result budget. |
NO_CONTACTS_FOUND | The checked page URLs and scan completeness | Open the source site; it may publish contacts elsewhere or only through JavaScript. |
CONTACTS_FOUND with PARTIAL, or LIMIT_REACHED | stopReason, page count, size, and elapsed time | Keep the useful values. Raise a permitted limit only if more checking is needed. |
BLOCKED | Robots policy, challenge, login, or rate-limit error | Respect the restriction. This Actor does not bypass it. |
UNREACHABLE | DNS, TLS, availability, or unsafe destination | Correct the public URL. Retry a transient failure once; repeated runs cost again. |
| Missing or unclear phone | displayValue, confidence, and source | Read the source; the Actor does not guess a country or verify the number. |
Recent updates
Update · 9 October 2026
- Include the platform start event in the maximum total-charge preflight. Insufficient total caps fail before website access; a platform start can still be charged.
- Classify invalid platform resources separately and preserve normalized input accounting.
- Refresh vulnerable transitive dependencies and pin patched
basic-ftp6.2.3. Website input still only permits HTTP/HTTPS; FTP/PAC is not a customer transport.
For developers
Sample result
One result from a real test run, in JSON. Real values change from run to run. In Excel or CSV each field is a column.
{
"websiteUrl": "https://iana.org/",
"status": "CONTACTS_FOUND",
"scanCompleteness": "PARTIAL",
"pagesScanned": 5,
"pagesLimit": 5,
"stopReason": "PAGE_LIMIT",
"contacts": {
"emails": [
{
"value": "[email protected]",
"sources": [
{
"pageUrl": "https://iana.org/contact",
"method": "mailto"
}
]
}
],
"phones": [
{
"value": "+14242545300",
"sources": [
{
"pageUrl": "https://iana.org/contact",
"method": "text"
}
]
}
]
},
"socialLinks": [],
"runSummaryKey": "RUN_SUMMARY"
}Use it from code
Actor ID: cliqtomedia/website-contacts-scraper. You need your Apify API token from Apify Console → Settings → API & Integrations.
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("cliqtomedia/website-contacts-scraper").call(run_input={
"startUrls": [
{
"url": "https://www.iana.org/"
}
],
"maxPagesPerSite": 5
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item)curl -X POST "https://api.apify.com/v2/acts/cliqtomedia~website-contacts-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"startUrls": [{"url": "https://www.iana.org/"}], "maxPagesPerSite": 5}'# pip install langchain-apify
# set APIFY_TOKEN in your environment
from langchain_apify import ApifyActorsTool
tool = ApifyActorsTool("cliqtomedia/website-contacts-scraper")
result = tool.invoke({"run_input": {"startUrls": [{"url": "https://www.iana.org/"}], "maxPagesPerSite": 5}})
print(result)The cURL call waits for the results, up to 300 seconds. For longer runs, start a run (/runs) and get the results with a webhook or by checking the run. Apify API docs. LlamaIndex, MCP for AI agents and the JavaScript client: AI agents & MCP.
Data fields (16)
| Field | Type | Meaning |
|---|---|---|
siteId | string | Stable opaque ID derived from the normalized site identity. |
inputIndexes | array | Zero-based input positions represented by this website row. |
status | string | Finding outcome. CONTACTS_FOUND can still have PARTIAL scanCompleteness when a page cap, timeout, source block or extraction limit stopped further checking. LIMIT_REACHED is used when no useful contact or social result was found before a configured limit. |
scanCompleteness | string | COMPLETE, PARTIAL, or NOT_STARTED. This is separate from the finding status and never claims whole-site completeness. |
pagesScanned | integer | Number of public HTML pages read successfully for this website. |
pagesLimit | integer | Configured maximum number of pages for this website, including the home page. |
stopReason | string | Why the bounded scan ended: COMPLETED, PAGE_LIMIT, SITE_TIMEOUT, RESPONSE_SIZE_LIMIT, SOURCE_BLOCKED, PAGE_ERROR, PARTIAL_EXTRACTION, NO_CONTACTS_FOUND, UNREACHABLE, BLOCKED, INVALID_INPUT, or NO_PUBLIC_HTML. |
statusInfo | string | Plain-language explanation of the finding, scan coverage and chargeability. |
websiteUrl | string | Sanitized canonical origin used for this website. |
finalUrl | string | Safe final URL after redirects, or empty when no usable response exists. |
contacts | object | Objects for observed public emails and phones, with normalized values, confidence, source pages and extraction methods. |
socialLinks | array | Profile-like links declared by first-party pages. The Actor does not fetch destinations and does not verify ownership; each row includes relationship=first_party_declared_unverified. |
pages | array | Attempted page evidence, response status, bounded bytes, extraction counts and whether the safe parser window was reached. |
metrics | object | Bounded request/page/byte/retry and elapsed-time metrics for this website. |
error | object | Stable safe error code and retryability, or null for a complete result. |
runSummaryKey | string | Key of the sanitized KVS run summary; normally RUN_SUMMARY. |
Input settings (6)
The main fields of the input form. With the API you send them as JSON.
| Setting | Name in the API | Type | Example value |
|---|---|---|---|
| Websites required | startUrls | array | [{"url": "https://www.iana.org/"}] |
| Pages per website | maxPagesPerSite | integer | 5 |
| Parallel websites | maxConcurrency | integer | 5 |
| Website time limit (seconds) | siteTimeoutSeconds | integer | 30 |
| Maximum page size (bytes) | maxBytesPerPage | integer | 2097152 |
| Respect robots.txt | respectRobotsTxt | boolean | true |
Use cases with this tool
Each guide shows all the steps and the other tools.