LinkedIn Company Employees Scraper - No Login avatar

LinkedIn Company Employees Scraper - No Login

Pricing

from $1.40 / 1,000 employees

Go to Apify Store
LinkedIn Company Employees Scraper - No Login

LinkedIn Company Employees Scraper - No Login

Get a LinkedIn company's employees from public search results by company URL or name, with no login or cookies. Name, headline, job title, location and public profile URL per person, each tied to the company with its evidence, plus coverage per company. Pay per employee and per company found.

Pricing

from $1.40 / 1,000 employees

Rating

0.0

(0)

Developer

Automation Craft

Automation Craft

Maintained by Community

Actor stats

0

Bookmarked

10

Total users

8

Monthly active users

2 days ago

Last modified

Share

LinkedIn Company Employees Scraper - No Login

LinkedIn Company Employees Scraper turns a LinkedIn company URL or a company name into a list of the people who work there: full name, headline, job title, location and the public LinkedIn profile URL, with no LinkedIn login, no cookies and no Sales Navigator seat. It finds the people in public Google results for LinkedIn profiles, and it delivers a person only when the result itself ties them to the company. Every row carries that evidence, and every company gets one row that says how many people were delivered against the head count LinkedIn shows.

You pay a small start fee per run, a price per delivered employee and a small fee per company where at least one employee was found. A company that does not exist, an ambiguous name, a duplicate and a company where nobody is found add nothing to the bill.

