LinkedIn Work Email Finder & Verifier avatar

LinkedIn Work Email Finder & Verifier

Pricing

$40.00 / 1,000 work email founds

Go to Apify Store
LinkedIn Work Email Finder & Verifier

LinkedIn Work Email Finder & Verifier

LinkedIn profile in, work email out — with its verification status. Give it profile URLs, public IDs, a CSV or API records and get one CRM-ready row per input: best work email, honest verification signals, company context, your own external ID. No LinkedIn login, cookies or session required.

Pricing

$40.00 / 1,000 work email founds

Rating

0.0

(0)

Developer

Delowar Munna

Delowar Munna

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

6 days ago

Last modified

Share

LinkedIn Work Email Finder & Verifier — work emails with honest verification signals, bulk profile enrichment, CRM-ready output

LinkedIn profile in. Work email out — with its verification status.

Give this Actor a list of LinkedIn person profiles — URLs, public identifiers, a CSV, or structured API records — and get back one CRM-ready row per input, carrying the best professional email, the verification signals behind it, person and current-company context, and your own external ID, unchanged.

No LinkedIn cookies. No LinkedIn login. No session ID. No residential proxy. You never hand this Actor a credential of any kind.


Why choose this Actor

Most LinkedIn-to-email tools return a string and leave you to find out the hard way whether it bounces. This one tells you what was actually checked.

This Actor
One row per submitted input, always✅ including not-found, invalid and error rows
External ID round-trips unchanged✅ for CRM / n8n / Make / Zapier reconciliation
Explicit verification semanticsvalid / risky / invalid / unknown / not_checked
Says which checks it actually ranverificationMethod lists only performed methods
Catch-all never sold as verified✅ always risky, at any confidence score
Person + current-company context✅ title, employer, website, domain, industry, size
Company-domain match check✅ catches a plausible address at the wrong company
Clear not-found and error states✅ 11 documented result statuses, with retryable
Duplicate inputs never billed twice✅ one lookup and one charge per person
You pay only for addresses✅ found nothing? costs nothing. Every miss and error row is free
Every address labelled before you sendisVerified, verificationStatus, isCatchAll, emailIsGenerated on every row

What "verified" means here

"Verified" means the address passed this Actor's validation policy and was classified as deliverable by the checks and provider actually used. Verification reduces bounce risk. It cannot guarantee future delivery.

No product can promise 100% accuracy, and this one does not.

What it does promise is that isVerified: true reflects an observation, never a prediction. A confidence score is a forecast; a mailbox check is evidence. The two are never conflated, and no score — however high — promotes an address to verified on its own.

Why catch-all domains come back risky

Many large employers run a mail gateway that accepts every recipient address. At such a domain nothing can prove a specific mailbox exists — and, worse, an email finder asked about a person who does not exist will happily return a well-formed address, because nothing can refute one. It looks identical to a real result apart from the score.

So a catch-all result is always reported as:

verificationStatus : risky
deliverability : risky
isCatchAll : true
isVerified : false

It is returned by default and labelled, and it is billed like any other delivered address — switch on every address is returned labelled, so you can triage them yourself.

The honest consequence: expect an empty email column for a meaningful share of people at large enterprises. That is the policy working, not the Actor failing.


Quick start

  1. Paste a few LinkedIn person profile URLs into LinkedIn profiles.
  2. Run.
  3. Open the Overview view, or export the Verified emails view to CSV.

That is the whole setup. Every other field has a sensible default.


Supported inputs

At least one of the three is required. They can be combined — every entry becomes its own result row, in the order you supplied them.

1. linkedinProfiles — one per line

https://www.linkedin.com/in/example-person/
https://www.linkedin.com/in/example-person/?trk=some-tracking-parameter
linkedin.com/in/example-person
example-person

Full URLs, URLs with tracking parameters, bare host URLs, and plain public identifiers all work. They are normalized to https://www.linkedin.com/in/<public-identifier> before anything else happens.

Person profiles only. Company, school, job, post and Sales Navigator URLs come back as unsupported_url rows rather than being silently dropped.

2. csvFile — bulk, with your own IDs

Paste the CSV rows directly, or give a public https:// URL that returns CSV.

