# LinkedIn Jobs Scraper (`axiomworks/linkedin-jobs-scraper`) Actor

Scrape public LinkedIn job listings for one keyword and location per run: title, company, location, posted date and job URL, plus optional full description, seniority, employment type, job function, industries and applicant count. Up to about 1,000 jobs per search. No login.

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

## Pricing

from $0.88 / 1,000 results

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

## LinkedIn Jobs Scraper

### What does LinkedIn Jobs Scraper do?

LinkedIn Jobs Scraper searches public LinkedIn job listings by keyword and location and returns one clean record per job as JSON, CSV or Excel. Turn on **Include full job details** and each job also comes back with its complete description text, seniority level, employment type, job function, industries and applicant count, all read from the job's public page.

It is used by recruiters and sourcers tracking hiring demand, job-board and aggregator builders, market and salary researchers, and sales teams watching which companies are hiring for a role. It needs no LinkedIn account, cookies or API key, because it reads the same public pages a logged-out visitor sees. Jobs are deduplicated by LinkedIn job ID within a run, and requests are retried with backoff and rotated proxy sessions when LinkedIn rate-limits.

### What data can you get?

Every job has the search-card fields. The fields marked (details) are null unless **Include full job details** is on. `salary` is null when LinkedIn shows none, and because LinkedIn's search cards carry no pay data it is only filled from the job page, so turn on details to get it.

| Field | Description | Example |
|---|---|---|
| `id` | Stable record ID, same as `jobId` | `4470016722` |
| `sourceUrl` | Page the record was read from (the job URL) | `https://www.linkedin.com/jobs/view/...-4470016722` |
| `jobId` | LinkedIn job posting ID, unique per job | `4470016722` |
| `title` | Job title | `Software Engineer II, Backend (Identity Decisioning)` |
| `company` | Hiring company name | `Affirm` |
| `companyUrl` | LinkedIn company page URL | `https://www.linkedin.com/company/affirm` |
| `location` | Job location as shown on LinkedIn | `Salt Lake City, UT` |
| `url` | Public job URL without tracking parameters | `https://www.linkedin.com/jobs/view/...-4470016722` |
| `postedDate` | Posting date, `YYYY-MM-DD` | `2026-09-24` |
| `salary` | Salary or compensation range if LinkedIn shows one, otherwise null. Filled from the job page, so it needs **Include full job details** | `$117,000.00/yr - $234,000.00/yr` |
| `description` (details) | Full job description text, up to 20,000 characters | `We are looking for a Software Engineer to join...` |
| `seniorityLevel` (details) | Seniority level | `Mid-Senior level` |
| `employmentType` (details) | Employment type | `Full-time` |
| `jobFunction` (details) | Job function category | `Engineering and Information Technology` |
| `industries` (details) | Industries of the hiring company | `Financial Services` |
| `applicants` (details) | Number parsed from `applicantsText`; `Over 200` gives 200 and `Be among the first 25` gives 25, see `applicantsIsApproximate` | `27` |
| `applicantsIsApproximate` (details) | True when the caption is a bound (`Over N`, `Be among the first N`) rather than an exact count | `false` |
| `applicantsText` (details) | Applicant caption as shown on LinkedIn | `27 applicants` |
| `keywords` | The keywords input that found the job | `software engineer` |
| `searchLocation` | The location input that found the job | `United States` |
| `scrapedAt` | UTC timestamp of the run | `2026-09-29T18:48:05.608024+00:00` |

The dataset has three views in the Console: **Jobs** (the basic table), **Full details** (description, seniority, function, industries, applicants) and **Companies** (company, company page, industries and location).

### How to use LinkedIn Jobs Scraper

1. Open the Actor in Apify Console and go to the **Input** tab.
2. Enter **Keywords**, for example `data analyst`, and a **Location** such as `Austin, Texas` or `United Kingdom`. Leave the location empty to search all locations.
3. Switch on **Include full job details** if you need descriptions, seniority, job function, industries and applicant counts.
4. Optionally narrow the search with date posted, job type, experience level and workplace type, and set **Max results**.
5. Click **Start** and wait for the run to finish. Runs with full details take longer because the Actor opens one extra page per job.
6. Open the **Output** tab and export the dataset as JSON, CSV, Excel, XML or HTML, or fetch it through the API.

### Input

All fields are optional in the input form, but `keywords` must contain a value when the run starts. If it is empty the run fails immediately with a clear message.

| Field | Type | Default / prefill | Description |
|---|---|---|---|
| `keywords` | string | prefill `software engineer` | Job title or keywords to search for, for example `python developer`. Required at run time. |
| `location` | string | prefill `United States`, no default | City, state or country. Leave empty to search all locations. |
| `includeDetails` | boolean | default `false`, prefill `false` | Open each job's public page to also return the full description, seniority level, employment type, job function, industries and applicant count. One extra request per job. |
| `datePosted` | string | `any` | One of `any`, `24h`, `week`, `month`. Only jobs posted within the period are returned. |
| `jobType` | array | empty (all) | Any of `fullTime`, `partTime`, `contract`, `temporary`, `internship`, `volunteer`, `other`. |
| `experienceLevel` | array | empty (all) | Any of `internship`, `entry`, `associate`, `midSenior`, `director`, `executive`. |
| `workType` | array | empty (all) | Any of `onSite`, `remote`, `hybrid`. |
| `maxResults` | integer | `10` (1 to 1000) | Maximum number of unique jobs to return. LinkedIn's public search stops at roughly 1,000 results per query. |
| `proxyConfiguration` | object | Apify Proxy | Proxy settings. Apify Proxy is recommended because LinkedIn rate-limits datacenter IPs; residential proxies are the most reliable for large runs. |