Why this one

  • Every row is tied to the company, and shows why. A person is delivered only when LinkedIn's own current employer field in the search result equals the company's name and no other employer text in the result (a second field, a text Google cut) points elsewhere (matchedOn: employer_field), or their public profile page lists the company by its LinkedIn URL (profile_page). A headline that says "at Company" is the person's own free text and never delivers a row by itself: such a person is delivered only when the profile page confirms it. The search result the match rests on is in evidence. People from another company with a similar name and results that only mention the company are dropped and never billed.
  • A second proof where LinkedIn allows it. With the profile check (on by default, included in the price) each person's public profile page is opened. When it lists the company among the current employers the row says employerConfirmed: true; when it lists other employers only, or none, the row is dropped. How often LinkedIn serves that page to a signed out visitor depends on the hour: in our night time test runs (UTC) 42 and 56 percent of delivered rows were confirmed this way, in afternoon and evening runs none. A row whose page could not be read rests on the search result and says so (profileCheck: page_unavailable or check_paused); such a row can be a person who left recently or is on a career break while LinkedIn's employer field still names the company (see "What this Actor does not do").
  • Coverage, in numbers. The company row carries employeesOnLinkedIn (the head count on the company's public page), employeesDelivered and coveragePercent, the reason the search stopped and every drop reason with its count. Public search results show a slice of a company, never the full roster, and this Actor says how large the slice is.
  • Public profile URLs. Every row has the linkedin.com/in/... URL, usable in any other tool.
  • The company is identified on LinkedIn first. A URL is read from the public company page; a name goes through LinkedIn's own company lookup and is searched only when exactly one company matches it. An ambiguous name gets a free row listing the candidates, never a guess. A company that does not exist is never searched and costs nothing.
  • Scheduled runs return only new people. Give a memory name and each run delivers and charges only people it has not delivered before, with firstSeenAt.

Quick start

  1. Paste companies into Companies (LinkedIn URLs or names), one per line: https://www.linkedin.com/company/stripe, a URL with a tab or tracking, a regional URL such as de.linkedin.com/company/zalando, a company name such as Datadog, or a numeric LinkedIn company id (searched only when a public page confirms the id; the URL or the name is the safer input).
  2. Set Maximum employees per company (the form starts at 10; the default for API calls is 100).
  3. Optional: add Job titles (for example CEO, CTO, Head of Marketing) or Locations (for example Berlin) to get only matching people.
  4. Click Start. Employee rows appear in the Dataset tab, then one company row per company and a run summary. Export JSON, CSV or Excel, or read them through the API.

What you get

Fill rates measured on 378 employee rows of 24 companies of mixed size (3 to 30,135 employees on LinkedIn) delivered by two test runs of the final build on 2026-10-01 with the default settings. They are what public search results show, not what the Actor chooses to return. Those runs ran in an evening hour (UTC) in which LinkedIn served no public profile page, so for the fields that come from the profile page the table gives a range: from zero (those runs) to the share measured in a night run of an earlier build (421 rows, 42 percent of the pages read).

FieldWhat it isFilled
fullNameThe person's name as the search result shows it100%
firstName, lastNameA simple split of the name; lastName is null when the name ends in an initial ("John W.")100%, 94%
headlineThe person's own LinkedIn headline (Google cuts long ones)100%
jobTitleCurrent title: Google's structured line for the profile when present, else the headline up to its "at Company"91%
seniority, department, isDecisionMakerRead from the title by a fixed word table: c_level, vp, director, head, manager, senior, entry; engineering, sales, marketing and ten more; null when the title does not say57%, 66%
locationFrom the profile page when it could be read, else from the search result98%
countryCodeTwo letter country code, from the profile page only0 to 43%
profileUrl, publicIdentifierPublic profile URL (https://www.linkedin.com/in/...) and its last part100%
matchedOnemployer_field or profile_page: how the row is tied to the company100% (all employer_field in the two runs; profile_page rows need a readable profile page)
employerOnLinkedInThe employer text the match rests on100%
employerConfirmed, profileChecktrue when the public profile page lists the company among the current employers; null when the page could not be read (page_unavailable), the check was paused because LinkedIn refused every page (check_paused) or the check was off. A null row rests on LinkedIn's employer field alone, which can lag behind a departuretrue on 0 to 56%
currentEmployersOnProfileCurrent employers the profile page lists: name and LinkedIn URL0 to 42%
followersExact from the profile page when read, else Google's rounded figure ("1.8K+")96%
connections, educationFrom the search result's snippet when LinkedIn's description shows them26%, 25%
schoolsOnProfile, languagesOnProfileSchools and languages the public profile page names0 to 32%, 0 to 20%
evidenceresultTitle, richLine (Google's structured line: location, title, company), snippet, the query that found the person, layout, and with a filter matchedLocation and matchedTitle (the text that satisfied it)100%
firstSeenAtWith a memory name: when this Actor first delivered the person under it. Not a hire datewith a memory
companyName, companyLinkedinUrl, companyId, companyKey, position, scrapedAtThe company the row belongs to and where it sits in your list100%

The company row (type: "company") carries companyName, companyLinkedinUrl, companyId, industry, companySize, website, headquarters, followers, employeesOnLinkedIn, employeesDelivered, employeesConfirmed, coveragePercent, knownSkipped, stopReason and a search object: queriesAsked, queriesAnswered, queriesFailed, unrelatedPages, resultsRead, candidates, droppedOtherEmployer, droppedMentionOnly, droppedHeadlineUnconfirmed, droppedNotAProfile, droppedLinkUnresolved, droppedContradictedByProfile, droppedUnconfirmed, droppedByTitleFilter, droppedByLocationFilter, duplicates, confirmedOnProfileOnly, lastQueries. Its status is ok when at least one employee was delivered. When the platform restarted the run while a company was being searched (a migration), its row carries search.resumedAfterRestart: true: employeesDelivered and employeesConfirmed are then counted from the dataset itself and are exact, the other search counters are what was on record and may miss the round that was in flight.

Status rows (type: "status", always free) take the place of a company that was not searched: not_found, ambiguous (with candidates), unresolved (a numeric id LinkedIn's public pages do not confirm), duplicate, invalid, school_page, login_required, blocked, skipped (the run stopped before this company), failed. A company that was searched without a deliverable person gets a free company row with status no_employees_found or search_unavailable. The last row of every run is a summary with counters and billing totals.

How many people per company

Google shows one page of about ten profiles per query, so the Actor asks many different queries per company (the plain forms, then job functions, then cities the results themselves show) and stops a company when further queries stop bringing new people (stopReason: "yield"), when your cap is reached, or when its 80 queries are used. The rule behind "yield": after the first query, a company earns one more query for every 4 people delivered, plus up to two for result pages that turned out to be about something else. The rule is the same on every plan.

Measured on 2026-10-05: with a cap of 30, all 12 larger companies of a test run (1,000 to 30,000 employees on LinkedIn) reached the cap; 12 small companies (3 to 124 employees) returned between 0 and 39 people each in two runs. Measured on 2026-10-01 with a cap of 300 (an earlier build with the same search and the same rule), Stripe (16,622 employees on LinkedIn) returned 300 people from 47 billed Google requests in 14 minutes. That is the most we have measured for one company; the cap goes to 1,000. Companies whose name is a common word ("Linear", "Notion", "Miro") return fewer people per query, because many results belong to other companies or products with that word in their name and are dropped. coveragePercent on the company row is the honest figure for each company; it can exceed 100 for a very small page, because employeesOnLinkedIn counts only the members LinkedIn links to the page.

Example employee row (from a run of the final build on 2026-10-01, shortened; the name and the profile URL are replaced by placeholders, and the profile page was not readable in that hour):

{
"type": "employee",
"companyName": "Stripe",
"companyLinkedinUrl": "https://www.linkedin.com/company/stripe",
"companyId": "2135371",
"fullName": "Jane Example",
"firstName": "Jane",
"lastName": "Example",
"headline": "Sales Development @ Stripe",
"jobTitle": "Sales Development",
"seniority": null,
"department": "sales",
"isDecisionMaker": null,
"location": "Greater Chicago Area",
"countryCode": null,
"profileUrl": "https://www.linkedin.com/in/jane-example",
"publicIdentifier": "jane-example",
"matchedOn": "employer_field",
"employerOnLinkedIn": "Stripe",
"employerConfirmed": null,
"profileCheck": "page_unavailable",
"followers": 970,
"evidence": {
"resultTitle": "Jane Example - Sales Development @ Stripe",
"richLine": ["Greater Chicago Area", "Sales Development", "Stripe"],
"query": "site:linkedin.com/in intitle:\"Stripe\" sales",
"layout": "modern"
},
"scrapedAt": "2026-10-01T17:34:50.079Z"
}

How much does it cost to scrape LinkedIn company employees?

Pay per event, no subscription. Prices per event by Apify plan:

EventFreeBronzeSilverGold
Employee (one delivered person)$0.0024$0.0024$0.0020$0.0014
Company (one company with at least one employee delivered)$0.024$0.024$0.028$0.035
Actor start (once per run)$0.012$0.012$0.012$0.012

Platinum and Diamond plans pay the Gold price. The Actor start event has no plan discount. The platform charges the Actor start event each time a run starts, so a run you resurrect after it stopped pays it once more. The Company event is higher on the plans where a person costs less: every plan gets the same search, and each company costs one Google request before its first person is known.

The Store pricing card shows these same prices per 1,000 events: "$2.40 / 1,000" on the Employee row means one employee costs 0.24 cents, and "$24.00 / 1,000" on the Company row means one company costs 2.4 cents.

Worked examples at the Free and Bronze price:

  • 1 company, 10 employees: $0.012 + $0.024 + 10 x $0.0024 = $0.060.
  • 1 company, 100 employees: $0.012 + $0.024 + 100 x $0.0024 = $0.276.
  • 25 companies, 20 employees each: $0.012 + 25 x $0.024 + 500 x $0.0024 = $1.812.

Every run pays the Actor start event once ($0.012), also a run that finds nothing. Beyond that, no Employee and no Company event is charged for: companies that are not found, ambiguous names, duplicates, invalid values, companies where nobody is found, people already delivered under your memory name, duplicates of a person, rows the profile page contradicts, every status row and the run summary. The Google proxy, the datacenter proxy and the profile check are included; there is no proxy bill on top.

You can cap a run's spend with the platform's maximum charge setting: the Actor delivers only rows the limit can pay for and does not start searching a company the remaining budget cannot cover.

Input

FieldTypeWhat it does
companieslistLinkedIn company URLs, company names or numeric ids, one per line, up to 5,000 per run. Also read from companyUrls, companyNames, company, companyUrl, identifier, queries and startUrls, so the common key names for a company list are accepted
maxEmployeesPerCompanyintegerStop a company after this many delivered employees, 1 to 1,000 (default 100, the form starts at 10)
maxEmployeesintegerStop the whole run after this many delivered employees; 0 means no run limit
jobTitleslistOnly people whose headline or job title contains one of these phrases as whole words; up to 20
locationslistOnly people whose location (search result or profile page) contains one of these phrases as whole words; up to 20
verifyEmployerbooleanCheck each person on their public profile page (default on). Off: only people whose search result carries LinkedIn's employer field are delivered
confirmedOnlybooleanDeliver only people the profile page confirms (default off)
memoryNamestringRemember delivered people under this name and deliver only new ones in later runs
resetMemorybooleanForget the memory before this run
maxConcurrencyintegerCompanies searched in parallel, 1 to 5 (default 3)
proxyConfigurationobjectProxy for the LinkedIn pages only; the default Apify datacenter proxy is included

Job titles and locations are real filters: the phrases go to Google as the search itself, and every row is then checked against them. A row that does not show the phrase in its own text is dropped and costs nothing. One value is one phrase, commas included ("Berlin, Germany" is a single phrase). Write a location the way LinkedIn shows it: a profile in Germany may say "Deutschland".

A run with no input at all (a literal {}) searches the sample company for 10 people. An explicitly empty list returns one status row without an Employee or Company charge (the run pays only its Actor start event), and so does an input that has other keys but no company list under an accepted key (a misspelled key never starts a paid search).

Run it from the API

curl -X POST "https://api.apify.com/v2/acts/automation_craft~linkedin-company-employees-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"companies":["https://www.linkedin.com/company/stripe"],"maxEmployeesPerCompany":10}'
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('automation_craft/linkedin-company-employees-scraper').call({
companies: ['https://www.linkedin.com/company/stripe', 'Datadog'],
maxEmployeesPerCompany: 25,
jobTitles: ['Account Executive', 'Sales Manager'],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
const employees = items.filter((x) => x.type === 'employee');
from apify_client import ApifyClient
client = ApifyClient("YOUR_TOKEN")
run = client.actor("automation_craft/linkedin-company-employees-scraper").call(run_input={
"companies": ["https://www.linkedin.com/company/stripe"],
"maxEmployeesPerCompany": 50,
"memoryName": "weekly-watch",
})
rows = list(client.dataset(run["defaultDatasetId"]).iterate_items())

Scheduled runs and the memory

With memoryName the Actor keeps, in a key-value store it creates in your account, the profile of every person it delivered under that name. A later run with the same name skips those people for free and delivers only the rest, each with firstSeenAt. A company with nobody new gets a free company row saying so. Newly visible on Google is not the same as newly hired: the row says when this Actor first saw the person, never when they joined.

  • Do not overlap runs that share one memory name: a run that finds another run's lock delivers its rows free of charge and does not remember them (two runs started in the same moment both do). Their rows still carry firstSeenAt, the time of that run.
  • If the memory cannot be opened or saved, the run delivers its rows free of charge and says so in a status row, because "pay once per person" cannot be guaranteed without it.
  • A run stops after 20 Google requests in a row that brought no deliverable person (companies with nobody on Google, or everybody already in the memory). The remaining companies get free skipped rows. Keep a watch list to companies that are active on LinkedIn.
  • A person is remembered by their public profile URL. Someone who changes that URL counts as a new person: they can be delivered and charged once more under the new URL. The Actor links an old and a new URL only where it can prove it (the page read through the old URL states the new one, or a remembered person of the same name appears in the same results), so do not rely on it.
  • A memory name belongs to this Actor in your account.

What this Actor does not do

  • The full roster of a company. Ours reads public Google results, which show a slice of a company (the company row says how large; the most we measured for one company is 300), and uses no login. Listing nearly every current employee needs LinkedIn's own people search behind a login.
  • A guarantee that every person still works there. A row tied by the search result alone rests on LinkedIn's own current employer field as Google last saw it, and that field can lag: a person who left recently, or who is on a career break, can still show the company. In our night test runs the profile page contradicted 8 of 236 and 3 of 180 rows it could check (3.4 and 1.7 percent), and those rows were dropped. In hours when LinkedIn serves no profile page such rows are delivered and charged with employerConfirmed: null. Rows with employerConfirmed: true are proven by the profile page; use confirmedOnly when it matters, and expect few or no rows from it in an hour when the page is closed.
  • Join dates, tenure and the full career. LinkedIn masks job titles and past employers on the public profile page, so ours returns the current title as the search result shows it, the schools and languages the public page names, and nothing that needs a login.
  • E-mail addresses. Ours finds none: it needs a paid third party service, and it is a different personal data product.
  • LinkedIn's own search filters. Ours filters by job title and location text, checked on every row, and labels seniority and department from the title; it does not filter by years at the company, industry, school, past title or "recently changed jobs".
  • Departures and title changes. A person who disappears from Google's results has not necessarily left, so ours reports newly visible people only and never a departure.
  • Profile photos and "open to work" flags. The photo is personal data nobody needs for a prospect list, and the flags need a login.
  • Counts without rows and alumni lists. Ours returns current people only.
  • Subsidiaries under the parent's name. A person whose employer on LinkedIn is "Zalando Marketing Services" is not delivered for Zalando; give the subsidiary's own LinkedIn page as a second company.
  • Two companies with exactly the same name. A row matched by the employer field cannot tell two companies of one name apart; rows with employerConfirmed: true are proven by the company's own LinkedIn URL. Use confirmedOnly when the name is shared.
  • School pages. LinkedIn does not serve school pages to signed out visitors; give the school's name instead of its URL.

Personal data

The rows describe people. The Actor reads only what is public without a login: Google's search results for LinkedIn profiles and the public profile page, and it returns no e-mail address, phone number or photo. You are responsible for having a lawful basis for what you do with the rows (under the GDPR, for example, a legitimate interest you have assessed, and the duty to inform people and to honour objections). If a person asks you to remove their data, delete their row from your copies; the Actor itself keeps only the company key with the profile identifier, two timestamps and a short run tag in your own memory store, and resetMemory deletes that.

FAQ

How do I get a list of a company's employees from LinkedIn without Sales Navigator?

Paste the company's LinkedIn URL or its name, set how many people you want and start the run. The Actor finds the people in public Google results for LinkedIn profiles, with no LinkedIn login, no cookies and no Sales Navigator seat, and returns the name, headline, job title, location and public profile URL of each.

Does it return every employee of the company?

No. Public search results show a slice of a company, never the full roster. The company row says how large the slice is: employeesDelivered against employeesOnLinkedIn, as coveragePercent, with the reason the search stopped. Listing nearly every current employee needs LinkedIn's own people search behind a login.

Can I filter by job title to find decision makers?

Yes. Put the titles into Job titles (for example CEO, CTO, Head of Marketing). The titles become the Google queries, and every row is then checked: a person is delivered only when their own headline or job title contains one of the phrases as whole words. Each row also carries seniority and isDecisionMaker, read from the title.

Can it tell me who was newly hired at a company?

It can tell you who is newly visible. With a memory name, a scheduled run delivers only people it has not delivered before, each with firstSeenAt. That is the date this Actor first saw the person in public results, not the date they joined; join dates need a LinkedIn login and are not returned.

What happens when a company is not found or nobody is found?

No Employee and no Company event is charged for it. A run in which no company gets an employee pays only its Actor start event ($0.012). A company that does not exist on LinkedIn gets a free not_found row and is never searched; a name that matches several companies gets a free ambiguous row listing them; a company where no result can be tied to it gets a free company row with status no_employees_found.

Why does this Actor run with limited permissions?

It follows least privilege: it reads its input, writes its own dataset and uses the Apify proxy, and it cannot see your other Actors, tasks or storages. The memory for scheduled runs is a named key-value store the Actor creates itself.

More data tools by Automation Craft

Changelog

See the Changelog tab. Version 0.1 (2026-10-01) is the first release.