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

Scrape LinkedIn job postings by keyword, location or search URL, with full details in the same run: description, salary, applicants, seniority and more. No login or cookies. Pay only for jobs returned.

- **URL**: https://apify.com/muhammad_noman_riaz/linkedin-jobs-scraper.md
- **Developed by:** [Muhammad Noman Riaz](https://apify.com/muhammad_noman_riaz) (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 $1.50 / 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

## LinkedIn Jobs Scraper

### What does LinkedIn Jobs Scraper do?

LinkedIn Jobs Scraper collects public job postings from LinkedIn without a login, cookies or a LinkedIn account. You give it job keywords and a location, LinkedIn search URLs copied from your browser, or individual job URLs. In one run it pages through the search results and then opens every job page, so each result already contains the full description (plain text and HTML), salary range when the employer shows one, applicant count, seniority level, employment type, industries, company name, company page and logo, location, posting date and whether the job still accepts applications. Duplicate jobs across your queries are removed automatically. You can narrow searches by date posted, from the last few hours up to the last month. Results download as JSON, CSV, Excel or HTML, or flow into Google Sheets, Make, Zapier and your own code through the Apify API. You pay only for jobs delivered.

### What data can I get from LinkedIn job postings?

| Field | Example | Source |
|---|---|---|
| `jobId`, `url` | `4476360306` | Search card |
| `title`, `companyName`, `companyUrl`, `companyLogo` | `NBCUniversal` | Search card and job page |
| `location`, `workplaceType` | `New York, NY` | `workplaceType` only when LinkedIn writes it in the location, such as "(Remote)" |
| `postedAt`, `postedText` | `2026-10-06`, `1 week ago` | Search card (date only) |
| `salary` | `{ text, min, max, currency, period }` | When the employer shows pay |
| `applicantsCount`, `applicantsText` | `200`, `Over 200 applicants` | Job page |
| `seniorityLevel`, `employmentType`, `industries`, `jobFunction` | `Associate`, `Full-time` | Job page |
| `descriptionText`, `descriptionHtml` | Full job description | Job page |
| `isClosed` | `false` | Job page ("No longer accepting applications") |
| `applyType`, `applyUrl` | `offsite`, `null` | Job page |
| `benefits` | `Actively Hiring` | Search card badge |
| `searchQuery`, `scrapedAt` | `data analyst in New York, NY` | Added by the scraper |

### How do I use LinkedIn Jobs Scraper?

1. Enter one or more job keywords and a location, or paste LinkedIn job search URLs or job URLs.
2. Set how many jobs you want per query and, if you like, a date posted window.
3. Click Start and download your jobs when the run finishes.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `searchQueries` | array | | Keywords such as `"data analyst"`, or objects like `{"keywords": "nurse", "location": "Texas"}` |
| `location` | string | | Location used by keyword queries without their own location. Empty means worldwide. |
| `startUrls` | array | | LinkedIn job search URLs (`linkedin.com/jobs/search/?...`) and job URLs (`linkedin.com/jobs/view/<id>`) |
| `maxJobsPerQuery` | integer | 50 | Maximum jobs per query or search URL |
| `includeDetails` | boolean | true | Open each job page for description, applicants, salary and criteria. Same price either way. |
| `postedWithin` | string | `any` | `any`, `24h`, `week`, `month`, or a number of hours such as `6` |
| `maxConcurrency` | integer | 10 | Pages loaded at the same time |
| `maxRequestRetries` | integer | 6 | Attempts per page, each on a new proxy IP |
| `proxy` | object | Residential | Apify Proxy settings |

#### Input example

```json
{
  "searchQueries": ["data analyst", { "keywords": "registered nurse", "location": "California" }],
  "location": "New York, NY",
  "startUrls": [
    { "url": "https://www.linkedin.com/jobs/search/?keywords=python&location=Berlin&f_TPR=r604800" },
    { "url": "https://www.linkedin.com/jobs/view/4476360306" }
  ],
  "maxJobsPerQuery": 50,
  "includeDetails": true,
  "postedWithin": "week",
  "proxy": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}
```

### Output example

A real item from a test run on 2026-10-17 (description shortened here):

```json
{
  "jobId": "4476360306",
  "url": "https://www.linkedin.com/jobs/view/4476360306",
  "title": "Data Analyst - Digital Media Sales, NBCU Local",
  "companyName": "NBCUniversal",
  "companyUrl": "https://www.linkedin.com/company/nbcuniversal-inc-",
  "companyLogo": "https://media.licdn.com/dms/image/v2/D4E0BAQHIwjfYY3sIoQ/company-logo_100_100/B4EZq9EoPBKoAQ-/0/1764108698870/nbcuniversal_inc__logo?e=2147483647&v=beta&t=AFdpKqbJ0ukvxZUJcdTNXTVTm4EstjF6KbqK0olCRHI",
  "location": "New York, NY",
  "postedAt": "2026-10-06",
  "postedText": "1 week ago",
  "salary": { "text": "$62,000.00/yr - $72,000.00/yr", "min": 62000, "max": 72000, "currency": "USD", "period": "year" },
  "benefits": "Actively Hiring",
  "workplaceType": null,
  "searchQuery": "data analyst in New York, NY",
  "applicantsText": "Over 200 applicants",
  "applicantsCount": 200,
  "seniorityLevel": "Associate",
  "employmentType": "Full-time",
  "jobFunction": null,
  "industries": "Broadcast Media Production and Distribution",
  "descriptionText": "NBCUniversal is one of the world's leading media and entertainment companies. We create world-class content, which we distribute across our portfolio of film, t...",
  "descriptionHtml": "<p>NBCUniversal is one of the world's leading media and entertainment companies. We create world-class content, which we...",
  "isClosed": false,
  "applyType": "offsite",
  "applyUrl": null,
  "scrapedAt": "2026-10-17T16:57:43.914Z"
}
```

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

$1.50 per 1,000 jobs, with job details included at no extra charge. A job is charged only when it is delivered: with details on, a job whose page could not be loaded after all retries is listed in the `FAILED_ITEMS` record of the key-value store and is not charged. Proxy costs are covered by the price.

### FAQ

#### Do I need a LinkedIn account?

No. The scraper reads the same public job pages LinkedIn shows to logged-out visitors. It never logs in, never uses cookies or accounts, and never tries to get past a login wall or a captcha. If LinkedIn blocks a request, the scraper retries from a new IP and otherwise skips the job.

#### Why do I get fewer jobs than LinkedIn shows?

LinkedIn shows logged-out visitors fewer results than members see and stops serving a search after roughly 1,000 results. The count LinkedIn displays is also an estimate that includes promoted and duplicate listings. The scraper removes duplicates across all your queries, so the same job is never returned or charged twice. To collect more, split a broad search into several narrower ones by city or keyword.

#### Which filters work?

Keywords, location (as text, or `geoId` from a pasted search URL) and date posted (`postedWithin`: last N hours, 24 hours, week or month). We tested the other LinkedIn filters on 2026-10-17 with logged-out requests: workplace type (remote, hybrid, on-site), experience level, job type and sort order returned exactly the same jobs as without the filter. LinkedIn only applies them for logged-in members, so this scraper does not offer them and ignores them in pasted search URLs rather than pretend they work. If LinkedIn writes "(Remote)" or "(Hybrid)" in a job's location, the scraper fills `workplaceType` from it.

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

This scraper collects only job postings that LinkedIn publishes to anyone on the web without a login. It does not collect member profiles or any data behind a login. Note that LinkedIn's User Agreement prohibits scraping, and laws on web scraping and on processing personal data, such as GDPR, differ by country. You are responsible for how you use the scraper and the data, including complying with LinkedIn's terms and the laws that apply to you. This is not legal advice; if in doubt, ask a lawyer.

### Known limits

- `postedAt` is a date without a time, and only search results carry it. Jobs added by a job URL have `postedAt: null` and only the relative `postedText`.
- `applyUrl` is usually `null`: logged-out pages hide the external apply link behind a sign-in prompt. `applyType` tells you when a job applies on the employer's site (`offsite`).
- `jobFunction` is often `null` because LinkedIn currently leaves it out of many public job pages.
- `applicantsCount` is a floor for "Over 200 applicants" (200) and `null` for "Be among the first 25 applicants".
- Salary is returned only when the employer shows it, mostly in US states with pay transparency laws.
- `workplaceType` is filled only when LinkedIn writes it in the location text.
- LinkedIn changes its public pages from time to time; if a field stops filling, please open an issue.

# Actor input Schema

## `searchQueries` (type: `array`):

Job keywords to search for, one per line, such as "data analyst". Each uses the Location below. Through the API you can also pass objects like {"keywords": "nurse", "location": "Texas"} to give each query its own location.

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

Location for the search queries, written as on LinkedIn: a country, region or city such as "United States", "London" or "Berlin, Germany". Leave empty to search worldwide.

## `startUrls` (type: `array`):

LinkedIn job search URLs copied from your browser (https://www.linkedin.com/jobs/search/?keywords=...) and job URLs (https://www.linkedin.com/jobs/view/1234567890). From search URLs only keywords, location, geoId and the date posted filter are used.

## `maxJobsPerQuery` (type: `integer`):

Maximum number of jobs to return for each search query or search URL. LinkedIn stops serving results to logged-out visitors at around 1,000 per search.

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

Loads each job page for the description (text and HTML), applicants, seniority, employment type, industries, salary and closed status. Same price either way. Turn off for faster runs with search card data only.

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

Only jobs posted within this window: any, 24h, week, month, or a whole number of hours such as 6.

## `maxConcurrency` (type: `integer`):

Maximum number of pages loaded at the same time.

## `maxRequestRetries` (type: `integer`):

How many times a page is tried, each time on a new proxy IP, before the job is skipped and not charged.

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

Residential proxies are recommended: LinkedIn rate-limits datacenter IPs quickly.

## Actor input object example

```json
{
  "searchQueries": [
    "software engineer"
  ],
  "location": "United States",
  "maxJobsPerQuery": 50,
  "includeDetails": true,
  "postedWithin": "any",
  "maxConcurrency": 10,
  "maxRequestRetries": 6,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

One item per LinkedIn job posting with search card data and, when details are on, the full job page

# 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 = {
    "searchQueries": [
        "software engineer"
    ],
    "location": "United States",
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("muhammad_noman_riaz/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 = {
    "searchQueries": ["software engineer"],
    "location": "United States",
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("muhammad_noman_riaz/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 '{
  "searchQueries": [
    "software engineer"
  ],
  "location": "United States",
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call muhammad_noman_riaz/linkedin-jobs-scraper --silent --output-dataset

```

## MCP server setup

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