linkedin_url,external_id
https://www.linkedin.com/in/lucy-attley-1a981730/,crm_10291
https://www.linkedin.com/in/swalkinshaw/,crm_10292
PurposeCanonical columnAlso accepted
Profilelinkedin_urllinkedinUrl, linkedin, profile_url, url, profile
Your IDexternal_idexternalId, id, crm_id, reference

external_id is optional. A headerless single-column list of profile URLs also works, as does a headerless two-column profile,id file.

3. records — structured, for API and automation callers

{
"records": [
{ "linkedinProfile": "https://www.linkedin.com/in/example-person", "externalId": "crm_12345" }
]
}

Example inputs

Minimal — a full URL and a bare public identifier, mixed freely

{
"linkedinProfiles": [
"https://www.linkedin.com/in/williamhgates/",
"frankbuschmann"
]
}

CSV with CRM IDs, risky candidates surfaced for triage

{
"csvFile": "linkedin_url,external_id\nhttps://www.linkedin.com/in/lucy-attley-1a981730/,crm_10291\nhttps://www.linkedin.com/in/swalkinshaw/,crm_10292\n",
"maxProfiles": 500
}

Structured, for a pipeline that only needs the address

{
"records": [
{ "linkedinProfile": "williamhgates", "externalId": "crm_12345" },
{ "linkedinProfile": "https://www.linkedin.com/in/lucy-attley-1a981730/", "externalId": "crm_12346" }
]
}

Every row still carries the full field set — see below. To export a narrower set of columns, use a dataset view rather than a run setting.


Output fields

One row per submitted input record. Every row carries every field, on every runnull, [] or false rather than a missing key. There is no setting that changes the column set, deliberately: two runs of the same list always produce CSVs that concatenate, and you can write row.companyDomain without guarding for its existence.

Want fewer columns in an export? Pick a dataset view (?view=overview, ?view=verifiedEmails, …) — that narrows the export without changing what is stored.

Input lineage

FieldType
inputIndexintegerposition in what you submitted
inputValuestringthe value you submitted, verbatim
externalIdstring / nullyour CRM ID, returned unchanged
inputTypestringprofile_url / public_identifier / profile_id / unsupported_url / unknown
normalizedLinkedinUrlstring / nullcanonical person URL
publicIdentifierstring / nullthe /in/<this> part
isDuplicatebooleanthis person already appeared in this run
duplicateOfInputIndexinteger / nullwhich row holds the original

Person

profileFound, linkedinProfileId, firstName, lastName, fullName, headline, profileImageUrl, location, country, countryCode, connectionsCount, followersCount.

Current company / role

jobTitle, companyName, companyLinkedinUrl, companyId, companyWebsite, companyDomain, companyIndustry, companySize, companyLocation.

Email

FieldType
emailFoundboolean
emailstring / nullthe chosen address
emailTypework / personal / role / nulla role mailbox is never sold as a person's own
emailDomainstring / null
emailMatchesCompanyDomainboolean / nullsits on the employer's own mail domain
emailSourcelinkedin_public / enrichment / nullpublished on LinkedIn, or independently discovered
candidateCountintegerhow many candidates were considered

Verification

Present on every row. On a row with no address they read not_checked / unknown / null rather than being absent.

FieldValues
isVerifiedboolean — an observation, never a score threshold
verificationStatusvalid / risky / invalid / unknown / not_checked
deliverabilitydeliverable / risky / undeliverable / unknown
confidenceScorenumber / null — the provider's own score, not what decides isVerified
verificationMethodarray of syntax, dns_mx, provider, smtponly methods actually performed
syntaxValidboolean / null
mxFoundboolean / null — null means the DNS lookup itself was inconclusive, which is not evidence of absence
smtpCheckedboolean — true only when an SMTP conversation really happened
smtpStatusstring / null
isCatchAllboolean / null — null means nobody told us, which is different from false
isDisposableboolean / null
isRoleAddressboolean / null
verifiedAtISO 8601 / null

Result and error

resultStatus, isPartial, errorType, errorMessage, retryable, attemptCount, processingTimeMs, processedAt.


Sample output

Apify Console dataset in table view, All fields tab: one row per submitted LinkedIn profile with company, email, verification and error columns