Example input:

```json
{
  "keywords": "nurse",
  "location": "Ohio",
  "includeDetails": true,
  "datePosted": "week",
  "workType": ["onSite"],
  "maxResults": 50,
  "proxyConfiguration": { "useApifyProxy": true }
}
```

### Output

Below are two items from a real run with `includeDetails` on (keywords `software engineer`, location `United States`). The `description` is shortened here for readability; the dataset holds the full text.

```json
[
  {
    "id": "4470016722",
    "sourceUrl": "https://www.linkedin.com/jobs/view/software-engineer-ii-backend-identity-decisioning-at-affirm-4470016722",
    "jobId": "4470016722",
    "title": "Software Engineer II, Backend (Identity Decisioning)",
    "company": "Affirm",
    "companyUrl": "https://www.linkedin.com/company/affirm",
    "location": "Salt Lake City, UT",
    "url": "https://www.linkedin.com/jobs/view/software-engineer-ii-backend-identity-decisioning-at-affirm-4470016722",
    "postedDate": "2026-09-24",
    "salary": null,
    "description": "At Affirm, we exist for the moments that matter, giving people a clear, predictable way to pay over time... We are looking for a Software Engineer to join the Identity Decisioning team ...",
    "seniorityLevel": "Mid-Senior level",
    "employmentType": "Full-time",
    "jobFunction": "Engineering and Information Technology",
    "industries": "Financial Services",
    "applicants": 27,
    "applicantsText": "27 applicants",
    "applicantsIsApproximate": false,
    "keywords": "software engineer",
    "searchLocation": "United States",
    "scrapedAt": "2026-09-29T18:48:05.608024+00:00"
  },
  {
    "id": "4462335889",
    "sourceUrl": "https://www.linkedin.com/jobs/view/software-engineer-ii-backend-post-transaction-at-affirm-4462335889",
    "jobId": "4462335889",
    "title": "Software Engineer II, Backend (Post-Transaction)",
    "company": "Affirm",
    "companyUrl": "https://www.linkedin.com/company/affirm",
    "location": "Salt Lake City, UT",
    "url": "https://www.linkedin.com/jobs/view/software-engineer-ii-backend-post-transaction-at-affirm-4462335889",
    "postedDate": "2026-09-26",
    "salary": null,
    "description": "At Affirm, we exist for the moments that matter... Affirm is reinventing credit to make it more honest and friendly ...",
    "seniorityLevel": "Mid-Senior level",
    "employmentType": "Full-time",
    "jobFunction": "Engineering and Information Technology",
    "industries": "Financial Services",
    "applicants": 63,
    "applicantsText": "63 applicants",
    "applicantsIsApproximate": false,
    "keywords": "software engineer",
    "searchLocation": "United States",
    "scrapedAt": "2026-09-29T18:48:05.608024+00:00"
  }
]
```

With `includeDetails` off, the items contain the search-card fields (`id`, `sourceUrl`, `jobId`, `title`, `company`, `companyUrl`, `location`, `url`, `postedDate`, plus `keywords`, `searchLocation` and `scrapedAt`), and the salary and detail fields are null.

### How much does it cost?

The Actor uses pay-per-event pricing: you are charged for each job saved to the dataset. Turning on full job details does not add a separate charge per job, it only makes the run slower because each job needs one more request. Apify's free plan credit covers small runs, so you can try a few dozen jobs before paying anything. For the exact price per result on each plan, open the Pricing tab on the Actor page.

You can also set a maximum cost per run in the run options. The Actor stops cleanly when that limit is reached and keeps everything collected so far.

### Integrations and API

Start runs, poll them and read datasets over the Apify API, or use the official clients. Set the `APIFY_TOKEN` environment variable to your API token from Apify Console.

Python:

```python
import os

from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("axiomworks/linkedin-jobs-scraper").call(run_input={
    "keywords": "python developer",
    "location": "United Kingdom",
    "includeDetails": True,
    "maxResults": 25,
})
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(job["title"], job["company"], job.get("applicants"))
```

