LinkedIn Company Headcount & Team Directory Scraper
List current or past employees of any LinkedIn company page with filters for role keyword, seniority, job function, and connection degree. Requires a LinkedIn session cookie.
Input schema
Input fields, types, required flags, descriptions, defaults, and prefills come from the published definition. Prefill examples are distinct from defaults.
| Field | Type | Required | Description |
|---|---|---|---|
companyUrls | array | optional | LinkedIn company page URLs. Accepts https://www.linkedin.com/company/stripe/, linkedin.com/company/stripe, or company/stripe. |
companyDomains | array | optional | Company website domains, e.g. stripe.com or notion.so. The resolver follows LinkedIn's page website to pick the correct same-name company. |
companyNames | array | optional | Company display names, e.g. Stripe or Microsoft. Name-only input is validated; ambiguous bare names (Notion, Linear, Cal) return an error row asking for a URL or domain. |
Show all input fields
| Field | Type | Required | Description |
|---|---|---|---|
companyUrls | array | optional | LinkedIn company page URLs. Accepts https://www.linkedin.com/company/stripe/, linkedin.com/company/stripe, or company/stripe. |
companyDomains | array | optional | Company website domains, e.g. stripe.com or notion.so. The resolver follows LinkedIn's page website to pick the correct same-name company. |
companyNames | array | optional | Company display names, e.g. Stripe or Microsoft. Name-only input is validated; ambiguous bare names (Notion, Linear, Cal) return an error row asking for a URL or domain. |
includeEmployees | boolean | optional | When true, also request the employee roster. Requires a valid LinkedIn session cookie and is capped by LinkedIn at 2,500 visible results per query. |
maxEmployees | integer | optional | Roster cap per company (1-2500). Ignored when includeEmployees is false. Large companies are sampled. |
maxItemsTotal | integer | optional | Hard cap across company summaries, employee rows, and error rows (1-10000). |
startPage | integer | optional | Resume the roster from this page (1-250, 10 employees per page). Use it to continue a capped run. |
titleKeywords | string | optional | Roster filter: free-text role or headline match, e.g. Software Engineer or VP Sales. Cookie roster only. |
location | string | optional | Roster filter: best-effort location match on the employee's LinkedIn location, e.g. London. Cookie roster only. |
seniorityLevels | array | optional | Roster filter: one or more LinkedIn seniority levels. Empty means all levels. Cookie roster only. Allowed values: Entry, Associate, Mid-Senior, Director, Executive. |
functions | array | optional | Roster filter: one or more LinkedIn job functions. Empty means all functions. Cookie roster only. Allowed values: Engineering, Sales, Marketing, Operations, Finance, Human Resources, Design, Product, Customer Service, Information Technology, Legal, Other. |
connectionDegree | string | optional | Roster filter: restrict to 1st, 2nd, or 3rd+ degree connections. Cookie roster only. |
language | string | optional | Roster filter: primary profile language. Cookie roster only. |
cookies | string | optional | Optional LinkedIn session cookie for the employee roster. Accepts the li_at value or a full cookies JSON array. Never required for headcount/firmographics. |
proxyConfiguration | object | optional | Optional advanced override. The actor works without a proxy and chooses a working connection automatically. |
Download the published input schema
View complete JSON
{
"description": "Provide at least one company URL, domain, or name. Headcount and firmographics work without a cookie; employee rows are optional and require your own LinkedIn session cookie.",
"properties": {
"companyDomains": {
"default": [],
"description": "Company website domains, e.g. stripe.com or notion.so. The resolver follows LinkedIn's page website to pick the correct same-name company.",
"editor": "stringList",
"prefill": [
"stripe.com"
],
"title": "Company domains",
"type": "array"
},
"companyNames": {
"default": [],
"description": "Company display names, e.g. Stripe or Microsoft. Name-only input is validated; ambiguous bare names (Notion, Linear, Cal) return an error row asking for a URL or domain.",
"editor": "stringList",
"prefill": [],
"title": "Company names",
"type": "array"
},
"companyUrls": {
"default": [],
"description": "LinkedIn company page URLs. Accepts https://www.linkedin.com/company/stripe/, linkedin.com/company/stripe, or company/stripe.",
"editor": "stringList",
"prefill": [],
"title": "Company LinkedIn URLs",
"type": "array"
},
"connectionDegree": {
"default": "",
"description": "Roster filter: restrict to 1st, 2nd, or 3rd+ degree connections. Cookie roster only.",
"enum": [
"",
"F",
"S",
"O"
],
"enumTitles": [
"All connections",
"1st degree only",
"2nd degree only",
"3rd degree+"
],
"prefill": "",
"title": "Connection degree",
"type": "string"
},
"cookies": {
"description": "Optional LinkedIn session cookie for the employee roster. Accepts the li_at value or a full cookies JSON array. Never required for headcount/firmographics.",
"editor": "textarea",
"isSecret": true,
"title": "LinkedIn cookie (optional)",
"type": "string"
},
"functions": {
"default": [],
"description": "Roster filter: one or more LinkedIn job functions. Empty means all functions. Cookie roster only. Allowed values: Engineering, Sales, Marketing, Operations, Finance, Human Resources, Design, Product, Customer Service, Information Technology, Legal, Other.",
"editor": "stringList",
"items": {
"type": "string"
},
"prefill": [],
"title": "Job functions",
"type": "array"
},
"includeEmployees": {
"default": false,
"description": "When true, also request the employee roster. Requires a valid LinkedIn session cookie and is capped by LinkedIn at 2,500 visible results per query.",
"prefill": false,
"title": "Include employee roster",
"type": "boolean"
},
"language": {
"default": "",
"description": "Roster filter: primary profile language. Cookie roster only.",
"enum": [
"",
"en",
"es",
"fr",
"de",
"pt",
"it",
"nl",
"zh",
"ja"
],
"enumTitles": [
"Any language",
"English",
"Spanish",
"French",
"German",
"Portuguese",
"Italian",
"Dutch",
"Chinese",
"Japanese"
],
"prefill": "",
"title": "Profile language",
"type": "string"
},
"location": {
"description": "Roster filter: best-effort location match on the employee's LinkedIn location, e.g. London. Cookie roster only.",
"editor": "textfield",
"prefill": "",
"title": "Location",
"type": "string"
},
"maxEmployees": {
"default": 100,
"description": "Roster cap per company (1-2500). Ignored when includeEmployees is false. Large companies are sampled.",
"maximum": 2500,
"minimum": 1,
"prefill": 100,
"title": "Max employees per company",
"type": "integer"
},
"maxItemsTotal": {
"default": 500,
"description": "Hard cap across company summaries, employee rows, and error rows (1-10000).",
"maximum": 10000,
"minimum": 1,
"prefill": 1,
"title": "Max dataset items",
"type": "integer"
},
"proxyConfiguration": {
"description": "Optional advanced override. The actor works without a proxy and chooses a working connection automatically.",
"editor": "proxy",
"title": "Proxy configuration",
"type": "object"
},
"seniorityLevels": {
"default": [],
"description": "Roster filter: one or more LinkedIn seniority levels. Empty means all levels. Cookie roster only. Allowed values: Entry, Associate, Mid-Senior, Director, Executive.",
"editor": "stringList",
"items": {
"type": "string"
},
"prefill": [],
"title": "Seniority levels",
"type": "array"
},
"startPage": {
"default": 1,
"description": "Resume the roster from this page (1-250, 10 employees per page). Use it to continue a capped run.",
"maximum": 250,
"minimum": 1,
"prefill": 1,
"title": "Roster start page",
"type": "integer"
},
"titleKeywords": {
"description": "Roster filter: free-text role or headline match, e.g. Software Engineer or VP Sales. Cookie roster only.",
"editor": "textfield",
"prefill": "",
"title": "Title / headline keywords",
"type": "string"
}
},
"schemaVersion": 1,
"title": "LinkedIn Company Headcount & Team Directory Scraper Input",
"type": "object"
}
Output schema
Published result links and storage locations. This schema is not an exhaustive contract for every field in a dataset record.
| Field | Type | Required | Description |
|---|---|---|---|
records | string | optional | Dataset containing company summary rows, employee rows, and error rows. |
Download the published output schema
View complete JSON
{
"actorOutputSchemaVersion": 1,
"description": "Company summaries with LinkedIn-reported headcount and firmographics, optional employee roster rows, and explicit error rows for unresolved or cookie-blocked inputs.",
"properties": {
"records": {
"description": "Dataset containing company summary rows, employee rows, and error rows.",
"template": "{{links.apiDefaultDatasetUrl}}/items",
"title": "Company headcount and employee records",
"type": "string"
}
},
"title": "LinkedIn Company Headcount & Team Directory Scraper Output"
}
Dataset schema
Published dataset views and any declared field definitions. A view can select only a subset of the returned record fields.
Download the published dataset schema
View complete JSON
{
"actorSpecification": 1,
"title": "Company headcount and employee records",
"views": {
"overview": {
"display": {
"component": "table",
"properties": {
"companyName": {
"format": "text",
"label": "Company"
},
"companySizeRange": {
"format": "text",
"label": "Size band"
},
"currentTitle": {
"format": "text",
"label": "Title"
},
"followerCount": {
"format": "number",
"label": "Followers"
},
"industry": {
"format": "text",
"label": "Industry"
},
"linkedinEmployeeCount": {
"format": "number",
"label": "Employees (LinkedIn)"
},
"name": {
"format": "text",
"label": "Employee"
},
"profileUrl": {
"format": "link",
"label": "Profile URL"
},
"recordType": {
"format": "text",
"label": "Type"
}
}
},
"title": "Company headcount and employee records",
"transformation": {
"fields": [
"recordType",
"companyName",
"linkedinEmployeeCount",
"companySizeRange",
"followerCount",
"industry",
"name",
"currentTitle",
"profileUrl"
]
}
}
}
}
Key-value schema
Published key-value store definitions, when provided by this build.
This definition is not present in the default published build. See the published README for available documentation.
Published README
Read the full published documentation
LinkedIn Company Headcount & Team Directory Scraper
Get a company's LinkedIn-reported headcount and firmographics without logging in — employee count, size band, followers, industry, headquarters, type, website, address, about text, specialties, and logo. Add your own LinkedIn session cookie only when you also want the optional employee roster.
What this actor does
- Cookieless headcount first: one
recordType: companyrow per input with LinkedIn's reported employee count, size band, followers, industry, HQ, type, website, address, specialties, and logo. - Domain-aware company resolution: accepts LinkedIn URLs, company domains, and names; mixed inputs are allowed. A URL is trusted, a domain is confirmed against the page's website (including rebranded hosts such as
notion.so→notion.com), and a name must match the page exactly with a matching website or enough follower authority. - Honest ambiguity handling: bare-name inputs such as Notion, Linear, or Cal can collide with a different same-name LinkedIn page. Those return an explicit error row instead of a silent wrong-company match; domain forms such as
cal.comresolve normally. - Optional cookie roster: with a valid cookie, fetches up to 2,500 visible employees per company in 10-row pages, with title, location, seniority, function, connection degree, language, and resume-from-page filters.
- Explicit coverage and confidence: every company row carries
headcountConfidence,employeesReported,employeesReturned, and acoverageblock. Error rows have anerrorCode, so a missing roster never looks like a complete one. - Empty fields are omitted: missing values are left out instead of being returned as
null.
Output per company (recordType: "company")
input— the value you suppliedcompanyName,companySlug,companyUrllinkedinCompanyId— numeric LinkedIn organization IDresolutionMethod—provided_url,domain_slug, orname_variantresolutionConfidence,confidence—highfor a validated pageresolutionCandidates— alternate slugs tried, when anylinkedinEmployeeCount— LinkedIn-reported employee countlinkedinEmployeeCountSource—public_company_pagecompanySizeRange— LinkedIn's band, e.g.5,001-10,000 employeesheadcountConfidence—highwhen the count is inside the band or the band is open-ended;mediumup to 2x above;lowabove 2xheadcount—{ reported, sizeBand, asOf, source, audited: false }; the count is never described as auditedfollowerCount,industry,headquarters,companyTypewebsite,primaryAddress,addressabout,tagline,specialties[],locations[],logoUrlemployeesReported— LinkedIn's company countemployeesReturned,employeesCoveragePctrosterMode—cookielessorcookierosterAvailable,rosterReportedTotal,rosterRowsReturned,rosterCoveragePct,rosterConfidence,rosterReasoncoverage—{ employeesReturned, employeesReported, rosterReportedTotal, rosterCoveragePct, rosterAvailable, rosterConfidence }filtersApplied— the roster filters that were sentsourceMode—cookieless_public_pagewarnings[]— human-readable caveatsrecordType,scrapedAt
Output per employee (recordType: "employee")
companySlug,companyName,currentCompanyname,firstName,lastName,publicIdentifierheadline,currentTitle,currentDepartment,departmentseniority,senioritySource— derived from the title when LinkedIn does not expose a seniority fieldlocation,countryprofileUrlprofilePicture,profileImageUrl,profilePictureUrltenure,tenureYears,tenureSource— only when LinkedIn exposes a durationconnectionDegreeindustryName,currentCompanyLogoopenToWork,premium,verifiedStatus— only when the badge is presentemail,emailSource— only if an email is explicitly visible; usually absentsource—cookie-rostersourceMode—cookie_voyagercompanyFilterApplied—true; rows are only returned when the company filter was appliedconfidence—highwith a headline,mediumotherwiserecordType,scrapedAt
Output per error (recordType: "error")
input,companySlug,companyName(when known)errorCode—ambiguous_company_name,company_not_found,company_unresolved,rate_limited(the public company page returned 999/429),cookie_missing,cookie_invalid,company_id_unresolved,roster_rate_limited,roster_request_budget_reached, orroster_unavailableerror,message,helpretryableresolutionCandidates,rejectedCandidates— for resolution failuresrecordType,scrapedAt
Input
| Field | Type | Default | Description |
|---|---|---|---|
companyUrls |
array | [] |
LinkedIn company URLs, e.g. https://www.linkedin.com/company/stripe/ |
companyDomains |
array | [] |
Company domains, e.g. stripe.com, notion.so, or cal.com |
companyNames |
array | [] |
Company names, e.g. Stripe; ambiguous names need a URL or domain |
includeEmployees |
boolean | false |
Also request the optional employee roster (requires a valid cookie) |
maxEmployees |
integer | 100 |
Roster cap per company (1–2500); ignored without includeEmployees |
maxItemsTotal |
integer | 500 |
Hard cap on all dataset rows (1–10000) |
startPage |
integer | 1 |
Resume the roster from this 10-row page (1–250) |
titleKeywords |
string | – | Roster filter on role/headline, e.g. Software Engineer |
location |
string | – | Best-effort roster filter on the employee's LinkedIn location |
seniorityLevels |
array | [] |
Entry, Associate, Mid-Senior, Director, Executive |
functions |
array | [] |
Engineering, Sales, Marketing, Operations, Finance, Human Resources, Design, Product, Customer Service, Information Technology, Legal, Other |
connectionDegree |
string | "" |
"" (all), F (1st), S (2nd), O (3rd+) |
language |
string | "" |
Profile language: en, es, fr, de, pt, it, nl, zh, ja |
cookies |
string | – | Optional LinkedIn cookie for the roster (li_at or a full cookies JSON array) |
proxyConfiguration |
object | – | Optional advanced override; no proxy setup is required |
Provide at least one of companyUrls, companyDomains, or companyNames. You can mix them in one run.
Example: cookieless headcount for mixed inputs
{
"companyUrls": ["https://www.linkedin.com/company/stripe/"],
"companyDomains": ["notion.so", "linear.app"],
"includeEmployees": false,
"maxItemsTotal": 10
}
Example: batch headcount by name
{
"companyNames": ["Microsoft", "HubSpot", "Figma"],
"includeEmployees": false,
"maxItemsTotal": 20
}
Example: roster for one company with a cookie
{
"companyDomains": ["raycast.com"],
"includeEmployees": true,
"maxEmployees": 50,
"titleKeywords": "Engineer",
"seniorityLevels": ["Mid-Senior", "Director"],
"maxItemsTotal": 100,
"cookies": "YOUR_LI_AT_COOKIE"
}
Example: resume a capped roster
{
"companyUrls": ["https://www.linkedin.com/company/microsoft/"],
"includeEmployees": true,
"maxEmployees": 100,
"startPage": 26,
"maxItemsTotal": 200,
"cookies": "YOUR_LI_AT_COOKIE"
}
Use cases
- Account research — capture headcount, size band, HQ, industry, and website for a list of target companies without a LinkedIn login.
- Market mapping — compare headcount and follower counts across a segment of public and private companies.
- Recruiting and sourcing — pair cookieless firmographics with an optional roster when you have a session that can see the people tab.
- Sales operations — enrich CRM accounts with LinkedIn-reported size and industry fields.
- Competitive tracking — periodically snapshot headcount, followers, and specialties for competitor pages.
- Data quality checks — use
headcountConfidenceto flag companies whose reported count and size band disagree.
Limitations and caps
- Headcount is LinkedIn-reported, not audited. The public count can differ from LinkedIn's own size band; both fields are returned and
headcountConfidencelabels the gap. It is not a payroll, HRIS, or verified workforce count. - The roster is optional and cookie-gated. Without a valid cookie only the company row is returned, with an explicit warning and no employee rows.
- LinkedIn caps a roster query at 2,500 visible results. Pages contain 10 rows, so a query can need up to 250 requests. The actor budgets about 250 roster requests per run to stay inside LinkedIn's daily limit; when the budget is used, the remaining companies get an explicit error row and a later run can resume with
startPage. Large companies are samples rather than complete workforces. maxEmployeesis the per-company cap. If LinkedIn reports more employees than the cap, the row set is a sample;rosterReportedTotalandrosterCoveragePctmake the shortfall visible.- Filters are best-effort. Title, seniority, function, connection degree, and language are sent to LinkedIn; location is matched against the returned location text. Missing fields are not used to exclude a row.
- Email is not harvested. It is returned only when an email is explicitly visible on the result; expect none. Tenure is returned only when LinkedIn exposes a duration.
- Past employees are not supported. The actor returns current employees when a roster is requested.
- Name-only resolution is restricted. Ambiguous bare names (Notion, Linear, Cal, and similar) return an error row. Pass the LinkedIn URL or the company domain instead (for example
cal.comfor Cal.com). - Public pages can rate-limit. If LinkedIn throttles the company page itself, the actor emits a retryable
rate_limitederror row instead of a company row. If it throttles a roster request, the company row is still returned with a warning. Retry later or narrow the input list.
FAQ
Do I need a LinkedIn cookie for headcount? No. Company name, LinkedIn-reported employee count, size band, followers, industry, HQ, type, website, address, about, specialties, and logo come from the public company page.
When do I need a cookie?
Only for the employee roster. Set includeEmployees to true and add a valid li_at value or full cookie export. The cookie is never required for the company row.
Why did I get an error row instead of employees?
The run tells you which case applies: cookie_missing (no cookie), cookie_invalid (expired or rejected session), company_id_unresolved (the roster filter could not be mapped), roster_rate_limited (LinkedIn returned 999/429), or roster_request_budget_reached (the ~250-request run budget was used). Company headcount is still returned whenever the page resolves.
Why does the employee count disagree with the size band?
LinkedIn shows two independent fields. A company can report 7,000 employees while its self-selected band says 501–1,000. The actor publishes both and labels the count high, medium, or low confidence rather than presenting either as audited.
Why is the roster smaller than the company's headcount?
LinkedIn caps a single query at 2,500 visible results and serves 10 per page; the daily request limit is about 250. For a large employer, the roster is a sample. Raise maxEmployees, split the request with startPage, or narrow the filters.
Why did my company name resolve to a different company?
Some names are shared by several LinkedIn pages. The actor refuses the low-confidence name match and asks for a URL or domain. For example, notion.so resolves to the right Notion page, while the name alone is treated as ambiguous.
Can I get email addresses? Only if LinkedIn explicitly shows an email in the result. The actor does not guess or construct emails, so most records have no email field.
Can I get tenure?
Only when LinkedIn exposes a duration for the person. Otherwise tenure and tenureYears are omitted instead of estimated.
Does the actor return past employees? No. The roster is current employees only.
Is this affiliated with LinkedIn? No. This is an independent, third-party actor. It uses publicly visible company pages and, when you supply one, your own LinkedIn session for the roster. You are responsible for how you use the data.
Python quickstart
Use a schema-derived input and the Python standard library to start a run. An Apify account and token are required for execution.
Source build CSf1eVuAUi59biXia. Documentation is regenerated from the default published build; private drafts are not included.