The dataset in the Console, All fields tab. The tabs along the top are the five views — Overview, Verified emails, All results, Not found / failed, Company context — plus All fields.

Three things in that screenshot are worth looking at, because they are the product rather than the decoration:

  • The two shopify.com rows score 81 and 84 and are still risky. Shopify runs a catch-all gateway, so the provider's confidence means nothing about whether the mailbox exists. A high score never promotes an address here.
  • The EMAIL_NOT_FOUND rows are still there, carrying the person and company context that was resolved before the email step came up empty. Nothing is dropped, and those rows are free.
  • Every column is present on every row. The field set does not vary between runs, so two exports of the same list concatenate.

Real rows from a real run — 12 LinkedIn profiles in, 12 rows out, nothing invented. Each block below is one record as it appears in the matching dataset view (the tabs above the results table). Every view is a column selection over the same rows; no view hides a row.

Overview

The default tab — the eight fields most people check first.

{
"externalId": null,
"fullName": "Bill Gates",
"jobTitle": null,
"companyName": "Gates Foundation",
"email": "bill.gates@gatesfoundation.org",
"verificationStatus": "valid",
"confidenceScore": 95,
"resultStatus": "success"
}

Verified emails

Leads with isVerified, emailType and resultStatus, so the rows you can actually send to sort to the top of the table.

{
"isVerified": true,
"emailType": "work",
"resultStatus": "success",
"externalId": null,
"fullName": "Lucy Attley",
"email": "lucy.attley@unilever.com",
"emailDomain": "unilever.com",
"companyName": "Unilever",
"companyDomain": "unilever.com",
"jobTitle": null,
"deliverability": "deliverable",
"confidenceScore": 95,
"verifiedAt": "2026-08-22T12:44:40.809Z",
"normalizedLinkedinUrl": "https://www.linkedin.com/in/lucy-attley-1a981730/"
}

Not found / failed

Leads with the error fields. Read retryable before you re-run anything — it is the difference between a person we could not find and a lookup that never completed:

{
"resultStatus": "timeout",
"errorType": "PROFILE_PROVIDER_TIMEOUT",
"errorMessage": "LinkedIn withheld the public page and the backup data source could not be reached (timeout_30000ms). This says nothing about whether the person exists — the lookup never completed. Re-running this input is worth doing.",
"retryable": true,
"inputIndex": 1,
"inputValue": "https://www.linkedin.com/in/towhid-rahman",
"externalId": null,
"normalizedLinkedinUrl": "https://www.linkedin.com/in/towhid-rahman/",
"profileFound": false,
"emailFound": false,
"companyDomain": null,
"isPartial": false,
"attemptCount": 1,
"processedAt": "2026-08-22T12:44:42.143Z"
}

A profile_not_found row means the opposite: the lookup finished and found nothing, so retryable is false and re-running will not help. This row was not charged — nothing on this tab ever is.

Company context

For enriching an account list rather than a person list.

{
"fullName": "Frank Buschmann",
"jobTitle": null,
"companyName": "Siemens AG",
"companyWebsite": "http://www.siemens.com",
"companyDomain": "siemens.com",
"email": "frank.buschmann@siemens.com",
"emailMatchesCompanyDomain": true
}

All results — every field

All 58 fields, on every row, on every run. The record below is deliberately the awkward one: a catch-all domain, where the Actor found a well-formed address with a confidence score of 81 and still refused to call it verified.

