# LinkedIn Jobs Scraper: Salary, Remote & New-Job Alerts (`humble-echidna/linkedin-jobs`) Actor

Scrape LinkedIn job listings by keyword, location, company or search URL, signed out, no cookies. Full description, salary, seniority, job type, applicants, Easy Apply vs external. Remote/hybrid and experience filters, only-new-jobs feeds. Unofficial; not affiliated with LinkedIn.

- **URL**: https://apify.com/humble-echidna/linkedin-jobs.md
- **Developed by:** [Michael Costa](https://apify.com/humble-echidna) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.95 / 1,000 jobs

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 does LinkedIn Jobs Scraper do?

**LinkedIn Jobs Scraper** searches **LinkedIn job listings** by keyword, location, company or search URL and returns
each job with its **full description, salary, seniority, job type** and applicant count, read live from the pages
LinkedIn shows anyone who isn't signed in. **No login, no cookies.** Workplace and experience filters that actually
hold, and an **only-new-jobs** mode for daily feeds.

It is **not** affiliated with LinkedIn, and it doesn't collect people: the job poster's or recruiter's name and
profile are never read.

[Input fields](https://apify.com/humble-echidna/linkedin-jobs/input-schema) ·
[API](https://apify.com/humble-echidna/linkedin-jobs/api) ·
[Use it from Claude, ChatGPT or Cursor (MCP)](#can-i-use-linkedin-jobs-scraper-from-an-ai-agent-mcp)

**Jump to:** [Monitor](#monitor-linkedin-jobs-get-only-new-jobs-in-slack-email-or-a-webhook) ·
[Fields](#what-data-does-linkedin-jobs-scraper-return) ·
[Price](#how-much-does-it-cost-to-scrape-linkedin-jobs) ·
[Filters](#remote-hybrid-experience-and-job-type-filters) ·
[Switching](#switching-from-another-linkedin-jobs-actor) ·
[Input](#input) · [Output](#output) ·
[AI agents (MCP)](#can-i-use-linkedin-jobs-scraper-from-an-ai-agent-mcp) · [Limits](#limits) · [FAQ](#faq)

**Try it in one click:** the input comes pre-filled with "software engineer" in the United States, 20 jobs. That's
about **$0.04** on the Free plan (20 × $1.90/1,000, plus $0.00005 for the run start), $0.02 on a paid plan. **Then
replace it with the jobs you actually want.**

### Monitor LinkedIn jobs: get only new jobs, in Slack, email or a webhook

With **Only jobs new since my last run** on, each run returns (and charges for) only jobs that search hasn't returned
before. Sorted newest first (the default), a run stops reading as soon as it reaches jobs it already returned, so a run
with nothing new costs almost nothing beyond the $0.00005 start fee.

1. Fill in **Job titles or keywords** and **Locations** (and any filters), turn on **Only jobs new since my last run**
   (`"onlyNewJobs": true`), and click **Start**. This first run returns every matching job (up to **Max jobs per
   run**) and is the baseline.
2. Click **Save as a new task** (top right of the actor page). The memory is kept per search: keywords, location,
   companies, date posted and filters together, so two tasks never hide each other's jobs.
3. In Apify Console, open **Schedules**, click **Create new**, set how often in **Schedule setup** (for example every
   hour, or daily at 07:00), then **Add** your task.
4. On the task, open the **Integrations** tab and pick where the new jobs go:
   - **Slack**: click **Configure**, sign in, pick the workspace and channel, and the "run succeeded" event. A
     useful message: `{{resource.statusMessage}}` and a link,
     `<https://console.apify.com/storage/datasets/{{resource.defaultDatasetId}}|new jobs>`.
   - **Gmail**: click **Connect with Google**, set the subject and body, and attach the dataset. It sends after each
     successful run.
   - **HTTP webhook**: event `ACTOR.RUN.SUCCEEDED`, your URL. Apify POSTs `{"eventType": ..., "resource": {...}}`;
     `resource.defaultDatasetId` is the run's dataset, and
     `GET https://api.apify.com/v2/datasets/<defaultDatasetId>/items?format=json` (with your API token) returns the
     new jobs.

Apify's integrations fire after every successful run, including quiet ones: a quiet run's dataset is empty.

### What data does LinkedIn Jobs Scraper return?

| Field | Example | Notes |
|---|---|---|
| `title` | `Software Engineer, 2027 New Grad` | As the posting shows it |
| `companyName`, `companyUrl`, `companyId` | `Whatnot`, its LinkedIn page, `52160608` | The id works in **Companies** |
| `location`, `country` | `New York, NY`, `United States` | Country read from LinkedIn's location line; `null` for areas |
| `statedWorkplace` | `hybrid` | `remote`, `hybrid` or `onsite` when the posting says so; `null` when it doesn't |
| `seniority`, `experienceLevel` | `Mid-Senior level`, `mid-senior` | The posting's own "Seniority level" line |
| `employmentType` | `FULL_TIME` | Also `PART_TIME`, `CONTRACT`, `TEMPORARY`, `INTERNSHIP`, `VOLUNTEER`, `OTHER` |
| `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod` | `155000`, `160000`, `USD`, `YEAR` | From LinkedIn's pay box, else the description (`salarySource` says which) |
| `applicants`, `applicantsCount` | `Over 200 applicants`, `200` | `null` count for "Be among the first 25" |
| `applyType` | `external` | `external` (employer's site) or `linkedin` (Easy Apply) |
| `postedAt`, `postedAgo` | `2026-10-14`, `6 hours ago` | |
| `description`, `descriptionHtml` | full text and HTML | `null` with **Read each job's full posting** off |
| `industries` | `Technology, Information and Internet` | As LinkedIn writes it |
| `url` | `https://www.linkedin.com/jobs/view/4460205602/` | The job on LinkedIn |

Every job has the same fields; the full list is under [Output](#output). The record is the same shape as our
company-careers-site job actors (Greenhouse, Lever, Ashby, Workday and remote boards), so results merge into one
table.

### How much does it cost to scrape LinkedIn jobs?

You pay per job returned: **$1.90 per 1,000 jobs on Apify's Free plan, $0.95 per 1,000 on every paid plan** (Starter,
Scale, Business and up), plus $0.00005 each time a run starts. No minimum per run.

- **The pre-filled example:** 20 jobs × $1.90/1,000 = $0.038 (Free) or $0.019 (paid), plus the start fee.
- **A month, for example:** a daily new-jobs feed of about 200 jobs a day ≈ 6,000 jobs ≈ **$5.70** on a paid plan.
- **Caps:** **Max jobs per run** and **Max jobs per search** in the input, and **Maximum cost per run** in the run
  options. The run stops cleanly at whichever comes first, and stops opening postings as soon as the limit is covered.

**Never charged:** jobs your workplace, experience or job type filters leave out; postings that fail to load or were
taken down; the same job found by two of your searches (returned once); with only-new on, jobs returned before.

### How to scrape LinkedIn jobs

1. Open LinkedIn Jobs Scraper and click **Try for free** (or **Start** if you're signed in).
2. Type what you're looking for in **Job titles or keywords** (one search per line) and where in **Locations**.
3. Optional: **Date posted**, **Companies**, **Workplace**, **Experience level**, **Job type**, or paste a LinkedIn
   search URL into **LinkedIn search URLs**.
4. Click **Start**, then open the **Output** tab and export as JSON, CSV or Excel.

### Remote, hybrid, experience and job type filters

When you aren't signed in, **LinkedIn's own search ignores its workplace, experience and job type filters**: the
same search returns the same jobs with or without them (we checked on 2026-10-14). This actor applies them itself,
from each posting:

- **Workplace** (`remote`, `hybrid`, `onsite`): what the posting says in plain words: its location line, its title
  ("Data Engineer (Remote)"), or its description ("this is a hybrid role", "fully remote", "3 days a week in the
  office", "not a remote position"). Company boilerplate such as "work from home policy" isn't counted. Jobs that
  don't say are left out.
- **Experience level**: the posting's own "Seniority level" line. Postings marked "Not Applicable" are left out.
- **Job type**: the posting's own "Employment type" line.

Jobs the filters leave out aren't charged. Filtering means opening postings that may not match, so a narrow filter
on a broad search takes longer: a remote filter on "software engineer" kept 30 of 120 postings in our test.

### Switching from another LinkedIn jobs actor?

Your inputs map like this:

| You had | Here |
|---|---|
| keywords / title / search terms | **Job titles or keywords** (`keywords`), one search per line |
| location (+ geoId, distance) | **Locations** (`locations`); for a geoId or distance, paste the search URL |
| date posted (`r86400`, `r604800`, `r2592000`) | **Date posted** (`past-24-hours`, `past-week`, `past-month`) |
| company names or ids | **Companies** (`companies`): exact name, LinkedIn company URL or id |
| search URLs / start URLs | **LinkedIn search URLs** (`searchUrls`), read as they are |
| work type / experience / contract type | **Workplace**, **Experience level**, **Job type**, applied from the posting |
| limit / rows / max items | **Max jobs per search** and **Max jobs per run** |
| a list of job ids to skip | **Only jobs new since my last run**: kept for you, per search |

Common output fields: job link → `url`, job id → `jobId`, posted date → `postedAt`, contract type → `employmentType`,
experience level → `seniority` / `experienceLevel`, work type → `statedWorkplace`, job function → `department`,
sector → `industries`, applications count → `applicants` / `applicantsCount`, salary → `salaryText` plus the parsed
`salaryMin` / `salaryMax` / `salaryCurrency` / `salaryPeriod`. Not here: the job poster's name, photo and profile,
and company logos.

### Example: software engineers in the United States

The pre-filled input:

```json
{"keywords": ["software engineer"], "locations": ["United States"], "maxResults": 20}
```

It returned 20 jobs. One of them (real output from a local run on 2026-10-14; long text shortened):

```json
{
  "id": "linkedin:whatnot-inc:4460205602",
  "jobId": "4460205602",
  "platform": "linkedin",
  "source": "LinkedIn",
  "company": "whatnot-inc",
  "companyName": "Whatnot",
  "companyId": "52160608",
  "companyUrl": "https://www.linkedin.com/company/whatnot-inc/",
  "title": "Software Engineer, 2027 New Grad",
  "location": "New York, NY",
  "country": "United States",
  "statedWorkplace": "hybrid",
  "employmentType": "FULL_TIME",
  "seniority": "Not Applicable",
  "industries": "Technology, Information and Internet",
  "postedAt": "2026-10-14",
  "postedAgo": "6 hours ago",
  "url": "https://www.linkedin.com/jobs/view/4460205602/",
  "applyType": "external",
  "applicants": "Over 200 applicants",
  "applicantsCount": 200,
  "salaryMin": 155000.0,
  "salaryMax": 160000.0,
  "salaryCurrency": "USD",
  "salaryPeriod": "YEAR",
  "salaryText": "$155,000.00/yr - $160,000.00/yr",
  "salarySource": "linkedin",
  "description": "🚀 Join the Future of Commerce with Whatnot!\n\nWhatnot is the largest live shopping platform in North America and Europe to buy, sell, and discover the …",
  "search": "software engineer in United States"
}
```

### Input

| Field | What it does |
|---|---|
| **Job titles or keywords** (`keywords`) | One search per line, as typed into LinkedIn. Empty: every job at the **Companies**. |
| **Locations** (`locations`) | One per line; each keyword is searched in each location. Empty: worldwide. |
| Date posted (`datePosted`) | `any`, `past-hour`, `past-24-hours`, `past-week`, `past-month`. |
| Companies (`companies`) | Exact LinkedIn company name, company page URL or numeric id, one per line. |
| Workplace (`workplaceTypes`) | `remote`, `hybrid`, `onsite`, read from each posting. |
| Experience level (`experienceLevels`) | `internship`, `entry`, `associate`, `mid-senior`, `director`, `executive`. |
| Job type (`employmentTypes`) | `FULL_TIME`, `PART_TIME`, `CONTRACT`, `TEMPORARY`, `INTERNSHIP`, `VOLUNTEER`, `OTHER`. |
| LinkedIn search URLs (`searchUrls`) | linkedin.com/jobs/search URLs, each read as its own search. |
| Sort by (`sortBy`) | `recent` (default, newest first) or `relevant`. |
| Read each job's full posting (`includeDetails`) | Default on. Off: only what the search list shows, faster. |
| Only jobs new since my last run (`onlyNewJobs`) | Return only jobs this search hasn't returned before. |
| Max jobs per search (`maxResultsPerSearch`) | Cap each keyword × location search (LinkedIn lists at most ~1,000). |
| Max jobs per run (`maxResults`) | Cap the total across all searches (default 100). |

```json
{
  "keywords": ["data engineer", "analytics engineer"],
  "locations": ["United States", "Canada"],
  "datePosted": "past-24-hours",
  "workplaceTypes": ["remote"],
  "onlyNewJobs": true,
  "maxResults": 500
}
```

### Output

Every job has the same fields. Fields LinkedIn doesn't show for a job are `null`.

```json
{
  "id": "linkedin:whatnot-inc:4460205602",
  "jobId": "4460205602",
  "platform": "linkedin",
  "source": "LinkedIn",
  "sourceUrl": "https://www.linkedin.com/jobs/",
  "company": "whatnot-inc",
  "companyName": "Whatnot",
  "companyId": "52160608",
  "companyUrl": "https://www.linkedin.com/company/whatnot-inc/",
  "title": "Software Engineer, 2027 New Grad",
  "department": null,
  "team": null,
  "location": "New York, NY",
  "locations": ["New York, NY"],
  "country": "United States",
  "remote": null,
  "workplaceType": null,
  "statedWorkplace": "hybrid",
  "employmentType": "FULL_TIME",
  "seniority": "Not Applicable",
  "experienceLevel": null,
  "industries": "Technology, Information and Internet",
  "postedAt": "2026-10-14",
  "postedAgo": "6 hours ago",
  "updatedAt": null,
  "url": "https://www.linkedin.com/jobs/view/4460205602/",
  "applyUrl": "https://www.linkedin.com/jobs/view/4460205602/",
  "applyType": "external",
  "acceptingApplications": true,
  "applicants": "Over 200 applicants",
  "applicantsCount": 200,
  "salaryMin": 155000.0,
  "salaryMax": 160000.0,
  "salaryCurrency": "USD",
  "salaryPeriod": "YEAR",
  "salaryText": "$155,000.00/yr - $160,000.00/yr",
  "salarySource": "linkedin",
  "description": "🚀 Join the Future of Commerce with Whatnot! …",
  "descriptionHtml": "<strong><strong>🚀 Join the Future of Commerce with Whatnot!<br><br></s…",
  "search": "software engineer in United States",
  "searchUrl": "https://www.linkedin.com/jobs/search/?keywords=software+engineer&location=United+States&sortBy=DD",
  "scrapedAt": "2026-10-14T22:57:19.865688Z"
}
```

`id` and `jobId` are stable across runs, so you can use them to deduplicate. `workplaceType` and `remote` are only set
when LinkedIn's location line itself says remote, hybrid or on-site; `statedWorkplace` also reads the title and
description. The run's `RUN_STATS` record lists every search: jobs listed, returned, left out by filters, already
returned before, and any posting that failed, with the reason.

### Run it on a schedule, or from your own code

1. Save your input as a [task](https://docs.apify.com/platform/actors/running/tasks) (**Save as a new task**, top right
   of the actor page) and add it to a [schedule](https://docs.apify.com/platform/schedules) (Console → Schedules →
   **Create new**).
2. Collect results: download the dataset as JSON, CSV or Excel; fetch the latest run's results from the
   [API](https://apify.com/humble-echidna/linkedin-jobs/api)
   (`GET https://api.apify.com/v2/actor-tasks/<task id>/runs/last/dataset/items?status=SUCCEEDED&format=csv`, with
   your API token); let a [webhook](https://docs.apify.com/platform/integrations/webhooks) tell your system when a
   run succeeds; or connect it to Make, Zapier or n8n through
   [Apify's integrations](https://docs.apify.com/platform/integrations).

#### Can I use LinkedIn Jobs Scraper from an AI agent (MCP)?

Yes, through [Apify's MCP server](https://docs.apify.com/platform/integrations/mcp), from Claude, ChatGPT, Cursor or
any other MCP client. Add this to your client's MCP configuration (or let the agent find it with the server's actor
search); your client signs you in to Apify:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=humble-echidna/linkedin-jobs"
    }
  }
}
```

To use an [Apify API token](https://console.apify.com/settings/integrations) instead of signing in, add
`"headers": {"Authorization": "Bearer <APIFY_TOKEN>"}` next to `url`.

An agent should pass a small search first, e.g. `{"keywords": ["product manager"], "locations": ["London"],
"datePosted": "past-week", "maxResults": 20}`.

### Who it's for

Job boards and aggregators refreshing listings every day; recruiters and sales teams watching which companies are
hiring, and for what; job seekers and agents running a saved search for new postings; researchers sampling pay and
demand across roles and places.

### Why this one?

- **Filters that hold.** Workplace, experience and job type are applied from each posting, because LinkedIn ignores
  them for signed-out searches, and the jobs they leave out cost nothing.
- **Only new jobs, built in.** A scheduled run returns only what that search hasn't returned before, remembered in
  your own Apify account; no list of seen job ids to keep yourself.
- **Salary as numbers**, from LinkedIn's pay box or the description, with the source named.
- **No duplicates** across your keyword and location lines in a run.
- **No personal data.** Recruiters' and job posters' names and profiles are never read or stored.
- **Reliable.** A posting that fails or was taken down only affects itself; the run log and `RUN_STATS` say which and
  why. Requests go through Apify's proxy with a fresh IP each time; you don't configure anything.

### Limits

- **About 1,000 jobs per search.** LinkedIn stops listing a search's results there. Split a big search into several
  locations or narrower keywords (each line is its own search).
- **Signed-out data only.** No poster or recruiter details, no "who applied", no company headcount or website
  (company pages aren't read), no LinkedIn salary estimates beyond the pay box the employer published.
- **Workplace is read from the posting's words.** Most postings don't say; with the Workplace filter on, those are
  left out. `statedWorkplace` can misread unusual wording.
- **Job function** (`department`) is often empty: LinkedIn shows it on few signed-out postings.
- `postedAt` is a day, not a time; `postedAgo` is LinkedIn's own rounded wording.

### FAQ

#### Is it legal to scrape LinkedIn jobs?

This actor reads only what any visitor who isn't signed in sees on linkedin.com: public job listings and job
postings. It never signs in, uses no cookies or accounts, and collects no personal data (the job poster's name and
profile on some postings are skipped). Be aware that **LinkedIn's robots.txt disallows automated access** for every
crawler, and **LinkedIn's User Agreement forbids scraping** its services without LinkedIn's permission. Whether and how
you use the data is your responsibility; job postings belong to the employers who wrote them. If you are an employer
or rights holder and want something removed or not read, open an issue and we'll act on it promptly.

#### Why did a search return fewer jobs than LinkedIn shows?

LinkedIn's count on its site is an estimate and includes jobs it only shows signed-in members; signed out it lists up
to about 1,000 per search. Your filters, **Max jobs per search** and **Max jobs per run** cut it further, and
`RUN_STATS` shows how many were listed, left out and returned for each search.

#### Why did a company name fail?

The name must match one LinkedIn company exactly ("Stripe", not "Strip"); a near miss is reported in the log with the
names LinkedIn offers and their ids. Paste the company's LinkedIn page URL or its numeric id to be sure.

#### Can I get only new jobs since my last run?

Yes: turn on **Only jobs new since my last run**, save the input as a task and schedule it (see
[Monitor](#monitor-linkedin-jobs-get-only-new-jobs-in-slack-email-or-a-webhook)). Keep **Sort by** on `recent`: the run
then stops at the first jobs it already returned. The first run returns up to **Max jobs per run**; jobs older than
those count as already seen.

#### How is this different from LinkedIn's own job alerts?

Alerts email you a few links; this returns every matching job as data (description, salary, seniority, applicants)
for a spreadsheet, a database, a job board or an agent, on your schedule, for several searches at once.

### Related actors

| Actor | Use it when |
|---|---|
| [Company Career Page Jobs Scraper](https://apify.com/humble-echidna/ats-jobs) | You want jobs straight from companies' own career pages (Greenhouse, Lever, Ashby and more) |
| [Remote Jobs Scraper](https://apify.com/humble-echidna/remote-jobs) | You want remote job boards (We Work Remotely, Remote OK, Python.org) |
| [Hiring Signals Monitor](https://apify.com/humble-echidna/hiring-signals) | You want which companies are hiring, and for what, as sales signals |

### Feedback and support

Found a bug, or need a field or filter that isn't here? Open an issue on the **Issues** tab with the input you used.

### Versions

Current version: **0.1**. See the Changelog tab for what changed in each version.

# Changelog

This Actor's version history is a separate document: https://apify.com/humble-echidna/linkedin-jobs/changelog.md

# Actor input Schema

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

What to search LinkedIn Jobs for, one search per line, as you'd type it into LinkedIn's search box, e.g. data engineer, nurse practitioner, react developer. Every line is searched in every location below. Leave empty to list every job at the companies you name below. Default: software engineer.

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

Where the jobs are, one per line, as you'd type it on LinkedIn: a country (United States, Germany), a state or region (California), or a city (London, Austin, Texas). Each keyword line is searched in each location. Leave empty for LinkedIn's worldwide results. Default: United States.

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

Only jobs posted within this time: `any` (default), `past-hour`, `past-24-hours`, `past-week` or `past-month`. LinkedIn applies it to the search itself.

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

Only jobs at these companies, one per line: the company's name exactly as LinkedIn shows it (Stripe), its LinkedIn page (https://www.linkedin.com/company/stripe/) or its numeric LinkedIn company id. A name that doesn't match one LinkedIn company exactly is reported in the log with the closest names LinkedIn offers, and left out. With no keywords, every job at these companies is listed. Leave empty for any company.

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

Only jobs the posting says are remote, hybrid or on-site. LinkedIn doesn't apply this filter to signed-out searches, so the actor reads it from each job's location line, title and description; jobs that don't say are left out (and not charged). Leave empty for all jobs.

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

Only jobs at these levels, matched against the posting's own "Seniority level" line (LinkedIn doesn't apply this filter to signed-out searches). Postings marked "Not Applicable" are left out when this is set (and not charged). Leave empty for all.

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

Only jobs of these types, matched against the posting's own "Employment type" line (LinkedIn doesn't apply this filter to signed-out searches). Leave empty for all.

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

Optional: LinkedIn job search pages to read as they are, one per line, e.g. https://www.linkedin.com/jobs/search/?keywords=product%20manager\&location=Canada\&f_TPR=r604800. Each URL is a search of its own; its workplace (f_WT), experience (f_E) and job type (f_JT) choices are applied by the actor, since LinkedIn ignores them signed out. The fields above don't change a pasted URL's search.

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

`recent` (default): newest first, best for feeds and "only new jobs". `relevant`: LinkedIn's best matches first.

## `includeDetails` (type: `boolean`):

Default true: open every job's posting for its description, salary, seniority, job type, industries, applicant count and how to apply. false: only what the search list shows (title, company, location, date, sometimes salary), which is faster. The workplace, experience and job type filters always read the posting.

## `onlyNewJobs` (type: `boolean`):

Default false. When true, the first run returns every matching job and later runs return only jobs this search hasn't returned before. What was returned is remembered in your own Apify account, separately for each search (keywords, location, companies and filters together), so different searches never hide each other's jobs. Schedule it hourly or daily for a new-job feed.

## `maxResultsPerSearch` (type: `integer`):

Stop each search (one keyword in one location, or one URL) after this many jobs, e.g. 50, so one broad search can't use up the whole run. Minimum 1; leave empty (the default) for as many as LinkedIn lists, which is at most about 1,000 per search.

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

Stop after this many jobs in total across all searches (default 100, pre-filled with 20 in the form). Minimum 1. Raise it (e.g. 1000) for a full sweep. The run also stops cleanly at the maximum cost per run you set in the run options, whichever comes first.

## Actor input object example

```json
{
  "keywords": [
    "software engineer"
  ],
  "locations": [
    "United States"
  ],
  "datePosted": "any",
  "sortBy": "recent",
  "includeDetails": true,
  "onlyNewJobs": false,
  "maxResults": 20
}
```

# Actor output Schema

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

No description

## `runStats` (type: `string`):

No description

# 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": [
        "software engineer"
    ],
    "locations": [
        "United States"
    ],
    "maxResults": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("humble-echidna/linkedin-jobs").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": ["software engineer"],
    "locations": ["United States"],
    "maxResults": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("humble-echidna/linkedin-jobs").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": [
    "software engineer"
  ],
  "locations": [
    "United States"
  ],
  "maxResults": 20
}' |
apify call humble-echidna/linkedin-jobs --silent --output-dataset

```

## MCP server setup

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

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/oKXvyOrmsm2BtcJia/builds/s94aFeGU3rfrOKnVq/openapi.json
