# LinkedIn Sales Navigator Company Employees (`rayackery/salesnav-company-employees`) Actor

Get every employee Sales Navigator lists for your companies, as flat rows, with a found-X-of-Y report for each company that names any gap.

- **URL**: https://apify.com/rayackery/salesnav-company-employees.md
- **Developed by:** [Denys Petrenko](https://apify.com/rayackery) (community)
- **Stats:** 1 total users, 1 monthly users, 90.5% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 employees

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### What it does

Get **every employee** LinkedIn Sales Navigator lists for your companies. Give the Actor one or more companies and your own Sales Navigator session, and it saves one row per person: name, current job, location and links, plus the full profile if you want it. It's built for sales, recruiting and research teams that already have a Sales Navigator seat and need a whole company's headcount in a spreadsheet, a CRM or an automation, including companies far past the 2,500 results one Sales Navigator search can page through.

- **You see what's missing.** Each company ends with a `found X of Y` line: how many people the Actor found against how many Sales Navigator says the company has, raised when the Actor's slices hold more, so any difference is visible. Every slice that comes back short, and every group of people LinkedIn's filters can't reach, is listed as a gap with the slice of the company it sits in, for example `7 unreachable: 7 in Berlin, Germany / Engineering (leaky split)`. LinkedIn reports the count of a very large company only approximately: a remainder of up to 0.5% of the count (at least 3 people) that no gap explains isn't listed separately, and a larger one is reported as count drift.
- **Big companies are fine.** Sales Navigator pages through at most 2,500 results for one search. The Actor counts first and splits any slice of more than 2,000 people. Location slices add up exactly. Where one location is still too big, it is split by function and then by seniority; LinkedIn files a few people under none of those values, so the Actor checks each of those splits against its count and reports any shortfall. A slice that LinkedIn won't page past 2,500 is reported as a search-cap gap. Every slice is then paged.
- **Narrow it with filters.** Locations, current and past job titles, schools, industries, seniority, function, years at the company, years of experience, keywords and more. The filter values you type are matched to LinkedIn's own suggestions, and the run log shows which one each was matched to.
- **Short or Full rows.** Short rows come straight from the search results. Full rows add each person's Sales Navigator profile: headline, positions, education, skills, languages and the contact details LinkedIn shows you. Optionally, public profiles add certifications, projects, volunteering and more (see [Short, Full and public profiles](#short-full-and-public-profiles)).
- **A failed lookup doesn't lose the person.** If a profile can't be fetched, the row is still saved from the search result, and `_meta.errors` says what failed.
- **Check first.** **Check only** counts every company and estimates the run without saving or charging a single row.
- Rows are saved as they're found, so a run that stops early keeps everything it already saved.

> **This Actor uses your own LinkedIn account.** It signs in to Sales Navigator with the session you paste in, so every request counts as yours. LinkedIn's User Agreement prohibits automated access, and heavy use can get your account restricted. Keep your volumes moderate and read the Limits section before your first run. This Actor is not affiliated with or endorsed by LinkedIn.

### How to run it

1. Click **Try for free** (or **Start** if you've used the Actor before).
2. In **Companies**, add each company on its own line: a LinkedIn company page link, a Sales Navigator company link, a numeric company ID or a company name.
3. In **LinkedIn session (Copy as cURL)**, paste your session. [How to copy your session](#how-to-copy-your-session) takes about a minute.
4. Pick **Profile detail**: Full (the default) or Short. Set **Max items, in total**. For a first run, keep the default of 100.
5. Optional: open **Filters** to narrow who you get, for example by location or seniority.
6. For a big company, tick **Check only** and start the run first. It shows how many people match in each company and an estimate of the requests and time for a run of up to **Max items, in total** (set that to 0 to estimate the whole company). It saves nothing and charges nothing beyond the start fee.
7. Under **Proxy**, keep the residential proxy and pick the country you normally log in to LinkedIn from.
8. Optional but recommended: set a **maximum cost** for the run in the run options. The run stops itself before it goes over (see [Pricing](#pricing)).
9. Click **Start**. The status line shows progress, for example `Company 2 of 5: 1,200 of 1,350 found · 3 slices left`.
10. When the run finishes, read the final status for the `found X of Y` line of each company, then open the **Output** tab and download the rows as CSV, Excel or JSON.

To reuse the same companies and settings, save them as a **task** and start the task whenever you need fresh data.

#### On a schedule

Attach a task to an Apify **schedule** to run it daily or weekly. Each scheduled run starts from the first person of each company and is charged like any other run, so point scheduled tasks at searches that only return what's new to you, for example with the **Recently changed jobs** filter. When LinkedIn ends your session, scheduled runs stop with a message saying so: copy a fresh session into the task.

#### Through the API

The **API** tab on this page shows ready-made calls for your account, in several languages. The input is the same JSON as the form:

```json
{
  "companies": ["https://www.linkedin.com/company/example"],
  "browserRequest": "curl 'https://www.linkedin.com/sales-api/salesApiLeadSearch?...' -H '...' ...",
  "profileScraperMode": "Full",
  "locations": ["Germany"],
  "seniorityLevelIds": ["220", "300", "310"],
  "maxItems": 500,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"],
    "apifyProxyCountry": "US"
  }
}
```

Add `maxTotalChargeUsd` to the run call to cap what the run can cost. The session field is stored encrypted, also when you pass it through the API.

#### With your other tools

Apify's integrations start runs from n8n, Make or Zapier, and send the results to Slack, a webhook or your own workflow when a run finishes. Set them up on the **Integrations** tab of this Actor or of your saved task.

### How to copy your session

The Actor needs one real Sales Navigator request from your browser. It holds your login, so treat it like a password. The field is stored encrypted.

1. Open a search in Sales Navigator in Chrome.
2. Open DevTools (⌥⌘I on Mac, Ctrl+Shift+I on Windows), open the **Network** tab and type `salesApi` into its filter box.
3. Reload the page, right-click a `salesApiLeadSearch` row, and choose **Copy → Copy as cURL (bash)**.
4. Paste it into the **LinkedIn session (Copy as cURL)** field.

Choose **Copy as cURL (bash)**, not the cmd or PowerShell versions. What you paste should start with `curl '`. A cookie export, a file path or a link won't work.

A session lasts until LinkedIn ends it. If a run says the session ended, log in to Sales Navigator in Chrome again and copy a fresh request.

### Input

| Field | API key | What it does | Default |
|---|---|---|---|
| **Companies** (required) | `companies` | The companies to collect employees for, one per line: a LinkedIn company page link, a Sales Navigator company link, a numeric company ID or a company name. Every company is searched on its own. A person who works at two of your companies is saved once, under the first. | none |
| **LinkedIn session (Copy as cURL)** (required) | `browserRequest` | The request you copied in the steps above. | none |
| **Profile detail** | `profileScraperMode` | `Full` or `Short`. See [Short, Full and public profiles](#short-full-and-public-profiles). | Full |
| **Add public profile sections** | `publicProfiles` | Also open each person's public LinkedIn profile for more sections. Each one counts as a profile view on your account. Full rows only. | off |
| **Max public profiles** | `maxPublicProfiles` | The most public profiles to open in one run, at least 1. After that, rows are still saved with their Sales Navigator details only. | 300 |
| **Max items, in total** | `maxItems` | Stop after this many rows, counted across all your companies. Use 0 for no limit. | 100 |
| **Max items per company** | `maxItemsPerCompany` | Stop each company after this many rows. Use 0 for no limit. | 0 |
| **Check only** | `checkOnly` | Count each company and estimate the run without saving or charging rows. | off |
| **Proxy** | `proxy` | Residential proxies are used by default. Choose your own country so the run looks like it comes from where you normally log in to LinkedIn. | Apify residential proxy |
| **Locations** / **Exclude locations** (Filters) | `locations`, `excludeLocations` | Place names or LinkedIn geo IDs. Each name is matched to LinkedIn's closest suggestion. | none |
| **Keywords** (Filters) | `searchQuery` | Words to look for in profiles. LinkedIn's boolean operators (AND, OR, NOT and quotes) work. | none |
| **Current job titles** / **Exclude current job titles** (Filters) | `jobTitles`, `excludeCurrentJobTitles` | Titles people hold now. A title with no match among LinkedIn's suggestions is searched as written. | none |
| **Past job titles** / **Exclude past job titles** (Filters) | `pastJobTitles`, `excludePastJobTitles` | Titles people held earlier in their career. | none |
| **Schools** / **Exclude schools** (Filters) | `schools`, `excludeSchools` | School names or LinkedIn school IDs. | none |
| **Industry IDs** / **Exclude industry IDs** (Filters) | `industryIds`, `excludeIndustryIds` | Numeric LinkedIn industry IDs. Choose an industry in a Sales Navigator search and read its ID from the address bar. | none |
| **Seniority levels** / **Exclude seniority levels** (Filters) | `seniorityLevelIds`, `excludeSeniorityLevelIds` | In Training (`100`), Entry Level (`110`), Senior (`120`), Strategic (`130`), Entry Level Manager (`200`), Experienced Manager (`210`), Director (`220`), Vice President (`300`), CXO (`310`), Owner / Partner (`320`). | none |
| **Job functions** / **Exclude job functions** (Filters) | `functionIds`, `excludeFunctionIds` | The 26 Sales Navigator functions, `1` (Accounting) to `26` (Customer Success and Support). | none |
| **Years at current company** (Filters) | `yearsAtCurrentCompanyIds` | `1` Less than 1 year, `2` 1 to 2 years, `3` 3 to 5 years, `4` 6 to 10 years, `5` More than 10 years. | none |
| **Years of experience** (Filters) | `yearsOfExperienceIds` | The same five ranges, for total experience. | none |
| **Company headcount** (Filters) | `companyHeadcount` | Only people whose current company has a headcount in these ranges, `A` (Self-employed) to `I` (10,001+). | none |
| **Recently changed jobs** (Filters) | `recentlyChangedJobs` | Only people who changed jobs recently, as Sales Navigator defines it. | off |
| **Exclude past companies** (Filters) | `excludePastCompanies` | People who used to work at any of these companies are left out. LinkedIn company page links or numeric company IDs. | none |
| **Pause between batches** (Advanced) | `pauseSeconds` | Seconds to wait between batches of profile lookups, at least 1. The real wait is random, between this and twice this. Only used for Full rows. Raise it if LinkedIn starts slowing you down. | 3 |

A filter value that is a place, title or school is looked up with LinkedIn's own suggestions before the first search. The run log prints what each value was matched to, so a wrong match is easy to spot and fix. A filter that would match nobody is dropped by LinkedIn, which then returns a broader set; the Actor notices this, saves nothing from that slice and names the filter in the report.

### Short, Full and public profiles

| | Short | Full | Full with public profiles |
|---|---|---|---|
| Where the data comes from | The search results | Sales Navigator's profile data, fetched in batches | Both of those, plus each person's public LinkedIn profile |
| Row layout | 11 fields | 52 fields | The same 52 fields, with more of them filled |
| Extra requests | None | About one for every 25 people | One for every person, opened every 4 to 8 seconds |
| Events charged per row | `employee` | `employee` and `full-profile` | `employee`, `full-profile` and `public-profile` |
| Public LinkedIn pages opened (profile views) | None | None | One for every person |

**Short** rows have the person's name, summary, open-profile and Premium flags, location, picture and current positions (title, company, company link, start date, time in the role and at the company). It is the cheapest and fastest way to list a company.

**Full** rows add headline, about, photo, the full position history, education, skills, languages, connection count and the contact details LinkedIn shows you. Email addresses are only filled for people whose contact details LinkedIn shows you, usually your 1st-degree connections. The Actor doesn't find or guess emails.

**Public profiles** add what only the public LinkedIn profile carries: certifications, projects, volunteering, courses, publications, patents, honors and awards, organizations, and fuller positions, education and skills. Because the Actor really opens each person's public profile, **LinkedIn counts every one as a profile view from your account.** Turn on LinkedIn's private viewing mode first, so your name stays out of their "who viewed your profile" list. Public profiles are opened slowly, one every 4 to 8 seconds, and **Max public profiles** (300 by default) caps how many a run opens. Past the cap, rows continue with their Sales Navigator details. Public profiles work with Full rows only.

### Pricing

You pay per event. The exact prices are on this Actor's **Pricing** tab. There may also be a small start fee, charged once when a run starts; if there is one, the Pricing tab shows it.

| Event | When you're charged |
|---|---|
| Employee (`employee`) | Once for every person saved to your dataset. |
| Full profile (`full-profile`) | Once more for each Full row whose Sales Navigator profile was fetched. If LinkedIn refused the profile for a person, that row is saved with the reason and you pay only the employee charge. |
| Public profile (`public-profile`) | Once for each public profile LinkedIn returns, charged when it comes back. A profile LinkedIn refuses or can't find isn't charged: that row is saved with its Sales Navigator details and the reason in `_meta.errors`. Only when **Add public profile sections** is on. |

With Short rows, 1,000 people are 1,000 `employee` events. With Full rows and every profile fetched, they are 1,000 `employee` events plus 1,000 `full-profile` events, and with public profiles on, one `public-profile` event more for each public profile LinkedIn returns (1,000 at most).

**Maximum cost.** Set a maximum cost for the run before you start it. The Actor works out how many rows that pays for and stops itself there, keeping everything it saved. If you turn public profiles on, they stop being opened once the limit is reached, and the remaining rows are saved without them. If the limit can't pay for a single row, the run fails straight away, before it contacts LinkedIn. The run also stops at **Max items, in total** and **Max items per company**, whichever comes first.

**Check only charges nothing but the start fee.** It counts every company and estimates the run without saving or charging any row.

A run that fails because the input or the session can't be used saves no rows, so nothing is charged. A start fee, if the Pricing tab shows one, still applies.

**A new run charges again.** Every run starts from the first person of each company and charges for every row it saves, including rows an earlier run already saved. It doesn't look at your earlier runs. To avoid paying twice, narrow the new run with filters (locations, seniority, **Recently changed jobs**) so it covers only what's still missing. To merge datasets from different runs and remove duplicates, match on `_meta.salesNavId`: it is the same person in every run. The `id` field is not safe for this, because with public profiles on it changes from the Sales Navigator ID to LinkedIn's public ID.

### Output

Results go to the run's dataset. On the **Output** tab, the **Short** and **Full** views show them as tables. Both views list every row of the run (Apify views can't filter rows), so they differ only in their columns. You can download the rows as CSV, Excel or JSON, or read them through the API.

Every key is always present. A value no source has is `null` for a single value and `[]` for a list, so every row of a run has the same shape.

Every row ends with `_meta`:

- `company`: the company the person was found under (`id`, `name`, `linkedinUrl`).
- `slice`: the part of the company's search the person was found in, for example `Germany / Berlin`.
- `sources`: where the row's data came from: `search`, `profile` and `public-profile`.
- `errors`: what failed for this person, if anything, for example `full profile unavailable: HTTP 404`. Empty when nothing did.
- `pagination`, `query` and `scrapedAt`: where the person sat in the company's search, the input fields used, and when the row was made.
- `salesNavId`: the person's stable Sales Navigator ID.

Example rows (made-up data, shortened). A Short row:

```json
{
  "id": "ACwAAA00000001xyz",
  "linkedinUrl": "https://www.linkedin.com/in/ACwAAA00000001xyz",
  "firstName": "Jane",
  "lastName": "Example1",
  "summary": "Builds robots.",
  "openProfile": false,
  "premium": true,
  "currentPositions": [
    {
      "title": "Head of Sales",
      "companyName": "Acme Robotics",
      "current": true,
      "startedOn": {"month": 3, "year": 2022},
      "companyId": "4242",
      "companyLinkedinUrl": "https://www.linkedin.com/company/4242"
    }
  ],
  "pictureUrl": "https://media.licdn.com/dms/image/example/profile-displayphoto-shrink_400_400/0/1",
  "location": {"linkedinText": "Berlin, Germany"},
  "_meta": {
    "pagination": {"totalElements": 1352, "totalPages": 55, "pageNumber": 1, "previousElements": 0, "pageSize": 25},
    "query": {"currentCompanies": ["https://www.linkedin.com/company/example"]},
    "scrapedAt": "2026-10-22T09:30:00Z",
    "company": {"id": "4242", "name": "Acme Robotics", "linkedinUrl": "https://www.linkedin.com/company/4242"},
    "slice": "Germany / Berlin",
    "sources": ["search"],
    "errors": [],
    "salesNavId": "ACwAAA00000001xyz"
  }
}
```

A Full row has the same start and the 52 fields in place of the 11, for example:

```json
{
  "id": "ACwAAA00000001xyz",
  "publicIdentifier": "jane-example1",
  "linkedinUrl": "https://www.linkedin.com/in/jane-example1",
  "firstName": "Jane",
  "lastName": "Example1",
  "emails": [],
  "headline": "Head of Sales at Acme Robotics",
  "location": {"linkedinText": "Berlin, Germany", "countryCode": "DE"},
  "experience": [{"position": "Head of Sales", "companyName": "Acme Robotics", "duration": "3 yrs 7 mos"}],
  "education": [{"schoolName": "Example University", "degree": "BA"}],
  "skills": [{"name": "Sales"}],
  "certifications": [],
  "sectionTotals": {"experience": 4, "education": 1, "skills": 12, "certifications": 0},
  "_meta": {"slice": "Germany / Berlin", "sources": ["search", "profile"], "errors": []}
}
```

The keys that none of the sources carry (`hiring`, `registeredAt`, `topSkills`, `followerCount`, `verified`, `profileActions`, `services`, `receivedRecommendations`, `causes`, `featured`, `composeOptionType`, `moreProfiles` and `interests`) are in every Full row and stay empty.

### Limits

- **Only people Sales Navigator lists for the company.** Sales Navigator, and its count, include a person only when their profile links the company's LinkedIn page. Someone who writes the company's name in their headline without linking the page isn't included, and isn't counted in `found X of Y` either.
- **Some people can be out of reach.** Location slices add up exactly. When one location is still too big to page, the Actor slices it by function and then by seniority, and LinkedIn files a few people under none of those values. The Actor compares each such split with its count and lists the shortfall as a gap with the slice it sits in; on a very large slice LinkedIn's counts change slightly between requests, so the shortfall is given as an upper bound. A slice that is still over 2,500 after that can't be paged to its end, and the rest of it is listed as a search-cap gap. So is a slice whose paging reaches the 2,500 wall on a full page, with at least 1 person: LinkedIn's count can be low, and nobody can page or count past the wall, so the line says the slice may hold more past LinkedIn's 2,500 limit. A slice whose list moves while it is paged (a page repeats someone) is paged a second time from the start, in full, since the people past a low count could hide the ones the move pushed out. Two cases are still invisible: a move that repeats the same way on both passes, and people who leave ahead of the page being read while LinkedIn's count for the slice is too low (the people LinkedIn didn't count, past the count, take their place). Both need people to join or leave during the minutes a slice is paged. A person LinkedIn lists without an ID can't be told apart from anyone else, so a move that repeats only such a person isn't seen, and that person can appear in two rows. A slice that comes back short of its count after being paged a second time is listed too, and so is a slice where LinkedIn dropped a filter that matched nobody. LinkedIn also reports the count of a very large company only approximately: a remainder of up to 0.5% of the count (at least 3 people) that no gap explains isn't listed separately, and a larger one is reported as count drift. The `found X of Y` line always shows the difference.
- **Fields no source provides are empty.** See the list under Output.
- **Raise the run timeout for big companies.** Apify ends a run when its timeout is reached and keeps the rows it saved, and a new run starts over and charges again for every row it saves. A company of about 40,000 people takes hours at the default pace, so raise the timeout in the run options before you start.
- **Large companies take time.** A run asks for one count per slice, one page for every 25 people, and for Full rows about one more request for every 25 people. A slice whose last page is full takes one page more, to look for people past LinkedIn's count, and a slice whose list moves while it's paged is paged a second time in full. Public profiles add one request each, opened 4 to 8 seconds apart. Use **Check only** to see the estimate, which covers up to **Max items, in total**, before you commit.
- **Your maximum cost.** The run caps itself at what your maximum cost can pay for, as described under Pricing.
- **One IP per run.** The Actor uses one residential IP for the whole run. Apify keeps an IP for about 30 minutes, so a longer run changes IP partway through, which LinkedIn may notice. For big jobs, run one company at a time.
- **Rate limits slow or stop the run.** If LinkedIn slows the session down, the Actor waits and retries. If that doesn't help, it stops and keeps what it saved. Start a new run later; it starts over (see Pricing).
- **Public profiles count as profile views.** Turn on LinkedIn's private viewing mode first, and keep **Max public profiles** modest.
- **One run at a time per LinkedIn login.** Parallel runs on one login are a quick way to get logged out.

**Your LinkedIn account.** The Actor uses your own LinkedIn account and your own session. LinkedIn's User Agreement prohibits automated access, and heavy use can get your account restricted or logged out. The Actor can't prevent that. Keep volumes moderate, prefer small batches over large ones, don't lower the pause between batches, and use a proxy in the country you normally log in from. You're responsible for how you use your account.

### FAQ

#### How do I know I got everyone?

Read the `found X of Y` line for each company in the run's final status and log. `Y` is the number of people Sales Navigator says match, raised to what the Actor's slices actually hold when Sales Navigator's headline number is lower, and `X` is how many the Actor found, so any difference is visible. The line lists the gaps the Actor can account for, each with the slice it sits in and the reason: for example people LinkedIn leaves out of a function or seniority slice, a slice that came back short, a slice past the 2,500 search cap, or a filter that matched nobody. On a very large company a remainder of up to 0.5% of the count (at least 3 people) that no gap explains isn't listed, because LinkedIn's count there is approximate; a larger one is listed as count drift. A run that stopped at a limit you set says so.

`found` also counts people who were already saved under an earlier company of the same run, since each person's row is saved only once. So the rows in your dataset can be fewer than the sum of all `found` numbers.

#### The run says LinkedIn ended the session. What now?

LinkedIn logged your session out, so it won't accept more requests. The run stops and keeps what it saved. Log in to Sales Navigator in Chrome again, check LinkedIn for any security notice, wait a while, then copy a fresh request and start a new run (or update the session in your task).

#### My run stopped early. Why?

The status message always says why. A run that saved at least one row ends as Succeeded, even if it stopped early. A **Check only** run ends as Succeeded without saving anything, and so does a run where none of your companies had a match. Any other run that saved nothing ends as Failed. If you stop a run yourself, Apify marks it Aborted.

- **Your spending limit was reached.** Everything saved up to that point is kept.
- **You hit Max items.** The run stops cleanly at your limit and says so.
- **LinkedIn is rate-limiting the session**, or the connection kept failing. Wait a few hours before the next run, and use smaller batches.
- **You stopped the run.** Results saved so far stay saved and charged.
- **Something unexpected went wrong**, for example LinkedIn changed the shape of its answers. The run log has the details. If it keeps happening, report it on the **Issues** tab.

Before you start a new run to get the rest, remember that it starts over: it charges for every row it saves, including rows an earlier run already saved. Narrow its filters to what's still missing.

#### The run failed straight away with a message about my input or session. What's wrong?

The Actor checks your input before it contacts LinkedIn, so a run that fails here saves and charges no rows. The message says what to fix:

- A message that starts with `companies:` means the **Companies** field is empty. Add at least one company.
- A message that names another field, for example `seniorityLevelIds:` or `maxPublicProfiles:`, says what that field expects.
- `Paste your LinkedIn session (Copy as cURL) first.` The session field is empty.
- `Paste the request copied with Copy as cURL (bash); it starts with curl '…` You pasted something else, such as a cookie export, a file path or a link.
- `Paste a fresh Copy as cURL (bash) from Chrome: …` The request couldn't be used. The rest of the message says why: it may be the Windows format (use the bash version), cut off, missing your login, or from a session LinkedIn has already ended. Copy a new one.

#### What happens if Apify restarts my run?

The run carries on where it stopped. Rows already saved aren't saved or charged again. This applies only to the same run; a new run you start yourself starts over (see Pricing).

#### Can I get email addresses?

Only the ones Sales Navigator shows you, in Full rows. The Actor doesn't look emails up anywhere else.

#### Does this collect personal data?

Yes. The results are names, jobs and profile details of real people. You're responsible for using them in line with LinkedIn's terms and the privacy laws that apply to you.

# Actor input Schema

## `companies` (type: `array`):

The companies to collect employees for, one per line: a LinkedIn company page link (linkedin.com/company/…), a Sales Navigator company link, a numeric company ID or a company name. Every company is searched on its own. A person who works at two of your companies is saved once, under the first.

## `browserRequest` (type: `string`):

Copy your session from Chrome:<br>1. Open a search in Sales Navigator in Chrome.<br>2. Open DevTools (⌥⌘I on Mac, Ctrl+Shift+I on Windows), open the Network tab and filter <code>salesApi</code>.<br>3. Reload, right-click a <code>salesApiLeadSearch</code> row, and choose Copy → Copy as cURL (bash).<br>4. Paste the result here. It holds your login; it's stored encrypted.

## `profileScraperMode` (type: `string`):

Short rows come straight from the search results and cost the least. Full rows also fetch each person's Sales Navigator profile: slower, and it adds a charge for every row that gets one.

## `publicProfiles` (type: `boolean`):

Also open each person's public LinkedIn profile, for the sections Sales Navigator doesn't carry (certifications, projects, volunteering, courses, publications, patents, honors and awards, organizations) and for fuller positions, education and skills. Full rows only. Each public profile counts as a profile view on your LinkedIn account, so turn on LinkedIn's private viewing mode before you start. They are opened one every 4 to 8 seconds, and every one LinkedIn returns adds a charge.

## `maxPublicProfiles` (type: `integer`):

The most public profiles to open in one run. After that, rows are still saved, with their Sales Navigator details only. Only used when public profile sections are on.

## `maxItems` (type: `integer`):

Stop after this many rows, counted across all your companies. Use 0 for no limit.

## `maxItemsPerCompany` (type: `integer`):

Stop each company after this many rows. Use 0 for no limit.

## `checkOnly` (type: `boolean`):

Count each company and estimate the run without saving or charging rows. The status line and the log show how many people match and how many requests a full run needs.

## `proxy` (type: `object`):

Residential proxies are used by default. Choose your own country so the run looks like it comes from where you normally log in to LinkedIn.

## `locations` (type: `array`):

Place names or LinkedIn geo IDs, for example Germany or Berlin. Only people located there are included. Each name is matched to LinkedIn's closest suggestion, and the run log shows which one was used.

## `excludeLocations` (type: `array`):

Places to leave out. Same format as Locations.

## `searchQuery` (type: `string`):

Words to look for in people's profiles. LinkedIn's boolean operators (AND, OR, NOT and quotes) work.

## `jobTitles` (type: `array`):

Titles people hold now, for example Head of Sales. Each is matched to LinkedIn's closest title suggestion; a title with no match is searched as written.

## `excludeCurrentJobTitles` (type: `array`):

Titles to leave out. Same format as Current job titles.

## `pastJobTitles` (type: `array`):

Titles people held earlier in their career.

## `excludePastJobTitles` (type: `array`):

Past titles to leave out.

## `schools` (type: `array`):

School names or LinkedIn school IDs. Only people who studied there are included.

## `excludeSchools` (type: `array`):

Schools to leave out. Same format as Schools.

## `industryIds` (type: `array`):

Numeric LinkedIn industry IDs. Choose an industry in a Sales Navigator search and read its ID from the address bar.

## `excludeIndustryIds` (type: `array`):

Industries to leave out. Same format as Industry IDs.

## `seniorityLevelIds` (type: `array`):

Only include people at these seniority levels.

## `excludeSeniorityLevelIds` (type: `array`):

Leave out people at these seniority levels.

## `functionIds` (type: `array`):

Only include people who work in these functions.

## `excludeFunctionIds` (type: `array`):

Leave out people who work in these functions.

## `yearsAtCurrentCompanyIds` (type: `array`):

Only include people who have been at the company this long.

## `yearsOfExperienceIds` (type: `array`):

Only include people with this much experience in total.

## `companyHeadcount` (type: `array`):

Only include people whose current company has a headcount in these ranges.

## `recentlyChangedJobs` (type: `boolean`):

Only include people who changed jobs recently, as Sales Navigator defines it.

## `excludePastCompanies` (type: `array`):

People who used to work at any of these companies are left out. LinkedIn company page links or numeric company IDs.

## `pauseSeconds` (type: `integer`):

Seconds to wait between batches of profile lookups. The real wait is random, between this and twice this. Only used for Full rows. Raise it if LinkedIn starts slowing you down.

## Actor input object example

```json
{
  "profileScraperMode": "Full",
  "publicProfiles": false,
  "maxPublicProfiles": 300,
  "maxItems": 100,
  "maxItemsPerCompany": 0,
  "checkOnly": false,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "recentlyChangedJobs": false,
  "pauseSeconds": 3
}
```

# Actor output Schema

## `results` (type: `string`):

All rows of the run, with the Short and Full table views.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("rayackery/salesnav-company-employees").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    } }

# Run the Actor and wait for it to finish
run = client.actor("rayackery/salesnav-company-employees").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call rayackery/salesnav-company-employees --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,rayackery/salesnav-company-employees"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/F0dbuLMVmJFUdagJP/builds/bYIH9cmD2DmVeDWvk/openapi.json