{
"inputIndex": 3,
"inputValue": "https://www.linkedin.com/in/swalkinshaw/",
"externalId": null,
"inputType": "profile_url",
"normalizedLinkedinUrl": "https://www.linkedin.com/in/swalkinshaw/",
"publicIdentifier": "swalkinshaw",
"isDuplicate": false,
"duplicateOfInputIndex": null,
"profileFound": true,
"linkedinProfileId": null,
"firstName": "Scott",
"lastName": "Walkinshaw",
"fullName": "Scott Walkinshaw",
"headline": null,
"profileImageUrl": "https://static.licdn.com/aero-v1/sc/h/bgaqk7x4ntjz0wg67d8u723eb",
"location": "Toronto, Ontario, Canada",
"country": "Canada",
"countryCode": "CA",
"connectionsCount": null,
"followersCount": 431,
"jobTitle": null,
"companyName": "Shopify",
"companyLinkedinUrl": "https://www.linkedin.com/company/shopify",
"companyId": "shopify",
"companyWebsite": "https://www.shopify.com",
"companyDomain": "shopify.com",
"companyIndustry": "Software Development",
"companySize": "10,001+ employees",
"companyLocation": "Ottawa, ON",
"emailFound": true,
"email": "scott.walkinshaw@shopify.com",
"emailType": "work",
"emailDomain": "shopify.com",
"emailMatchesCompanyDomain": true,
"emailSource": "enrichment",
"candidateCount": 1,
"isVerified": false,
"verificationStatus": "risky",
"deliverability": "risky",
"confidenceScore": 81,
"verificationMethod": ["syntax", "dns_mx", "provider"],
"syntaxValid": true,
"mxFound": true,
"smtpChecked": false,
"smtpStatus": null,
"isCatchAll": true,
"isDisposable": false,
"isRoleAddress": false,
"verifiedAt": "2026-08-22T12:44:39.998Z",
"resultStatus": "success",
"isPartial": false,
"errorType": null,
"errorMessage": null,
"retryable": false,
"attemptCount": 0,
"processingTimeMs": 78458,
"processedAt": "2026-08-22T12:44:42.143Z"
}

Read that record from the bottom up and it explains the whole product. isCatchAll: true means shopify.com accepts mail for any address you throw at it, so the provider's valid proves nothing. verificationStatus is therefore risky, isVerified is false, and the score of 81 was not allowed to promote it. You get the address, clearly labelled, and you are charged for it — it is a delivered work address, and the label is how you decide whether to send. Billing is one rule with no exceptions: an address on the row is a charge, once per person. What you are never charged for is a row with no address on it.


Result statuses

StatusMeaningRetryableCharged
successprofile resolved and a work email returnedyes — an address on the row is a charge
email_not_foundprofile resolved, no acceptable candidatenono
profile_not_foundthe person could not be resolved — the lookup COMPLETED and found nothingnono
invalid_inputthe entry is not a LinkedIn identifiernono
unsupported_urla URL, but not a person profilenono
verification_failedverification could not completesometimesno
provider_rate_limitedupstream 429 / quotayesno
provider_errorupstream failuresometimesno
timeouta lookup never completed — the profile or email source could not be reachedyesno
internal_errorunexpected Actor errorsometimesno
skipped_over_limitbeyond maxProfiles, or the run hit your spending limit firstyesno

isPartial: true means person and company context were obtained but the email step failed — the row is still useful, and it is worth retrying.

A note on risky addresses, since there is no status for them. An unconfirmed or catch-all address is not a separate result status — it comes back as success with verificationStatus: "risky" and isVerified: false, and it is charged like any other delivered address. Filter on verificationStatus, never on resultStatus, when you want only the confirmed ones. Measured on a 52-profile run: 34 addresses delivered, 17 of them confirmed.

An earlier version of this table listed an email_risky_only status. It is unreachable in the shipped Actor and has been removed from the table rather than left as a row that can never appear.


Pricing

You pay for one thing: a professional work email we actually hand you.

EventFires when
email_foundonce per unique person for whom a work email is returned on a success row

Every address comes labelled — isVerified, verificationStatus, isCatchAll, confidenceScore, and verificationMethod listing the checks actually run. Filter on those before you send. At a catch-all domain an email finder can return a plausible address for a person who does not exist, which is why we mark it risky and never call it verified — see What "verified" means here.

The label to watch: emailIsGenerated: true means the vendor built the address from a name and a domain rather than finding it published anywhere. Paired with isVerified: false that is a plausible guess, not a confirmed mailbox — most likely at a domain that accepts mail for anyone. It is delivered, labelled and billed like any other address, so filter on emailIsGenerated and isVerified together before you send. We never call one verified.

Never charged

  • invalid input, unsupported URLs
  • profile not found
  • no email found
  • verification that could not complete, where no address was returned
  • provider errors, rate limits, timeouts
  • every duplicate row for a person already charged once in this run
  • inputs beyond your maxProfiles limit
  • emails already published in public LinkedIn text (free, and labelled emailSource: linkedin_public)
  • every row where no work address was returned at all

