# LinkedIn Jobs Scraper: Titles, Companies, Salaries & Full Text (`apisamurai/linkedin-jobs-scraper`) Actor

LinkedIn jobs scraper: job listings with title, company, location, posted date, salary, seniority, employment type, applicants and full description. Date, remote, job type and experience filters. Pay only for unique jobs.

- **URL**: https://apify.com/apisamurai/linkedin-jobs-scraper.md
- **Developed by:** [API Samurai](https://apify.com/apisamurai) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.40 / 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.

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: titles, companies, salaries and descriptions

Get LinkedIn job listings as clean data: job title, company, location, posted date, salary when shown, seniority, employment type, industries, applicant count and the full job description. Search by keywords and location, or paste a LinkedIn job search link.

You pay only for real, unique jobs. Duplicates, jobs removed by your filters and failed requests are free, and limits are exact.

### What you can use it for

- **Job market research**: count openings by title, city or company and track how they change week to week.
- **Salary research**: collect posted pay ranges for a role across cities.
- **Recruiting and sales leads**: find companies hiring for a skill you place or a product you sell.
- **Job boards and alerts**: feed fresh postings from the last 24 hours into your own site, sheet or Slack channel.
- **Career planning**: compare seniority levels, industries and requirements in job descriptions.

### How to use it

1. Enter **Keywords** (for example `data analyst`) and a **Location** (for example `London`).
2. Pick **Date posted** and set **Max jobs**.
3. Turn on **Include full description** if you need the job text, seniority, employment type and applicant count.
4. Optionally add filters: **Remote jobs only**, **Job type**, **Experience level**.
5. Click **Start**, then download the results as Excel, CSV, JSON or HTML, or read them through the API.

You can also paste one or more LinkedIn job search links (`linkedin.com/jobs/search?keywords=...`). Their keywords, location, date and company filters are used.

### Input

| Field | What it does | Default |
|---|---|---|
| `keywords` | Job title, skill or company, e.g. `python developer` | |
| `location` | City, region or country, e.g. `Berlin`. Empty means worldwide | |
| `maxItems` | Jobs saved in the run, across all searches, enforced exactly (1 to 5,000) | 100 |
| `datePosted` | `any`, `24h`, `week` or `month` | `any` |
| `includeDescription` | Open each job page for the description and job details | off |
| `remoteOnly` | Keep only jobs marked or described as remote | off |
| `jobType` | `full-time`, `part-time`, `contract`, `temporary`, `internship`, `volunteer`, `other` | any |
| `experienceLevel` | `internship`, `entry`, `associate`, `mid-senior`, `director`, `executive` | any |
| `startUrls` | LinkedIn job search links | |

Example input:

```json
{
    "keywords": "data analyst",
    "location": "London",
    "datePosted": "week",
    "maxItems": 200,
    "includeDescription": true
}
```

### Output

One row per job. Example from a live run (description shortened):

```json
{
    "jobId": "4484203926",
    "title": "Senior Data Analyst",
    "company": "Imperial College London",
    "companyUrl": "https://uk.linkedin.com/school/imperial-college-london",
    "location": "London Area, United Kingdom",
    "isRemote": false,
    "postedDate": "2026-10-27",
    "postedText": "2 days ago",
    "salary": "£59,808.00/yr - £71,793.00/yr",
    "seniority": "Associate",
    "employmentType": "Full-time",
    "jobFunction": null,
    "industries": "Higher Education",
    "applicantsText": "Over 200 applicants",
    "description": "Job number: BUS00907\nSalary or Salary range: £59,808 - £71,793 per annum\nPosting End Date: 10 Nov 2026\n\nAbout the role\n\nReady to turn data into insights...",
    "descriptionHtml": "<p>Job number: <strong>BUS00907</strong></p>...",
    "applyMethod": "external",
    "applyUrl": "https://www.linkedin.com/jobs/view/4484203926",
    "jobUrl": "https://www.linkedin.com/jobs/view/4484203926",
    "companyLogo": "https://media.licdn.com/dms/image/v2/D4E0BAQGOM2ioD3Ym6w/company-logo_100_100/...",
    "benefits": null,
    "searchInput": "data analyst in London",
    "scrapedAt": "2026-10-30T17:16:57.293Z"
}
```

Always filled from the search results: `jobId`, `title`, `company`, `companyUrl`, `location`, `isRemote`, `postedDate` (ISO date), `postedText`, `jobUrl`, `companyLogo`, `searchInput`, `scrapedAt`. `salary` and `benefits` (e.g. "Actively Hiring") appear when LinkedIn shows them.

With **Include full description** on, each job also gets `description` (plain text), `descriptionHtml`, `seniority`, `employmentType`, `industries`, `applicantsText`, `applyMethod` (`external` when the employer's own site takes applications, `linkedin` for LinkedIn's apply form) and more salaries, since many pay ranges appear only on the job page. In our London test 45 of 200 jobs had a salary.

Each run also writes a `SUMMARY` record to the key-value store with, per search: jobs found, delivered, skipped (duplicates, filters, unreadable pages), pages read and why it stopped.

### Pricing

Pay per event, no start fee:

- **$0.40 per 1,000 jobs** (`job`)
- **$0.60 per 1,000 full descriptions** (`job-details`), only for jobs where a description was saved

A job with its full description costs $1.00 per 1,000. Your spending limit is respected exactly.

### Fair billing

- No charge for duplicates: a job found twice, on two pages or by two searches, is saved and charged once.
- No charge for jobs removed by your remote, job type or experience filters, even though the Actor had to open their pages to check.
- No charge for failed requests, and no description charge when a job page could not be read.
- When LinkedIn finds nothing and shows unrelated "similar jobs" instead, they are dropped.
- Limits are exact: ask for 20 jobs, get 20, never 21.
- The run stops cleanly at your spending limit, without going over.

### Integrations

- **API**: start runs and read results with the Apify API or the JavaScript and Python clients.
- **Zapier, Make, n8n**: send new jobs to Google Sheets, Airtable, Slack, a CRM or any app.
- **MCP**: use the Actor from AI assistants through the Apify MCP server.
- **Schedules and webhooks**: run a search every morning and get notified when it finishes.

### FAQ

**Do I need a LinkedIn account or cookies?**
No. The Actor reads only the public job pages LinkedIn shows to logged-out visitors. It never logs in, so no account is at risk.

**How many jobs can I get per search?**
LinkedIn shows about 1,000 jobs per search to logged-out visitors. For more, split the search by city, by date posted, or by related keywords. Jobs found twice are saved once and charged once.

**How do the remote, job type and experience filters work?**
LinkedIn's public search currently ignores these filters, so the Actor applies them itself. Job type and experience are read from each job page. Remote jobs are recognized when the title or location says remote, or the description clearly states it (for example "fully remote" or "this position is remote"). LinkedIn does not show its workplace label to logged-out visitors, so a few remote jobs that never say so in the text can be missed. Strict filters scan more pages and take longer, but you only pay for jobs that match.

**Why is `applyUrl` usually the LinkedIn job page?**
LinkedIn shows the employer's own application link only to signed-in members. The Actor returns it when it is on the public page and otherwise the LinkedIn job page, where the Apply button is. `applyMethod` tells you whether applications go to an external site.

**Why is `jobFunction` empty?**
LinkedIn currently leaves job function off its public job pages. The field is filled when it appears.

**How fresh is the data?**
Every run reads LinkedIn live. Use `datePosted: 24h` on a daily schedule to get only new jobs.

**Can I paste a LinkedIn search link?**
Yes. Search on linkedin.com/jobs, copy the address of the results page and paste it into **LinkedIn job search links**. Links to single job pages or other sites are skipped with a warning.

**Is scraping LinkedIn jobs legal?**
The Actor collects only public job postings, not personal profiles. Check the rules that apply to your use, especially if you republish the data.

# Actor input Schema

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

Job title, skill or company, as you would type it in the LinkedIn job search box, e.g. data analyst or python developer.

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

City, region or country, e.g. London, Texas or Germany. Leave empty to search worldwide.

## `maxItems` (type: `integer`):

Maximum jobs saved in this run, across all searches. Enforced exactly. LinkedIn shows about 1,000 jobs per search to logged-out visitors.

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

Only jobs posted in this time window.

## `includeDescription` (type: `boolean`):

Opens each job page for the full description, seniority, employment type, industries, applicant count and salary when shown. Charged as an extra job-details event per job that has a description.

## `remoteOnly` (type: `boolean`):

Keep only jobs marked remote in the title or location, or clearly described as remote in the job text. Other jobs are skipped and not charged.

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

Keep only these employment types, as shown on the job page. Jobs of other types are skipped and not charged.

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

Keep only these seniority levels, as shown on the job page. Jobs at other levels are skipped and not charged.

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

Optional. Paste the address of a LinkedIn job search results page (linkedin.com/jobs/search?keywords=...). Its keywords, location, date and company filters are used; workplace, job type and experience codes become filters as above.

## Actor input object example

```json
{
  "keywords": "software engineer",
  "location": "United States",
  "maxItems": 20,
  "datePosted": "week",
  "includeDescription": true,
  "remoteOnly": false
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `summary` (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",
    "location": "United States",
    "maxItems": 20,
    "datePosted": "week",
    "includeDescription": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("apisamurai/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",
    "maxItems": 20,
    "datePosted": "week",
    "includeDescription": True,
}

# Run the Actor and wait for it to finish
run = client.actor("apisamurai/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",
  "maxItems": 20,
  "datePosted": "week",
  "includeDescription": true
}' |
apify call apisamurai/linkedin-jobs-scraper --silent --output-dataset

```

## MCP server setup

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