# LinkedIn Job Finder - Search & Export Jobs (`extractkit/linkedin-job-finder`) Actor

Find LinkedIn jobs in bulk and export them to Excel, CSV or JSON. Search by job title, location, date posted, remote/hybrid, experience level and more. Get company, location, salary, applicant count, full description and a direct link to every posting.

- **URL**: https://apify.com/extractkit/linkedin-job-finder.md
- **Developed by:** [Extract Kit](https://apify.com/extractkit) (community)
- **Categories:** Jobs, Lead generation, Social media
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 job scrapeds

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 Job Finder

Search LinkedIn jobs the way you would by hand — then get every result as a spreadsheet instead of 40 browser tabs.

Type what you're looking for and where. Narrow by date, remote/hybrid, contract type, experience level. Get back a clean table: **company, location, salary, applicant count, full description and a direct link** to every posting.

### Who this is for

- **Job seekers** — pull today's postings across five cities into one sheet, filter out the agency reposts and the roles needing clearance you don't have, and apply to the ones with under 25 applicants.
- **Recruiters and sourcers** — track who's hiring for what, in which markets, and how fast roles are moving.
- **Analysts and founders** — measure hiring demand for a skill, a title or a competitor over time.

### What you get

One row per job:

| Column | Example |
|---|---|
| Job title | Software Engineer, Backend |
| Company | Tessera Labs |
| Location | San Jose, CA |
| Posted | 2026-08-16 · "2 days ago" |
| Salary | $200,000 – $250,000 / year |
| Applicants | Over 200 applicants |
| Type & seniority | Full-time · Mid-Senior level |
| Industry & function | Software Development · Engineering |
| Description | Full text, formatting intact |
| Link | Direct URL to the posting |

Download it as **CSV or Excel**, pull it through the **API**, or send it straight to Google Sheets, Airtable, Make or Zapier.

**Salary, even though LinkedIn has no salary field.** When an employer publishes pay it's written into the job description — this reads it out and gives you the numbers plus the exact sentence they came from, so you can check anything that looks off. If no pay range can be read *confidently*, the field is left empty rather than guessed at. A wrong salary is worse than a blank one.

### Filters that LinkedIn doesn't give you

This is where most of the time savings are. All of them run before you're charged, so you never pay for a job you filtered out.

- **Job title must / must not include** — searching "engineer" on LinkedIn also returns sales engineers. This fixes that.
- **Skip these companies** — the usual use is cutting out staffing agencies that repost the same ten roles.
- **Skip these locations** — for when a country-wide search keeps surfacing one city you can't move to.
- **Description must / must not mention** — find roles that mention `visa sponsorship`, or drop every one that says `security clearance`.
- **At most N applicants** — the single most useful filter if you're applying. Jobs with 200+ applicants are usually already decided.
- **Only jobs that show a salary.**

### Getting started

Fill in two fields:

```json
{
  "keywords": ["product manager"],
  "locations": ["United States"]
}
```

A more realistic run:

```json
{
  "keywords": ["product manager", "product designer"],
  "locations": ["United States", "Canada"],
  "datePosted": "past-week",
  "titleExcludes": ["intern", "senior director"],
  "companyExcludes": ["robert half"],
  "maxApplicants": 25,
  "maxJobsTotal": 500
}
```

Every job title is searched in every location, so that's four searches.

**Already built the search on LinkedIn?** Set your filters on linkedin.com/jobs, copy the address bar, and paste it in:

```json
{ "searchUrls": ["https://www.linkedin.com/jobs/search?keywords=designer&location=Berlin&f_TPR=r604800"] }
```

Your filters come along with it — with one exception, which the run log names: LinkedIn stopped applying job type, experience level, remote/hybrid and sort order to logged-out search in August 2026, so those parts of a pasted URL are ignored on their side. Use the **Full-time, contract…** and **Experience level** fields instead; this Actor checks those against each job's own page.

### Run it on a schedule

Set `datePosted` to **Past 24 hours** and schedule it daily. You get each morning's new postings before the applicant count climbs. Job IDs are stable, so it's easy to tell which are new since yesterday.

### Good to know

**LinkedIn caps any single search at about 1,000 results.** That's LinkedIn's limit, not this Actor's — their own website does the same. If your search is broader than that, this tells you plainly instead of quietly handing you a partial list: check *What was searched* in the output, and the run summary says so too.

The fix is to split one wide search into several narrow ones. Instead of one search for the whole United States, list the five states you care about — you get up to 1,000 from each. Narrowing by date does the same thing.

**Remote/hybrid filtering and "sort by most recent" are not offered**, because LinkedIn no longer applies either one to logged-out searches (measured 2026-08-22) and publishes no remote/hybrid label on the public job page for us to filter on instead. Other Actors still show these switches. They do not do anything. Use `datePosted` for recency, and screen for remote roles with `titleIncludes` / `descriptionIncludes` if you need to — knowing that is a text match, not LinkedIn's own label.

**A few other honest limits:**

- Applicant counts are **bands**, not exact numbers — LinkedIn publishes "first 25 applicants" or "over 200", never "37".
- **Salary only appears when the employer wrote one.** Plenty of postings have none.
- **No recruiter names or email addresses.** This Actor reads public job postings; it doesn't collect personal contact details.
- Occasionally a job closes between being found and being opened. You still get the row, marked `detailUnavailable`, rather than silently losing a result.

### Cost

You pay **per job returned** — not per search, not per page. Jobs filtered out don't count, and duplicates are removed across all your searches, so overlapping terms like "product manager" and "product management" never bill you twice for the same posting.

Start with `maxJobsTotal` set to something small to see the shape of the results, then raise it.

# Actor input Schema

## `keywords` (type: `array`):

Job titles, skills or keywords — the same thing you'd type into LinkedIn's search box. Each one is searched separately, so "product manager, product designer" gives you both.

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

A city, state or country, written the way LinkedIn writes it: "United States", "Berlin, Germany", "Greater London". Add several and every job title is searched in every location.

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

Search on linkedin.com/jobs, set your filters there, then copy the address bar and paste it here. Your filters come along with it. Runs in addition to anything above.

## `datePosted` (type: `string`):

Fresh postings get far fewer applicants, so narrowing this is usually worth it.

## `jobTypes` (type: `array`):

Leave empty for all. Checked on each job's own page, so a run that uses this reads every posting and takes longer — you are still only charged for the ones that match.

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

Leave empty for all levels. Checked on each job's own page, like the job type above.

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

Only jobs you can apply to without leaving LinkedIn. Cuts the result count sharply — useful when you're applying, less so when you're researching a market.

## `maxJobsTotal` (type: `integer`):

The run stops here. Set 0 for everything the search can reach — pair that with a spend limit rather than leaving both open.

## `scrapeDetails` (type: `boolean`):

Adds the description, seniority, job function, industry and applicant count. Turn it off if you only need a list of titles, companies and links — that runs faster.

## `titleIncludes` (type: `array`):

Keep only jobs whose title contains one of these words. Useful when LinkedIn's search is too generous — searching "engineer" returns sales engineers too.

## `titleExcludes` (type: `array`):

Drop jobs whose title contains any of these.

## `companyExcludes` (type: `array`):

Most people use this to filter out staffing and recruiting agencies that repost the same roles.

## `locationExcludes` (type: `array`):

Drop jobs whose location mentions any of these — handy when a country-wide search keeps returning one city you can't relocate to.

## `descriptionIncludes` (type: `array`):

Keep only jobs whose description mentions one of these — a specific tool, framework or benefit. Needs the full description, so it turns that on automatically.

## `descriptionExcludes` (type: `array`):

Drop jobs whose description mentions any of these. Common uses: "security clearance", "unpaid", a language you don't speak.

## `maxApplicants` (type: `integer`):

Only jobs that haven't been flooded yet. LinkedIn reports this in bands ("first 25 applicants", "over 200"), so this matches against the band. Jobs where LinkedIn shows no applicant count at all are left out.

## `postedAfter` (type: `string`):

For when "past week" is too broad and "past 24 hours" is too narrow.

## `requireSalary` (type: `boolean`):

LinkedIn has no salary field, so this reads the pay range out of the description text. It only works when the employer actually wrote one — many don't.

## `companyIds` (type: `array`):

LinkedIn's numeric company IDs. Filter by company on linkedin.com/jobs and copy the number from the f\_C part of the address. Company names won't work here — LinkedIn ignores them and would quietly return every company, so this Actor rejects them instead.

## `geoId` (type: `string`):

LinkedIn's numeric location ID, from the geoId part of a search address. Use it when a place name is ambiguous — there are a lot of Springfields.

## `maxJobsPerSearch` (type: `integer`):

A cap for each job-title/location pair, so one broad term can't use up the whole run. 0 means no per-search cap.

## `concurrency` (type: `integer`):

How many job pages to load at once. Leave at 2 — going faster gets requests refused, and a failed run costs more than a slow one.

## Actor input object example

```json
{
  "keywords": [
    "product manager",
    "product designer"
  ],
  "locations": [
    "United States",
    "Germany"
  ],
  "searchUrls": [
    "https://www.linkedin.com/jobs/search?keywords=designer&location=Berlin&f_TPR=r604800"
  ],
  "datePosted": "any",
  "jobTypes": [
    "full-time"
  ],
  "experienceLevels": [
    "mid-senior"
  ],
  "easyApplyOnly": false,
  "maxJobsTotal": 200,
  "scrapeDetails": true,
  "titleIncludes": [
    "senior",
    "staff"
  ],
  "titleExcludes": [
    "intern",
    "manager"
  ],
  "companyExcludes": [
    "robert half",
    "insight global"
  ],
  "locationExcludes": [
    "remote",
    "san francisco"
  ],
  "descriptionIncludes": [
    "kubernetes",
    "visa sponsorship"
  ],
  "descriptionExcludes": [
    "security clearance"
  ],
  "maxApplicants": 25,
  "postedAfter": "2026-08-01",
  "requireSalary": false,
  "companyIds": [
    "1035"
  ],
  "geoId": "103644278",
  "maxJobsPerSearch": 0,
  "concurrency": 2
}
```

# Actor output Schema

## `jobs` (type: `string`):

Every job with all fields — title, company, location, date, salary, description and link.

## `jobsCsv` (type: `string`):

Open this in Excel, Google Sheets or Numbers. Salary is split into its own columns.

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

Just the columns most people want: title, company, location, date, type, seniority, salary and link.

## `searches` (type: `string`):

Each search that ran and how many jobs it found. Check here if you got fewer jobs than expected — it flags any search that hit LinkedIn's 1,000-result limit.

## `summary` (type: `string`):

Totals for the run: jobs found, companies covered, and how many postings included a salary.

## `runDetails` (type: `string`):

This run in Apify Console, including its log.

# 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 = {
    "keywords": [
        "product manager"
    ],
    "locations": [
        "United States"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("extractkit/linkedin-job-finder").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 = {
    "keywords": ["product manager"],
    "locations": ["United States"],
}

# Run the Actor and wait for it to finish
run = client.actor("extractkit/linkedin-job-finder").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 '{
  "keywords": [
    "product manager"
  ],
  "locations": [
    "United States"
  ]
}' |
apify call extractkit/linkedin-job-finder --silent --output-dataset

```

## MCP server setup

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

```

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/PY4apcqRjBZmidGQv/builds/SM4aHasVFHehCRr2L/openapi.json