There is no setting that changes any of this. Every professional address we find is returned and billed once. To work with only the confirmed ones, filter on verificationStatus or export the Verified emails view — that narrows your export without changing what the run found or what it cost.

There is no Actor-start fee, no rental, no monthly minimum, and no charge for a run that returns nothing.

Cost examples

A run's cost is number of work emails returned × the per-event price. Nothing else.

You submitTypical hit rate¹You are billed for
100 profiles~60%~60 events
1,000 profiles~60%~600 events
10,000 profiles~60%~6,000 events

Of those, roughly a third verify clean and the rest sit at catch-all domains, labelled risky. Both are billed; only the label tells you which is which. | 1,000 profiles, all at catch-all domains | 0% | 0 events |

¹ Illustrative only, and it varies a great deal with your list. Employer mix is the single biggest factor: a list concentrated on large enterprises running catch-all mail gateways will verify far less than a list of small and mid-size companies. Run 50 of your own profiles first and read verifiedEmailRate in the run summary — that is your rate, not ours.

The live per-event price is on this Actor's Pricing tab. This README deliberately quotes no currency figures, so it cannot go stale.

Free plan limitations

Apify Free planAny paid plan
Unique profiles per run25up to 10,000
Everything elseidenticalidentical

Inputs beyond the free-plan cap still get a result row, marked skipped_over_limit, so your list still reconciles one-to-one with what you submitted. Nothing is charged for them.

Why the limit exists: every person looked up costs this Actor a real third-party credit, and Apify pays developers nothing for Free-plan runs. Capping the count — rather than removing the feature — keeps the trial honest: you see real verified emails, on profiles you chose, with the full verification detail.


Use cases

  • B2B sales / RevOps — enrich an existing LinkedIn lead list into a sendable one.
  • Recruiting — get a professional contact route for a shortlist.
  • Lead-gen agencies — bulk profile-to-email, with the client's IDs preserved.
  • CRM hygiene — re-verify addresses you already hold and flag the risky ones.
  • Developers — one deterministic API step in a larger pipeline.

API, n8n, Make, Zapier and webhooks

Call the Actor and read the dataset. Because there is exactly one row per input and every row carries inputIndex and externalId, merging back needs no matching logic.

curl -X POST "https://api.apify.com/v2/acts/coregent~linkedin-email-scraper/runs?token=$APIFY_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"records": [
{ "linkedinProfile": "https://www.linkedin.com/in/example-person", "externalId": "crm_12345" }
]
}'

Then fetch just the verified rows:

$curl "https://api.apify.com/v2/datasets/<DATASET_ID>/items?view=verifiedEmails&format=csv&token=$APIFY_TOKEN"

The run summary — totals, verified rate, duration, billing counts — is stored separately in the run's key-value store under RUN_SUMMARY, and is never mixed into your dataset rows, so exports stay uniform.

If a run's Output panel says "No output returned." immediately after it finishes, give it a moment and reload — the results are in the dataset either way, under Storage → Dataset or at https://api.apify.com/v2/datasets/<DATASET_ID>/items.


Limits

  • Normal use: 1–1,000 profiles per run.
  • Hard cap: 10,000 unique profiles per run; 50,000 submitted records.
  • Apify Free plan: 25 unique profiles per run (see above).
  • Duplicate inputs do not consume your maxProfiles allowance.

Not-found behaviour

Nothing ever silently disappears. Every submitted record gets a terminal row.

There is no setting that changes this. Invalid entries, unsupported URLs, duplicates and profiles LinkedIn withheld all come back as rows with a resultStatus explaining themselves, and none of them is charged. Your export always has exactly as many rows as your import, in the same order.

Filter the rows you do not want on your side — resultStatus is the column, and the Not found / failed view leads with it.


Troubleshooting

