LinkedIn Email Finder | Profile URL to Work Email, No Login
Pricing
from $1.50 / 1,000 email founds
LinkedIn Email Finder | Profile URL to Work Email, No Login
Find work emails from LinkedIn profile URLs, or from a name plus a company or domain. One address per person with a status, a confidence score and its evidence: verified, published on the company site, matching its staff pattern, or a labelled guess. No login, no cookies.
Pricing
from $1.50 / 1,000 email founds
Rating
0.0
(0)
Developer
The Mine Works
Maintained by CommunityActor stats
1
Bookmarked
36
Total users
18
Monthly active users
a day ago
Last modified
Categories
Share
From The Mine Works, makers of Threads Scraper and B2B Leads Finder, with over 140,000 runs across 170+ public actors.
Why choose this actor?
- Every email says how sure it is, and why. A recorded run took 3 people and returned 3 work emails in 10 seconds: one address printed on the company's own website (
published, confidence 90) and two labelled best guesses (best_guess, confidence 40). Each row carries its status, a 0 to 100 confidence, the evidence and the page it came from. - Start from whatever you have. A name with a domain, a name with a company name, a public LinkedIn profile URL, a CRM export row or a pasted CSV. Up to 5,000 people per run, duplicates dropped before any work, and no LinkedIn login or cookies.
- Rows without an email are never charged. $1.50 to $2.00 per 1,000 emails depending on your Apify plan. Guesses cost the same as evidence but are always labelled. Rejected mailboxes, domains with no mail servers, personal domains such as gmail.com, unconfirmed domains, names that could not be split reliably and duplicate rows are free.
Part of The Mine Works LinkedIn family: LinkedIn Company Scraper, LinkedIn Post Scraper, LinkedIn Employees Scraper, LinkedIn Profile Scraper, LinkedIn Newsletter Scraper, LinkedIn Candidate Finder.
Try it in one minute
Paste this into the input's JSON tab and start the run. It is the input of our recorded run: three people, three rows, about 10 seconds.
{"people": [{ "fullName": "Jason Fried", "domain": "basecamp.com" },{ "fullName": "Tobi Lutke", "companyName": "Shopify" }],"linkedinUrls": ["https://www.linkedin.com/in/satyanadella"],"maxPeople": 1000}
Each person can be given in any of these ways:
- an object in
peoplewithfullName(orfirstNameandlastName) plusdomainorcompanyName; - a CRM export row in
people, such as{"First Name": "Jane", "Last Name": "Doe", "Company": "Acme Inc", "Website": "https://acme.com"}; column names are matched loosely, and a LinkedIn URL in the wrong column is still found; - a line of text in
people:"Jane Doe, acme.com"or"Jane Doe @ Acme Inc"; - a public profile URL in
linkedinUrls(or as a line inpeople), with any country subdomain, trailing slash or tracking parameters; - a pasted CSV with a header row in
csv, comma, semicolon or tab separated.
domainOverride fills in one domain for every row that has none. A domain always beats a company name.
Apify's free plan includes $5 of credit every month, which covers about 2,450 emails at this actor's price (the Free plan rate of $0.002 per email, plus the $0.01 start fee on each of 10 runs at the default memory). From 13 Oct 2026, when the start fee becomes a flat $0.005, the same credit covers about 2,475.
Copy to your AI assistant
themineworks/linkedin-email-finder on Apify. Finds the most likely work email for a person from a name plus company domain, a name plus company name, a public LinkedIn profile URL, a CRM row or a pasted CSV, and labels each result with a status, a 0 to 100 confidence and the evidence (address published on the company site, the site's email pattern, a mail server check, or a labelled guess), with no LinkedIn login. Call ApifyClient("TOKEN").actor("themineworks/linkedin-email-finder").call(run_input={...}), then client.dataset(run["defaultDatasetId"]).list_items().items. Required: at least one of people (objects like {"fullName": "Jane Doe", "domain": "acme.com"} or {"fullName": "Jane Doe", "companyName": "Acme Inc"}, CRM rows, or strings "Jane Doe, acme.com"), linkedinUrls (public profile URLs) or csv (text with a header row). Optional: domainOverride, findPatternOnWebsite (default true), checkSmtp (default true), maxPeople (default 1000, up to 5000), monitorMode (default false; true delivers and charges only people new or changed since an earlier run with the same input). Each person row has status, email, confidence, evidence, reason, alternates and billable; statuses verified, published and pattern_match have evidence, best_guess and catch_all are charged guesses, and rejected, no_mx, domain_not_found, company_unknown, not_a_company_domain, invalid_input and error rows are free. One row with _type "summary" and one with _type "info" close each run and are never charged. Full spec: GET https://api.apify.com/v2/acts/themineworks~linkedin-email-finder/builds/default (Bearer TOKEN), which returns inputSchema and readme. Token: https://console.apify.com/account/integrations?fpr=ymnoit&utm_source=apify-readme&utm_medium=referral
Key features
- Five steps per person, evidence first. Read the input; read the public LinkedIn profile when you give a URL (name and current employer, then the employer's LinkedIn page for its website); find the company domain; look for evidence on the company's own website; ask the mail server whether the mailbox exists, where outbound port 25 is open.
- Evidence from the company's own website. Up to 5 pages per company (home, then contact, team, press, imprint and similar pages),
robots.txthonoured, each site read once per run however many people work there. If the site prints this person's address, that is the answer. If it prints other staff addresses, their shared pattern (jane.doe,jdoe,jane) is used. - One address plus four alternates. Each row has the most likely address and, in
alternates, the next four candidates with their pattern names, at no extra cost. - 12 statuses, 5 of them charged. Every row says exactly what happened, from
verified(the mail server accepted this mailbox) down toinvalid_input(no usable name). See the table below. - Fast enough for CRM batches. Four people are looked up at a time. Rows come back in input order, each with
inputIndexpointing at its place in your input. - Stops wasting time on blocks. If LinkedIn refuses several profile pages in a row, the remaining URLs fall back to the name in the URL. If the mail server port is closed, the check is switched off for the rest of the run within seconds.
How to use it
Basic: name and domain
{"people": [{ "fullName": "Jason Fried", "domain": "basecamp.com" }]}
The fastest and most accurate input. In our recorded run this person came back published with confidence 90: Basecamp prints the address on its homepage.
Several people from a CRM export
{"people": [{ "First Name": "Jane", "Last Name": "Doe", "Company": "Acme Inc", "Website": "https://acme.com" },{ "First Name": "Sam", "Last Name": "Lee", "Company": "Example Ltd", "Website": "example.co.uk" }],"maxPeople": 5000}
Export your contacts as JSON with their own column names and paste them in. Names like "First Name", "Last Name", "Company", "Website" and "LinkedIn URL" are recognised however they are spelled or capitalised. Or paste the same export as text into csv.
LinkedIn profile URLs from a prospect list
{"linkedinUrls": ["https://www.linkedin.com/in/satyanadella","https://www.linkedin.com/in/patrickcollison"]}
Each profile is read logged out for the name and current employer, then the employer's LinkedIn page for its website. If everyone on the list works at one company, add domainOverride (for example "acme.com") to skip the company lookup, and when a row already has a name and a domain the profile is not read at all, which is faster. LinkedIn Employees Scraper finds public profile URLs at a company by job title.
Keep only addresses with evidence
Run the list as usual, then filter the dataset on confidence of 70 or more, or drop rows whose evidence is pattern_prior. What is left was published on the company's site, follows the site's own pattern or passed a mail server check. Guesses are still charged, so for large lists of companies that publish no staff addresses, run a small sample first and look at the share of guesses.
Pipeline enrichment as new leads arrive
This is usually a one off step on a fresh batch of names. Call it from n8n, Make, Zapier or the API when new rows land in your CRM, with maxPeople set to the batch size. To run it on a timer instead, save the input as a task and add a schedule in Apify Console (Schedules, Create new). With monitorMode on, each scheduled run delivers and charges only people who are new or whose email, company or domain changed (see Run it on a schedule).
Input parameters
| Parameter | Type | Default | What it does |
|---|---|---|---|
people | array | none (prefilled with two examples) | One entry per person: an object (fullName or firstName and lastName, plus domain or companyName, optionally linkedinUrl), a CRM export row, or a line such as "Jane Doe, acme.com" or "Jane Doe @ Acme Inc". |
linkedinUrls | array of strings | none (prefilled with one URL) | Public LinkedIn profile URLs. Read logged out for the name and current company. |
csv | string | none | A pasted CSV with a header row. Comma, semicolon or tab separated. |
domainOverride | string | none | Domain used for every row that has none. |
findPatternOnWebsite | boolean | true | Read up to 5 pages of the company's site for this person's address or the pattern of its published personal addresses. Turning it off leaves mostly guesses. |
checkSmtp | boolean | true | Ask the company's mail server whether the mailbox exists, without sending anything. Switched off automatically when port 25 is closed. |
maxPeople | integer | 1000 | Most people processed, 1 to 5,000. Extra rows are skipped and never charged. |
monitorMode | boolean | false | Deliver only people who are new or whose email, company or domain changed since an earlier run with the same input. Unchanged people are not charged. Made for monthly schedules. |
How sure is each email?
| Status | Confidence | What it means | Charged? |
|---|---|---|---|
verified | 95 | The mail server accepted this exact mailbox and rejected a random one | Yes |
published | 90 | This exact address is printed on the company's own website | Yes |
pattern_match | 70 or 80 | Personal addresses on the company's website use this pattern (80 when two or more agree) | Yes |
best_guess | about 40 | No evidence for this company; the most common pattern, first.last | Yes, labelled a guess |
catch_all | about 40 | The server accepts any address, so nothing could be checked, and the site gave no pattern | Yes, labelled a guess |
rejected | 5 | The server said none of the candidates exist (often: the person has left) | No |
no_mx | 0 | The domain has no mail servers | No |
domain_not_found | 0 | No website with mail servers could be found for the company name | No |
company_unknown | 0 | No company in the row and none on the LinkedIn profile | No |
not_a_company_domain | 0 | gmail.com, outlook.com and similar personal mail providers | No |
invalid_input | 0 | No usable name | No |
error | 0 | Something unexpected failed on this row | No |
A row with an email is still not charged when its domain was only guessed from the company name and could not be confirmed on the company's homepage (its confidence is then capped at 40), or when the name had to be read from a LinkedIn URL that could not be split reliably (for example williamhgates). billable: true marks every charged row. billable is false on uncharged rows that reached the final check, and missing on rows that stopped early (not_a_company_domain, company_unknown, invalid_input, domain_not_found).
What data do you get?
One row per person, in input order, then a summary row and an info row.
The person: inputIndex (position in your input), fullName, firstName, lastName, nameSource (input, linkedin_profile, or linkedin_url_slug when the name had to be read from the URL), and for LinkedIn rows linkedinUrl, linkedinStatus (read, blocked, not_found or skipped) and headline.
The company: company, domain, domainSource (input, domain_override, linkedin_company_page, company_name_lookup or company_name_guess), mxProvider (for example google_workspace, microsoft_365 or other).
The answer: email, pattern (such as first.last), status, confidence, evidence (published_on_company_site, company_site_pattern, smtp_accepted, smtp_rejected or pattern_prior), reason (one plain sentence), alternates (four more addresses, each with its pattern), verified and billable.
What the website and mail server showed: evidenceUrl (the page the evidence came from), sitePattern (the pattern the site's personal addresses use), sitePersonalAddresses (how many it found), smtpCheck (unavailable when port 25 is closed, skipped when you turned the check off).
When: scrapedAt, the ISO time the row was written.
Summary row (_type: "summary", never charged): received, duplicates, over_cap, processed, charged_for, by_status (a count per status), linkedin_fetches, linkedin_blocked, company_pages, sites_read, site_pages, smtp_sessions, smtp_rcpts, smtp_available, linkedin_stopped and scrapedAt, plus note: "no_valid_rows" when the input held nobody to look up.
Info row (_type: "info", never charged): written when at least one email was charged, with the count and a scheduling tip. With monitor mode on, person rows also carry monitor_status (new or changed), and a last info row gives the monitor counts.
Stable fields for automations
The first five fields were present in every person row of our test runs, whatever the status. The other eight were present in every row that carried an email. Their names will not change, so a Google Sheet, Zapier zap or n8n flow can map them once.
| Field | What it is |
|---|---|
inputIndex | Position of the person in your input, the key to join back on |
status | One of the 12 statuses above |
confidence | 0 to 100 |
reason | One sentence on how the answer was reached |
scrapedAt | When this row was written, ISO 8601 |
email | The most likely work address |
pattern | Its pattern, such as first.last or first |
evidence | What the answer rests on |
alternates | Next four addresses, each { "email", "pattern" } |
domain | The company domain used |
domainSource | Where that domain came from |
verified | True only for verified rows |
billable | True when the row was charged |
Output examples
An address printed on the company's website (recorded run XG8G6WMekAvbzPtvs, 28 Sep 2026; charged):
{"inputIndex": 0,"nameSource": "input","fullName": "Jason Fried","firstName": "jason","lastName": "fried","domain": "basecamp.com","domainSource": "input","mxProvider": "other","sitePattern": "first","evidenceUrl": "https://basecamp.com/","sitePersonalAddresses": 1,"smtpCheck": "unavailable","status": "published","email": "jason@basecamp.com","pattern": "first","confidence": 90,"evidence": "published_on_company_site","reason": "This exact address is published on the company website.","alternates": [{ "email": "jason.fried@basecamp.com", "pattern": "first.last" },{ "email": "jfried@basecamp.com", "pattern": "flast" },{ "email": "jasonfried@basecamp.com", "pattern": "firstlast" },{ "email": "j.fried@basecamp.com", "pattern": "f.last" }],"verified": false,"billable": true,"scrapedAt": "2026-09-28T12:47:52.378Z"}
A LinkedIn URL with no evidence on the company site (same run; charged and labelled a guess):
{"inputIndex": 2,"linkedinUrl": "https://www.linkedin.com/in/satyanadella","nameSource": "linkedin_profile","fullName": "Satya Nadella","linkedinStatus": "read","headline": "Chairman and CEO","firstName": "satya","lastName": "nadella","company": "Microsoft","domain": "microsoft.com","domainSource": "linkedin_company_page","mxProvider": "microsoft_365","smtpCheck": "unavailable","status": "best_guess","email": "satya.nadella@microsoft.com","pattern": "first.last","confidence": 40,"evidence": "pattern_prior","reason": "No evidence for this company yet. This is the most common work email pattern.","alternates": [{ "email": "satya@microsoft.com", "pattern": "first" },{ "email": "snadella@microsoft.com", "pattern": "flast" },{ "email": "satyanadella@microsoft.com", "pattern": "firstlast" },{ "email": "s.nadella@microsoft.com", "pattern": "f.last" }],"verified": false,"billable": true,"scrapedAt": "2026-09-28T12:47:52.826Z"}
The third person in that run, Tobi Lutke at "Shopify", was matched to shopify.com through the company name (domainSource: "company_name_lookup") and also came back best_guess.
A row that cannot produce a work email (test run on our machine, 1 Oct 2026; never charged, and billable is missing because the row stopped early):
{"inputIndex": 0,"nameSource": "input","fullName": "Jane Nobody","firstName": "jane","lastName": "nobody","domain": "gmail.com","domainSource": "input","status": "not_a_company_domain","confidence": 0,"reason": "gmail.com is a personal mailbox provider, not a company domain.","scrapedAt": "2026-10-01T13:45:41.448Z"}
The summary row of the recorded run (trimmed):
{"_type": "summary","received": 3,"duplicates": 0,"over_cap": 0,"processed": 3,"charged_for": 3,"by_status": { "published": 1, "best_guess": 2 },"linkedin_fetches": 1,"linkedin_blocked": 0,"company_pages": 1,"sites_read": 3,"site_pages": 4,"smtp_sessions": 3,"smtp_rcpts": 0,"smtp_available": false,"linkedin_stopped": false,"scrapedAt": "2026-09-28T12:47:52.920Z"}
smtp_available: false is what every run of ours on Apify has shown: outbound port 25 is closed there, so the mail server statuses (verified, rejected and catch_all) do not come up on Apify runs today. See "Does it check the mail server?" below.
Pricing
Pay per event. You are charged for each email returned on a charged row, plus a start fee per run. The rate falls as your Apify plan rises. The start fee changes on 13 Oct 2026.
| Event | Free | Bronze | Silver | Gold and above |
|---|---|---|---|---|
Email returned (email-found), per email | $0.002 | $0.002 | $0.0018 | $0.0015 |
| Per 1,000 emails | $2.00 | $2.00 | $1.80 | $1.50 |
Run start until 12 Oct 2026 (apify-actor-start) | $0.01 per GB of run memory, minimum one event | same | same | same |
Run start from 13 Oct 2026 (run-start) | $0.005 flat per run | same | same | same |
The start fee, exactly. Until 12 Oct 2026, Apify's apify-actor-start event is charged once when every run starts, one event per GB of memory with a minimum of one: $0.01 at the default 512 MB and at 1 GB, $0.04 at 4 GB. It applies to every run, including one with nothing to look up. From 13 Oct 2026 (00:00 UTC) it is replaced by our own run-start event: a flat $0.005 per run whatever memory you choose, charged once the input holds at least one row to look up. A run whose input holds no rows at all then costs nothing. The per email prices do not change.
Never charged: rows with the statuses rejected, no_mx, domain_not_found, company_unknown, not_a_company_domain, invalid_input and error; rows whose domain was guessed from a company name and not confirmed; rows whose name was read from a LinkedIn URL that could not be split reliably; duplicate rows and rows over maxPeople (skipped before any work); the summary and info rows.
Guesses cost the same as evidence. best_guess and catch_all rows are charged at the same price, always labelled. To keep only addresses with evidence, filter on confidence of 70 or more.
What real jobs cost on Gold:
| Run | Rows with an email | Until 12 Oct | From 13 Oct |
|---|---|---|---|
| The recorded run: 3 people | 3 | $0.0045 + $0.01 = $0.0145 | $0.0045 + $0.005 = $0.0095 |
| 500 people, 450 with an email | 450 | $0.675 + $0.01 = $0.685 | $0.675 + $0.005 = $0.68 |
| 500 people, no email found | 0 | $0.01 | $0.005 |
The Pricing tab on this page always shows the rate for your own plan.
Run it on a schedule
Switch on monitorMode to keep a lead list current. The first run delivers every person. After that, a person comes back only when the work email found for them, their company or their domain changes, which is what a job change looks like on a list of LinkedIn profile URLs. You pay only for the emails delivered.
- Fill in the input, tick Monitor mode, and save it as a task.
- In Apify Console open Schedules, click Add schedule, and pick Monthly (or a cron such as
0 7 1 * *). - Add the saved task to the schedule and click Save.
{"linkedinUrls": ["https://www.linkedin.com/in/satyanadella/"],"monitorMode": true}
Each delivered row carries monitor_status: new the first time, changed when the email, company or domain differs from the version you last received. A person with no change is looked up but not delivered and not charged; the flat run fee applies to every run as usual. A change in status or confidence alone does not count. A run that finds no email for someone already delivered, or cannot read their LinkedIn profile, never sends that as a change. The last row of each run (_type: "info") gives new_this_run, changed_this_run and skipped_already_seen. The history belongs to the input: change the people, URLs, CSV or domainOverride and a new history starts; change maxPeople, checkSmtp or findPatternOnWebsite and it carries on.
FAQ
What does this actor do that a pattern guesser does not? A pattern guesser hands you a list of candidates. This actor looks for evidence first (the company's own website, then the mail server where it can be reached), returns one address per person, says which kind of evidence it found or that it is a guess, and lists four alternates at no extra cost.
Does it check the mail server?
It tries. Asking a mail server whether a mailbox exists needs outbound port 25, and many cloud hosts block it, including the Apify servers this actor runs on in all our tests. The run notices within seconds, switches the check off for the rest of the run, reports smtpCheck: "unavailable" on each row and smtp_available: false in the summary, and relies on website evidence instead. Run it on a machine where port 25 is open and verified rows can appear. Even then, many large companies accept every address (catch all), so no single mailbox can be confirmed there; those rows say catch_all.
Why did most of my rows come back best_guess?
Because the companies on your list do not publish staff addresses on their websites, and the mail server could not be asked. Those rows are charged and labelled, so you can drop them or check them with an email verifier first. Small firms, agencies, media, law firms and German companies (imprint pages) often publish staff addresses; large tech companies usually publish only role addresses such as press@ or sales@, which are not used as evidence.
How accurate is a best guess?
A best guess is the most common work email pattern, first.last, and it is still wrong more often than right: many companies use first, flast or something else, and founders at tech companies often use just their first name (jason@basecamp.com in the example above). That is why its confidence is about 40 and why alternates lists the next four patterns.
How does the LinkedIn URL input work?
The public profile page is read the way a logged out visitor or a search engine sees it, over Apify's datacenter proxy, with no login and no cookies. From it the actor takes the name, the headline and the current employer, then the employer's public LinkedIn page for its website. Some profiles are hidden from logged out visitors: the name is then read from the URL (nameSource: "linkedin_url_slug"), and without a company or domain in your row the result is company_unknown, so add domainOverride or a domain column. Some members show only a last initial, so only first and firstl style addresses can be built. Masked employer names are skipped.
Do I need a LinkedIn account, cookies or an API key? No. The actor never logs in and never uses cookies. It reads only what LinkedIn shows to logged out visitors and what companies publish on their own websites.
How many people can I look up?
Up to 5,000 per run (maxPeople, default 1,000). Four people are processed at a time and each company website is read once per run, however many of its people are on your list.
Does it use a database of known emails? No. Every answer comes from public pages read during your run, the mail server where reachable, or the pattern prior. It never returns personal addresses at gmail.com and similar providers.
What happens with duplicates and bad rows?
Duplicates (same LinkedIn URL, name and domain or company) are dropped before any work and counted in the summary's duplicates. Rows with no usable name come back invalid_input. Neither is charged.
How do I get the data out?
From the run's Storage tab as JSON, CSV, Excel, XML or HTML, or through the Apify API. CSV and Excel flatten alternates into numbered columns.
Can I run it from the API in one call? Yes, with Apify's run sync endpoint, which waits for the run and returns the rows:
curl -X POST "https://api.apify.com/v2/acts/themineworks~linkedin-email-finder/run-sync-get-dataset-items" \-H "Authorization: Bearer <YOUR_APIFY_TOKEN>" \-H "Content-Type: application/json" \-d '{"people": ["Jason Fried, basecamp.com"]}'
Can I use it from Claude, ChatGPT or another AI assistant?
- Connector URL:
https://mcp.apify.com/?tools=themineworks/linkedin-email-finder. - Claude: Settings > Connectors > Add custom connector, paste the URL, sign in with Apify.
- ChatGPT: developer mode, add an MCP connector with the URL, sign in with Apify.
- Cursor or VS Code: add it as an HTTP MCP server with that URL.
- Claude Code:
claude mcp add -t http linkedin-email-finder "https://mcp.apify.com/?tools=themineworks/linkedin-email-finder".
Is it legal to find work emails this way? The actor reads only public pages: public LinkedIn profiles as a logged out visitor, and the company's own website. In hiQ Labs v. LinkedIn (9th Cir. 2022) the court found that scraping publicly available LinkedIn profiles likely does not violate the Computer Fraud and Abuse Act. No login is used and no account terms are accepted. Work emails are still personal data, so you are responsible for how you use the results: have a lawful basis under GDPR, UK GDPR or CCPA where they apply, honour opt outs, and follow anti spam rules such as CAN-SPAM when you send. This is general information, not legal advice. This actor is independent and not affiliated with or endorsed by LinkedIn.
Integrations
Results land in a standard Apify dataset, so they connect without extra code:
- Google Sheets: send each run's rows to a sheet with Apify's Google Sheets integration.
- Make, Zapier and n8n: start a run and read its dataset with the official Apify apps and nodes.
- Webhooks: get a call to your own URL when a run succeeds, then fetch the dataset.
- API and SDKs: start runs and read results with the Apify API, or the Python and JavaScript clients.
- MCP clients: Claude, ChatGPT, Cursor and other MCP clients can call the actor through
https://mcp.apify.com/?tools=themineworks/linkedin-email-finder.
More from The Mine Works
- LinkedIn Company Scraper
- LinkedIn Post Scraper
- LinkedIn Employees Scraper
- LinkedIn Profile Scraper
- LinkedIn Newsletter Scraper
- LinkedIn Candidate Finder
Social media and video
Leads and business directories
Marketing, SEO and reviews
Real estate
Science, health and government data
Jobs and hiring
E-commerce and marketplaces
Company and business data
Food and local services
Developer and AI tools
More tools
Support
Found a bug or need a field? Open an issue on the Issues tab of this actor. To ask for a new source, email dmineworks@gmail.com.
LinkedIn Email Finder turns a name and company, or a public LinkedIn profile URL, into one labelled work email with its confidence and evidence, with no login, from $1.50 per 1,000 emails.