JavaScript:

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('axiomworks/linkedin-jobs-scraper').call({
    keywords: 'python developer',
    location: 'United Kingdom',
    includeDetails: true,
    maxResults: 25,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => // one job per item
  process.stdout.write(`${item.title}\n`));
```

cURL:

```bash
curl -X POST "https://api.apify.com/v2/acts/axiomworks~linkedin-jobs-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keywords": "python developer", "location": "United Kingdom", "includeDetails": true, "maxResults": 25}'
```

Because it runs on Apify, the Actor plugs into Zapier, Make, n8n, Google Sheets and any system that accepts webhooks through Apify integrations. A common setup is a scheduled run with a `datePosted` of `24h` that sends new jobs to a spreadsheet or a Slack channel.

### Use with AI agents (MCP)

LinkedIn Jobs Scraper can be called by AI agents through Apify's MCP server at https://mcp.apify.com. Agents discover it with the `search-actors` tool and run it with `call-actor`. To expose only this Actor to your client, add it to the MCP configuration of Claude, ChatGPT, Cursor or any MCP-compatible tool:

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

Example prompts you can type once it is connected:

- "Find 30 remote product designer jobs on LinkedIn posted this week and include the full descriptions."
- "Search LinkedIn for entry level data analyst jobs in Berlin and list the companies with the most applicants."
- "Pull recent LinkedIn postings for `site reliability engineer` in Canada and summarize the seniority levels and industries."

The input and output schemas are typed, so the agent sees exactly which fields exist (`keywords`, `location`, `includeDetails`, filters) and what comes back, and can fill them without guessing.

### FAQ

**How many LinkedIn jobs can I get per search?** LinkedIn's public search stops at roughly 1,000 results for one keyword and location combination, and `maxResults` is capped at 1,000. To collect more, split the search by location, date posted, job type or experience level.

**Why did I get fewer jobs than Max results?** LinkedIn had fewer matching public listings, or duplicates were removed. The Actor also stops early if it sees no new jobs on two consecutive result pages.

**How fast is it, and why is full details slower?** The search pages return 10 jobs each and the Actor pauses briefly between pages. With full details on, it opens one extra job page per job, five at a time with Apify Proxy and two at a time without a proxy, so a run with details takes noticeably longer than one without. The default run timeout is one hour.

**Do I need a proxy?** For small runs it often works without one, but LinkedIn rate-limits datacenter IPs with HTTP 429 and 999 responses. The Actor retries up to five times with growing delays and gets a fresh proxy session on every attempt. For large runs or full details, use Apify Proxy, ideally the residential group.

**Why do I get zero results or a blocked error?** If LinkedIn refuses the first search request after all retries, the run fails with a message asking you to enable a proxy or try later. If a search simply has no public matches, the run finishes with an empty dataset and logs a warning. Try broader keywords, an empty location or fewer filters.

**Is salary always included?** No. LinkedIn shows salary on only some listings, and only on the job page, so `salary` needs **Include full job details** and is null for listings without a pay range.

**Can I schedule it, and how fresh is the data?** Yes, use Apify schedules. Data is read live from LinkedIn on every run, and `scrapedAt` records when. There is no cross-run deduplication, so for a daily job feed set `datePosted` to `24h` and store results by `jobId`.

### Is it legal to scrape LinkedIn?

The Actor only reads publicly visible job listings that a logged-out visitor can open. It does not log in, use cookies or collect private profile data. Job postings can still include personal or company information, so make sure your use complies with privacy laws such as GDPR and CCPA and with the terms of the platform. You are responsible for how you use the data you collect.

### Feedback

Found a bug, a missing field or a change in LinkedIn's pages that breaks a run? Open an issue in the Issues tab of this Actor with your input and the run link. Feature requests are welcome there too.

# Actor input Schema

## `keywords` (type: `string`):

Job title or keywords to search for, e.g. 'python developer'. Required when the run starts.

## `location` (type: `string`):

City, state or country, e.g. 'Austin, Texas' or 'United Kingdom'. Leave empty to search all locations.

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

Open each job's public page to also return the full description, seniority level, employment type, job function, industries and applicant count. One extra request per job, so runs are slower. The price per result stays the same.

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

Only return jobs posted within this period.

## `jobType` (type: `array`):

Only return these employment types. Leave empty for all.

## `experienceLevel` (type: `array`):

Only return these experience levels. Leave empty for all.

## `workType` (type: `array`):

Only return on-site, remote or hybrid jobs. Leave empty for all.

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

Maximum number of unique jobs to return (1-1000). LinkedIn's public search does not go beyond roughly 1,000 results per query (min 1, max 1000).

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

LinkedIn often rate-limits datacenter IPs. Apify Proxy is recommended; residential proxies are the most reliable for large runs.

## Actor input object example

```json
{
  "keywords": "software engineer",
  "location": "United States",
  "includeDetails": false,
  "datePosted": "any",
  "maxResults": 10,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

All results in the dataset

# 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",
    "location": "United States",
    "includeDetails": false,
    "datePosted": "any",
    "maxResults": 10,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("axiomworks/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 = {
    "keywords": "software engineer",
    "location": "United States",
    "includeDetails": False,
    "datePosted": "any",
    "maxResults": 10,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("axiomworks/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 '{
  "keywords": "software engineer",
  "location": "United States",
  "includeDetails": false,
  "datePosted": "any",
  "maxResults": 10,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call axiomworks/linkedin-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,axiomworks/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/OZuL3Tst9orm3RfcS/builds/kbBseeFPxacQPQBhk/openapi.json