SymptomCauseWhat to do
Lots of profile_not_foundLinkedIn withheld those public pages and the backup source looked, and found nothingNothing you can configure fixes this — LinkedIn decides per profile, not per requester. Re-run later; the set shifts a little between runs.
Lots of timeout or provider_error rowsour backup data source could not be reached, so those lookups never ranThis says nothing about whether those people exist. All are retryable: true — re-run just those rows.
Lots of email_not_found with companyDomain: nullno employer could be resolved for those peopleAn email finder is given a name and a company domain. Without a domain nobody can find them, at any price.
Lots of risky at one big employerthat employer runs a catch-all mail gateway, which accepts mail for any addressThe addresses are real candidates, but nobody can confirm them. Filter on isCatchAll and treat them as lower-confidence leads.
provider_rate_limited rowsupstream throttlingretryable: true — re-run just those rows. Nothing to configure: concurrency is fixed at a value that stays inside every provider's rate limit.
Empty email column everywhere, verificationStatus: unknownno email vendor is configured on this ActorContact support; this is a configuration fault on our side, not your input.
Output shorter than inputshould be impossible — every input gets a rowReport the run ID. The run log ends with a COMPLETENESS line that checks exactly this.
Fewer rows processed than submitted, skipped_over_limitmaxProfiles, the Free-plan cap, or your per-run spending limitRaise the relevant limit and re-run.

FAQ

Does this scrape emails directly from LinkedIn? Not usually. LinkedIn is the professional identity input. If someone has published an address in their own profile text, it is extracted for free and labelled emailSource: linkedin_public. Otherwise the address is discovered through independent business-data enrichment sources and labelled emailSource: enrichment.

Do I need LinkedIn cookies, a login, or a session ID? No. Never. This is a hard design constraint, not a default.

Do I need a residential proxy? No. Apify Datacenter proxy is the default and is what the Actor uses. Selecting Apify Residential has no effect — it was measured to block the identical set of profiles while costing bandwidth, so it is declined and the run says so. Your own Custom proxy URLs are always honoured in full, through your own provider account.

Are the emails guaranteed to deliver? No. Nobody can promise that. This Actor distinguishes verified, risky, invalid and unknown and tells you which checks it ran, which is the honest version of that promise.

Can I upload a CSV? Yes — paste the rows or give a public CSV URL.

Can I keep my CRM ID? Yes, via external_id. It round-trips unchanged.

Company URLs? No. Person profiles only; company URLs return unsupported_url.

Sales Navigator URLs? Not supported. Use the public person-profile URL.

Personal emails? No. This Actor is optimised for professional/work addresses.

What happens to duplicates? One lookup, one charge, and a row each — so your export still lines up with your import.


Compliance and privacy

This Actor is an independent data-enrichment tool and is not affiliated with, endorsed by, or sponsored by LinkedIn. It accepts public LinkedIn person-profile identifiers as input. Professional email discovery may use independent business-data and public-data enrichment sources; an email is not guaranteed for every profile.

  • No LinkedIn credentials of any kind are requested, stored or used.
  • Provider keys live in Apify secrets. They never appear in logs, errors or output.
  • Raw provider responses are not emitted.
  • Email data is not stored outside your own Apify run output.

You are responsible for ensuring your use of contact data complies with applicable laws and regulations (including GDPR, CAN-SPAM and equivalents), platform terms, and outreach requirements in the jurisdictions you operate in.


Changelog

1.0

  • First public release.
  • LinkedIn URL / public identifier / CSV / structured-record input, with external-ID passthrough.
  • One row per submitted input, including duplicates, not-found and error rows.
  • Work-email discovery with free extraction from public LinkedIn text before any paid lookup.
  • Full verification block: syntax, MX, provider verdict, SMTP where actually performed.
  • Catch-all always classified risky, never sold as verified.
  • Per-address billing on email_found: an address on the row is a charge, once per person, with no exception for grade or provenance. Rows with no address are free.
  • Five dataset views and a separate run summary.

Support

Open an issue on this Actor's Issues tab. Include the run ID — the run summary in the key-value store contains everything needed to diagnose a run, and no personal data beyond what you submitted.


What this Actor does NOT do

  • ❌ Search LinkedIn for new people by keyword, title, company or location — it enriches profiles you name.
  • ❌ Scrape Sales Navigator.
  • ❌ Return full work history, education, skills or certifications.
  • ❌ Find phone numbers.
  • ❌ Find personal / private email addresses.
  • ❌ Send email campaigns.
  • ❌ Use LinkedIn cookies, logins, sessions or your LinkedIn account.
  • ❌ Solve CAPTCHAs or access private/restricted LinkedIn data.
  • ❌ Claim any address is guaranteed deliverable.