# LinkedIn Jobs Scraper – Jobs, Companies & Recruiters (`coregent/linkedin-jobs-scraper`) Actor

Scrape LinkedIn jobs by keyword, location, or search URL. Get full job descriptions, company details, recruiter/job-poster data, salaries, applicant counts, and structured output. No LinkedIn login or cookies required.

- **URL**: https://apify.com/coregent/linkedin-jobs-scraper.md
- **Developed by:** [Delowar Munna](https://apify.com/coregent) (community)
- **Categories:** Jobs, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 job results

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## LinkedIn Jobs Scraper – Jobs, Companies & Recruiters

![LinkedIn Jobs Scraper – Jobs, Companies & Recruiters](https://raw.githubusercontent.com/coregentdevspace/linkedin-jobs-scraper-assets/main/thumbnail-linkedin-jobs-scraper.png)

Scrape LinkedIn jobs by keyword, location, or search URL. Get full job descriptions, company details, recruiter/job-poster data, salaries, applicant counts, and structured output. **No LinkedIn login or cookies required.**

### What does this actor do?

It searches LinkedIn Jobs the way you would — by keyword and location, or from a search URL you already built — and returns every result as a clean, structured record. For each job you get the full description, the employer, where it is, when it was posted, how many people have applied, the salary when LinkedIn publishes one, and the recruiter or job poster when one is shown publicly.

You never supply a LinkedIn username, password, cookie, `li_at` token, or session ID. The actor only reads pages that are public to anyone on the internet.

### Why use this actor?

- **Search the way you already do** — keywords plus locations, or paste LinkedIn search URLs.
- **Full job descriptions**, not just the card preview.
- **Company enrichment** — website, domain, industry, size, employee count, headquarters, founding year.
- **Recruiter / job-poster details** when LinkedIn shows them publicly, at no extra charge.
- **Automatic deduplication** across every keyword and location in the run.
- **Incremental scraping** — pass the job IDs you already have and they are skipped before they cost anything.
- **Every keyword x every location** in a single run, with full traceability back to the search that found each job.
- **No login, no cookies, no proxy setup required.**

### What data do you get?

| Group | Fields |
|---|---|
| Job | `jobId`, `jobUrl`, `title`, `descriptionText`, `descriptionHtml`, `applyUrl`, `postedAt`, `listedAt` |
| Employer | `companyName`, `companyLinkedInUrl`, `companyId` |
| Classification | `location`, `workplaceType`, `employmentType`, `seniorityLevel`, `jobFunctions`, `industries` |
| Demand | `applicantCount`, `easyApply` |
| Pay | `salary.min`, `salary.max`, `salary.currency`, `salary.period`, `salary.text` |
| Recruiter | `recruiter.name`, `recruiter.title`, `recruiter.linkedinUrl`, `recruiter.profileImageUrl` |
| Company (opt-in) | `company.website`, `company.domain`, `company.industry`, `company.companySize`, `company.employeeCount`, `company.headquarters`, `company.companyType`, `company.foundedYear`, `company.description`, `company.logoUrl` |
| Contact (opt-in) | `emails` |
| Traceability | `searchMetadata.source`, `.keyword`, `.location`, `.searchUrl`, `.parentSearchId`, `.segment`, `.provider` |
| Run | `scrapedAt` |

Fields LinkedIn does not publish are returned as `null`. Values are never invented.

### How to search

Provide **Search Keywords** and optionally **Locations**. Every keyword is combined with every location, so 3 keywords x 2 locations runs 6 searches and merges the results with deduplication.

```json
{
  "searchKeywords": ["Software Engineer"],
  "locations": ["Sydney, Australia"],
  "maxResults": 100
}
```

### Using LinkedIn search URLs

Already built a search on LinkedIn? Paste the URL. Filters in the URL are preserved, tracking parameters are stripped, and duplicates are removed. You can also paste individual job URLs (`linkedin.com/jobs/view/...`) to scrape those postings directly.

```json
{
  "searchUrls": [
    "https://www.linkedin.com/jobs/search/?keywords=data%20analyst&location=Melbourne",
    "https://www.linkedin.com/jobs/view/4443029595/"
  ],
  "maxResults": 200
}
```

### Filters

Standard: **Posted Within**, **Workplace** (remote / hybrid / on-site), **Employment Type**, **Experience Level**, **Easy Apply**, **Under 10 Applicants**, **Sort By**.

Advanced: include/exclude companies, exclude job-title keywords, exclude locations, job functions, industries, salary range and currency, applicant range, company employee-count range, and exclude recruitment agencies.

LinkedIn does not always honour its own filter parameters, so every filter is **re-checked against the scraped data** before a job is saved. See [Limitations](#limitations) for which filters are best-effort.

### Company enrichment

Enable **Include Company Details** to add employer data. Enrichment is charged **once per unique company, not once per job** — a company appearing in 50 jobs is charged once. Failed lookups are not charged and never remove the job.

### Recruiter enrichment

Enable **Include Recruiter Details** (on by default) to capture the job poster or hiring-team member LinkedIn shows publicly: name, headline, profile URL, and photo. This costs nothing extra because it is read from the job page already being fetched.

**Expect roughly 1 in 5 jobs to name a recruiter** — measured at 16–20% across live runs. LinkedIn simply does not attach a public poster to most postings, so an empty `recruiter` field is that job's reality rather than a scraping failure. The actor reports what is public and never guesses.

### Deduplication and incremental runs

Duplicates are removed by job ID, then job URL, then title + company + location. For scheduled runs, pass the IDs you already collected in **Skip Job IDs** — matching jobs are skipped *before* their page is fetched, so they cost you nothing.

```json
{
  "searchKeywords": ["Product Manager"],
  "locations": ["Remote"],
  "skipJobIds": ["4219847745", "4219848811"],
  "maxResults": 500
}
```

### Large search mode

Enable **Expand Large Searches** when a broad search hits LinkedIn's result ceiling. The actor splits it into smaller sub-searches, merges the results, and deduplicates globally. Your **Maximum Jobs** limit still applies to the whole run.

### Example inputs

#### 1. Simplest possible run

```json
{
  "searchKeywords": ["Software Engineer"],
  "locations": ["Sydney, Australia"],
  "maxResults": 100
}
```

#### 2. Lead generation — direct employers with company data

Skip staffing agencies, keep mid-sized companies, and pull employer details plus the job poster.

```json
{
  "searchKeywords": ["Software Engineer", "Backend Engineer"],
  "locations": ["Sydney, Australia", "Melbourne, Australia"],
  "maxResults": 1000,
  "postedWithin": "pastWeek",
  "workplaceTypes": ["remote", "hybrid"],
  "employmentTypes": ["fullTime", "contract"],
  "experienceLevels": ["midSenior"],
  "excludeJobTitleKeywords": ["Manager", "Director"],
  "excludeRecruitmentAgencies": true,
  "minimumCompanyEmployees": 20,
  "maximumCompanyEmployees": 5000,
  "removeDuplicates": true,
  "includeCompanyDetails": true,
  "includeRecruiterDetails": true,
  "expandLargeSearches": true
}
```

#### 3. Daily monitoring — only jobs you haven't seen

Pass back the `jobId` values from yesterday's run. Matching jobs are skipped before their page is fetched, so repeats cost you nothing.

```json
{
  "searchKeywords": ["Product Manager"],
  "locations": ["Remote"],
  "postedWithin": "past24Hours",
  "maxResults": 500,
  "skipJobIds": ["4219847745", "4219848811", "4438759861"],
  "removeDuplicates": true
}
```

#### 4. Scrape a search URL you already built

```json
{
  "searchUrls": [
    "https://www.linkedin.com/jobs/search/?keywords=data%20analyst&location=Melbourne&f_TPR=r604800",
    "https://www.linkedin.com/jobs/view/4438759861/"
  ],
  "maxResults": 200,
  "extractPublicEmails": true
}
```

### Example output

One dataset item per job. Every example below is a **real record from a live run** — nothing is illustrative. Fields LinkedIn does not publish come back `null`; values are never invented.

```json
{
  "jobId": "4438759861",
  "jobUrl": "https://www.linkedin.com/jobs/view/4438759861/",
  "title": "Software Engineer",
  "companyName": "Verse",
  "companyLinkedInUrl": "https://www.linkedin.com/company/verse-group---recruitment-specialists",
  "companyId": "verse-group---recruitment-specialists",
  "location": "Greater Perth Area",
  "workplaceType": null,
  "employmentType": "Full-time",
  "seniorityLevel": "Mid-Senior level",
  "postedAt": "2026-07-17T00:00:00.000Z",
  "listedAt": null,
  "applicantCount": 200,
  "easyApply": true,
  "salary": {
    "min": 110000,
    "max": 130000,
    "currency": "AUD",
    "period": "YEAR",
    "text": "A$110,000.00/yr - A$130,000.00/yr"
  },
  "jobFunctions": ["Information Technology"],
  "industries": ["Software Development"],
  "descriptionText": "About the Role. An exciting opportunity is available for a Software Engineer to join a growing software development team based in Perth. You'll work across both backend and frontend development using technologies including Java, Spring Boot, Python …",
  "descriptionHtml": "<p><strong>About the Role</strong></p><p>An exciting opportunity is available for a Software Engineer …</p>",
  "applyUrl": "https://www.linkedin.com/jobs/view/4438759861/",
  "recruiter": {
    "name": "Matt Burke",
    "title": "2024 Sourcr Best Recruiter WA - IT/ Telecommunications | Division Manager - Applications at Verse Group",
    "linkedinUrl": "https://www.linkedin.com/in/matt-burke-perth",
    "profileImageUrl": "https://media.licdn.com/dms/image/v2/D5603AQECuO74T8q5TA/profile-displayphoto-scale_400_400/…"
  },
  "company": {
    "website": "http://www.versegroup.com.au",
    "domain": "versegroup.com.au",
    "industry": "Staffing and Recruiting",
    "companySize": "51-200 employees",
    "employeeCount": 28,
    "headquarters": "Perth, Western Australia",
    "companyType": "Privately Held",
    "foundedYear": 2013,
    "description": "Verse is an innovative, agile boutique recruitment business specialising in niche tech skills …",
    "logoUrl": "https://media.licdn.com/dms/image/v2/D560BAQEBvgF08-iP7A/company-logo_200_200/…"
  },
  "emails": ["matthew.burke@versegroup.com.au"],
  "searchMetadata": {
    "source": "structured_search",
    "keyword": "Software Engineer",
    "location": "Australia",
    "searchUrl": null,
    "parentSearchId": null,
    "segment": null,
    "provider": "linkedin_public"
  },
  "scrapedAt": "2026-08-12T06:12:25.335Z"
}
```

`companySize` is the employer's self-reported band; `employeeCount` is how many members LinkedIn actually lists. They legitimately differ — both are reported as published rather than reconciled.

### Output views

The dataset ships five ready-made views. Switch between them with the tabs above the results table, or append `?view=<name>` to the dataset API URL. Each sample below is the same real job seen through a different view.

#### Jobs overview

The fields most runs need — role, employer, location, recency, and how to apply.

```json
{
  "title": "Software Engineer",
  "companyName": "Verse",
  "location": "Greater Perth Area",
  "postedAt": "2026-07-17T00:00:00.000Z",
  "workplaceType": null,
  "employmentType": "Full-time",
  "applicantCount": 200,
  "salary.text": "A$110,000.00/yr - A$130,000.00/yr",
  "recruiter.name": "Matt Burke",
  "recruiter.linkedinUrl": "https://www.linkedin.com/in/matt-burke-perth",
  "jobUrl": "https://www.linkedin.com/jobs/view/4438759861/"
}
```

#### Full job data

Every field, including the complete job description and the search trace — the same shape as the Example output above.

#### Company details

Employer data, populated when **Include Company Details** is enabled.

```json
{
  "companyName": "Verse",
  "companyLinkedInUrl": "https://www.linkedin.com/company/verse-group---recruitment-specialists",
  "companyId": "verse-group---recruitment-specialists",
  "company.website": "http://www.versegroup.com.au",
  "company.domain": "versegroup.com.au",
  "company.industry": "Staffing and Recruiting",
  "company.employeeCount": 28,
  "company.companySize": "51-200 employees",
  "company.headquarters": "Perth, Western Australia",
  "company.foundedYear": 2013,
  "jobUrl": "https://www.linkedin.com/jobs/view/4438759861/"
}
```

#### Recruiters

Job posters and hiring-team members shown publicly on the job page.

```json
{
  "jobId": "4438759861",
  "title": "Software Engineer",
  "companyName": "Verse",
  "recruiter.name": "Matt Burke",
  "recruiter.title": "2024 Sourcr Best Recruiter WA - IT/ Telecommunications | Division Manager - Applications at Verse Group",
  "recruiter.linkedinUrl": "https://www.linkedin.com/in/matt-burke-perth",
  "jobUrl": "https://www.linkedin.com/jobs/view/4438759861/"
}
```

#### Search trace

Which search produced each job — essential when one run covers many keywords, locations, or URLs.

```json
{
  "jobId": "4438759861",
  "jobUrl": "https://www.linkedin.com/jobs/view/4438759861/",
  "searchMetadata.source": "structured_search",
  "searchMetadata.keyword": "Software Engineer",
  "searchMetadata.location": "Australia",
  "searchMetadata.searchUrl": null,
  "searchMetadata.parentSearchId": null,
  "searchMetadata.segment": null,
  "searchMetadata.provider": "linkedin_public"
}
```

#### How complete is the data?

Measured on a real 50-job run with company enrichment enabled:

| Field | Populated | Why |
|---|---|---|
| `title`, `companyName`, `location`, `postedAt`, `jobUrl`, `applyUrl` | **100%** | Always available |
| `descriptionText`, `employmentType`, `seniorityLevel`, `jobFunctions`, `industries`, `applicantCount` | **100%** | Read from the job page |
| `company` | **100%** | When Include Company Details is on |
| `workplaceType` | ~44% | Only when the posting states remote / hybrid / on-site |
| `salary` | ~20% | Most LinkedIn jobs publish no salary |
| `recruiter` | ~16% | LinkedIn names a public job poster on a minority of postings |
| `emails` | ~14% | Only when an address appears in the description |

The last four reflect what LinkedIn publishes, not extraction failures — where the value exists on the page, it is captured.

### Pricing

This actor uses Apify's **pay-per-event** pricing:

| Event | When it is charged |
|---|---|
| `job-result` | Once per unique job saved to the dataset. |
| `company-enrichment` | Once per **unique company** successfully enriched — not once per job. |

**Never charged:** duplicates, jobs removed by filters, jobs matched by Skip Job IDs / Skip URLs, failed extractions, failed company lookups, and recruiter data (which is bundled into the job result at no extra cost).

There is **no Actor-start fee** — a run that returns nothing costs nothing.

This README deliberately quotes no figures: current rates are shown on the actor's **Pricing** tab, which is always the live source.

#### Free plan limitations

Nothing is switched off on the Apify Free plan. Every job field — including full descriptions, company details and recruiter data — is returned exactly as it is on a paid plan.

The single difference: when LinkedIn blocks part of a run, the actor can fall back to a paid backup data provider to recover those jobs, and that backup has a smaller budget on the Free plan. If it is ever reached the run continues normally with a status message, is never failed, and you are never charged for it. Paid plans get the larger budget because the backup provider bills us per request.

### 🚦 Proxy policy

Use **Apify Datacenter** proxy or **no proxy** for normal runs — both work reliably for LinkedIn's public job pages at this actor's conservative request rate.

**Apify Residential proxy is not supported.** The actor fails at startup if `apifyProxyGroups` includes `RESIDENTIAL`. Reason: in pay-per-event actors, residential bandwidth is billed to the developer rather than to you, so a single bandwidth-heavy run could exceed the per-result revenue.

If you genuinely need residential routing, supply your own provider via the proxy editor's **Custom proxy URLs** field — that traffic goes through your provider, not Apify, and is unaffected:

```
http://user:pass@proxy.iproyal.com:12321
http://user:pass@proxy.brightdata.com:22225
http://user:pass@proxy.oxylabs.io:7777
```

### API example

```bash
curl -X POST "https://api.apify.com/v2/acts/coregent~linkedin-jobs-scraper/runs?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "searchKeywords": ["Software Engineer"],
    "locations": ["Sydney, Australia"],
    "maxResults": 100
  }'
```

Results are available from the run's dataset endpoint. Every run also writes a `RUN_SUMMARY` record to the default key-value store:

```json
{
  "searchesPlanned": 1,
  "searchesCompleted": 1,
  "rawJobsFetched": 120,
  "duplicatesRemoved": 3,
  "jobsFilteredOut": 0,
  "jobsSaved": 100,
  "companiesEnriched": 0,
  "recruitersEnriched": 20,
  "errors": 0,
  "jobsSkippedByIncremental": 0,
  "detailPagesVisited": 115,
  "requestsAttempted": 130,
  "retriedRequests": 2,
  "throttled429s": 2,
  "blockedRequests": 0,
  "runtimeSeconds": 64
}
```

`blockedRequests` counts requests that failed for good. `throttled429s` counts requests LinkedIn rate-limited — these are normally retried successfully, so a small number here with `blockedRequests: 0` means your results are complete. If throttling climbs above a few percent of `requestsAttempted`, the run logs a warning telling you to slow it down.

`recruitersEnriched` and `companiesEnriched` count what was **delivered in your dataset**, so they reconcile against the rows you receive. `detailPagesVisited` counts work done and is normally slightly higher than `jobsSaved`.

### Performance

A 100-job run completes in about a minute. The figures above are from a real run: 100 jobs saved in **64 seconds**, with 0 errors and 0 blocked requests.

The actor collects only a small margin beyond your **Maximum Jobs** limit, and widens that margin automatically when you set filters that can only be judged after the job page is read (workplace, employment type, experience level, salary, applicants, company size). So an unfiltered run does close to the minimum work needed, and a heavily filtered run fetches enough to still fill your limit.

### Limitations

- **Public data only.** Anything LinkedIn hides behind a login is not available, by design.
- **Recruiter data is sparse — about 1 in 5 jobs.** Measured at 16–20% across live runs. LinkedIn only attaches a public job poster to some postings.
- **Public emails are rarer still.** Most employers route applications through LinkedIn instead of publishing an address, so `emails` is usually empty. Measured on recruitment-agency listings — the densest case — only about 1 in 3 contained one; on general roles it is far lower. Every address that does appear in a description is captured.
- **Applicant counts are best-effort.** LinkedIn often renders a bucket such as "Over 200 applicants" instead of an exact figure. Jobs with no exact count are kept, so **Under 10 Applicants**, **Min Applicants** and **Max Applicants** are approximate.
- **Salary is only as good as the posting.** Most LinkedIn jobs publish no salary. Jobs without one are kept unless you enable **Exclude Jobs Without Salary**. Salary is never estimated.
- **`postedAt` can be approximate.** LinkedIn often shows a relative date ("2 weeks ago"), which resolves to a day, not an exact time.
- **Filter parameters are hints.** LinkedIn does not reliably honour its own URL filter parameters, so every filter is re-checked against the scraped data. Jobs missing the relevant field are kept rather than silently dropped.
- **Recruitment-agency exclusion is a heuristic** based on company naming patterns, plus the company's LinkedIn industry when company enrichment is enabled.
- **Result ceiling.** LinkedIn caps how deep any single search can page. Use **Expand Large Searches** for very broad queries.

### FAQ

**Do I need a LinkedIn account, cookies, or an `li_at` token?**
No. The actor never asks for credentials and only reads publicly accessible pages.

**Do I need to configure a proxy?**
No. Defaults work. See the [Proxy policy](#-proxy-policy) for the one unsupported option.

**Am I charged for duplicates or filtered-out jobs?**
No. You are charged only for unique jobs actually saved, plus company enrichment if you enable it.

**How do I run this daily without paying for the same jobs twice?**
Collect `jobId` values from each run and pass them back in **Skip Job IDs**. Matching jobs are skipped before their page is fetched.

**Why is `recruiter` empty on so many jobs?**
Because LinkedIn only shows a job poster on some postings — about 1 in 5 in practice. The actor reports what is public and does not fabricate the field.

**Do I need a proxy, and which one?**
Defaults work. Apify Datacenter or no proxy both run reliably; Apify Residential is not supported (see [Proxy policy](#-proxy-policy)).

**What happens if LinkedIn blocks part of my run?**
The actor retries, rotates sessions, and falls back to a backup data provider for any job page it still cannot read, so blocked pages are recovered rather than returned empty. `blockedRequests` in `RUN_SUMMARY` tells you whether this happened.

**Can I get more than a few hundred results for one broad search?**
LinkedIn limits how deep a single search can page. Enable **Expand Large Searches**, or split your query into more specific keywords and locations.

### Legal

You are responsible for complying with applicable laws, with LinkedIn's terms and policies, and with privacy and data-protection rules that apply to the data you collect, including how you store and use personal data such as recruiter names and public email addresses. This actor collects only publicly accessible information and does not access private, logged-in, or paywalled content.

# Actor input Schema

## `searchKeywords` (type: `array`):

Job titles, keywords, skills, or natural-language job searches. Each keyword is combined with each location, so 3 keywords x 2 locations = 6 searches.

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

Cities, regions, or countries to search in. Use "Remote" for remote-only searches. Leave empty to search everywhere.

## `maxResults` (type: `integer`):

Maximum number of unique jobs to save across the whole run. You are charged only for jobs actually saved.

## `searchUrls` (type: `array`):

Paste LinkedIn job search URLs, or individual job URLs (linkedin.com/jobs/view/...). Filters already present in the URL are preserved. Non-LinkedIn URLs are skipped with a warning.

## `postedWithin` (type: `string`):

Only return jobs posted within this window.

## `sortBy` (type: `string`):

Result ordering requested from LinkedIn.

## `workplaceTypes` (type: `array`):

Remote, hybrid, or on-site. Leave empty for all.

## `employmentTypes` (type: `array`):

Full-time, part-time, contract, and so on. Leave empty for all.

## `experienceLevels` (type: `array`):

Seniority levels to include. Leave empty for all.

## `easyApplyOnly` (type: `boolean`):

Only return jobs that can be applied to directly on LinkedIn.

## `underTenApplicantsOnly` (type: `boolean`):

Only return jobs with fewer than 10 applicants. Best-effort: LinkedIn often shows a bucket such as "Over 200 applicants" instead of an exact number, and jobs with no stated count are kept.

## `includeCompanies` (type: `array`):

Only keep jobs from these companies. Accepts company names, LinkedIn company URLs, or company slugs.

## `excludeCompanies` (type: `array`):

Drop jobs from these companies. Exclusion always wins over inclusion.

## `excludeJobTitleKeywords` (type: `array`):

Drop jobs whose title contains any of these words (case-insensitive). Applied before the job page is fetched, so excluded jobs are never charged.

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

Drop jobs whose location contains any of these terms. Useful when LinkedIn broadens results beyond the location you asked for.

## `jobFunctions` (type: `array`):

Job functions to keep, for example Engineering or Marketing. Jobs with no stated function are kept.

## `industries` (type: `array`):

Industries to keep, for example Software Development. Jobs with no stated industry are kept.

## `excludeRecruitmentAgencies` (type: `boolean`):

Drop staffing and recruiting agencies so you see direct employers. Uses company-name patterns, plus the company's LinkedIn industry when company enrichment is enabled. Off by default — agencies are never removed silently.

## `minimumCompanyEmployees` (type: `integer`):

Only keep jobs at companies with at least this many employees. Requires company data, so setting this automatically enables Include Company Details and its per-company charge. Jobs whose company size could not be determined are kept.

## `maximumCompanyEmployees` (type: `integer`):

Only keep jobs at companies with at most this many employees. Requires company data, so setting this automatically enables Include Company Details and its per-company charge.

## `minimumApplicants` (type: `integer`):

Best-effort — jobs with no exact applicant count are kept.

## `maximumApplicants` (type: `integer`):

Best-effort — jobs with no exact applicant count are kept.

## `minimumSalary` (type: `integer`):

Only keep jobs whose stated salary range reaches this figure. Jobs without a published salary are kept unless you enable Exclude Jobs Without Salary.

## `maximumSalary` (type: `integer`):

Only keep jobs whose stated salary range starts at or below this figure.

## `salaryCurrency` (type: `string`):

Restrict salary filtering to one currency. Leave empty to accept any.

## `excludeJobsWithoutSalary` (type: `boolean`):

Drop jobs that do not publish a salary. Most LinkedIn jobs do not, so this reduces results sharply.

## `removeDuplicates` (type: `boolean`):

Remove the same job when it appears under several keywords or locations. Matched by job ID, then job URL, then title + company + location.

## `skipJobIds` (type: `array`):

Job IDs already collected in a previous run. Matching jobs are skipped before their page is fetched, so they cost nothing.

## `skipUrls` (type: `array`):

Previously scraped job URLs. Same effect as Skip Job IDs, for when you stored URLs rather than IDs.

## `includeCompanyDetails` (type: `boolean`):

Add company website, domain, industry, size, employee count, headquarters, and description. Charged once per unique company, not once per job — a company appearing in 50 jobs is charged once. Failed lookups are not charged.

## `includeRecruiterDetails` (type: `boolean`):

Add the job poster / hiring team member when LinkedIn shows one publicly: name, headline, profile URL, and photo. No extra charge — it is read from the job page already being fetched. Most public job posts do not name a recruiter, so this field is often empty.

## `extractPublicEmails` (type: `boolean`):

Collect email addresses that appear publicly in the job description (and the company profile, if company details are enabled). No external email-lookup service is used and nothing extra is charged. Expect most jobs to return an empty list — most employers route applications through LinkedIn rather than publishing an address. Recruitment-agency listings are the most likely to include one.

## `expandLargeSearches` (type: `boolean`):

For broad searches that hit LinkedIn's result ceiling, split the search into smaller location-based sub-searches and merge the results. Your Maximum Jobs limit still applies to the whole run.

## `dataSource` (type: `string`):

Where job data comes from. "Automatic" uses LinkedIn's public pages and falls back to a backup provider only for jobs LinkedIn blocks. On the Apify Free plan the backup provider has a smaller budget; everything else is unaffected and you are never charged for it.

## `proxyConfiguration` (type: `object`):

Apify Proxy settings. Datacenter or no proxy both work well here. Apify Residential is NOT supported and the run will stop immediately if it is selected — if you need residential routing, supply your own provider under Custom proxy URLs.

## Actor input object example

```json
{
  "searchKeywords": [
    "Software Engineer"
  ],
  "locations": [
    "Sydney, Australia"
  ],
  "maxResults": 100,
  "searchUrls": [],
  "postedWithin": "anytime",
  "sortBy": "relevance",
  "workplaceTypes": [],
  "employmentTypes": [],
  "experienceLevels": [],
  "easyApplyOnly": false,
  "underTenApplicantsOnly": false,
  "includeCompanies": [],
  "excludeCompanies": [],
  "excludeJobTitleKeywords": [],
  "excludeLocations": [],
  "jobFunctions": [],
  "industries": [],
  "excludeRecruitmentAgencies": false,
  "salaryCurrency": "",
  "excludeJobsWithoutSalary": false,
  "removeDuplicates": true,
  "skipJobIds": [],
  "skipUrls": [],
  "includeCompanyDetails": false,
  "includeRecruiterDetails": true,
  "extractPublicEmails": false,
  "expandLargeSearches": false,
  "dataSource": "auto",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `overview` (type: `string`):

Role, employer, location, recency, and how to apply.

## `full` (type: `string`):

Every field, including full job descriptions.

## `companies` (type: `string`):

Employer data, when company enrichment is enabled.

## `recruiters` (type: `string`):

Publicly listed job posters and hiring-team members.

## `searchTrace` (type: `string`):

Which search produced each job.

# 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 = {
    "searchKeywords": [
        "Software Engineer"
    ],
    "locations": [
        "Sydney, Australia"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("coregent/linkedin-jobs-scraper").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 = {
    "searchKeywords": ["Software Engineer"],
    "locations": ["Sydney, Australia"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("coregent/linkedin-jobs-scraper").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 '{
  "searchKeywords": [
    "Software Engineer"
  ],
  "locations": [
    "Sydney, Australia"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call coregent/linkedin-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,coregent/linkedin-jobs-scraper"
        }
    }
}

```

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/P9qWU54ID7hNqrUMs/builds/5XdKJWNLHDYKrId7y/openapi.json
