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

Scrape LinkedIn job listings by job title, skill, and location. Extract job title, company, location, posting date, job link, and LinkedIn job ID.

- **URL**: https://apify.com/dineshwadhwani/linkedin-jobs-scraper.md
- **Developed by:** [Dinesh Kumar Wadhwani](https://apify.com/dineshwadhwani) (community)
- **Categories:** Jobs, Automation, AI
- **Stats:** 53 total users, 4 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

$0.10 / actor start

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 Jobs Scraper — Multi-Role, Multi-Location, Ultra-Fast

Extract LinkedIn job listings by role, skill, and location in **under 60 seconds** — no browser required, no bloat, production-ready.

Built for developers, data teams, and recruiters who need clean, deduplicated job data fast and cheap. Runs on Crawlee's HTTP-only crawler, not a full browser — making it **up to 23x faster and 8x cheaper** than equivalent Playwright-based LinkedIn scrapers on the Apify Store.

***

### Why this Actor

| | This Actor | Typical browser-based LinkedIn scrapers |
|---|---|---|
| ⏱ Runtime (100 jobs) | ~10–65 seconds | 3–5 minutes |
| 💰 Cost per run | ~$0.001–$0.003 | ~$0.008–$0.02 |
| 🧠 Memory needed | 256 MB | 512–1024 MB |
| 🔁 Deduplication | Global, across all locations | Usually per-location only |
| 🔍 Multi-role search | Yes — up to 3 roles, OR-combined | Rarely |

No Playwright. No Chrome. No scrolling loops. Just clean HTTP requests and fast HTML parsing.

***

### What it does

This Actor scrapes LinkedIn's job search results for up to:

- **3 job roles** — combined into a single OR-query so you cover title variants in one pass
- **3 skills** — appended as supporting keywords to tighten relevance
- **3 locations** — each gets its own search, results pooled and deduplicated globally
- **1 time frame** — last 24 hours, 48 hours, 7 days, or 15 days

Results are automatically deduplicated by job ID and URL across all locations. The run stops the moment 100 unique jobs are collected — no wasted requests, no runaway costs.

***

### Input

| Field | Type | Required | Limit | Description |
|---|---|---|---|---|
| `roles` | `string[]` | ✅ Yes | 3 | Job titles to search for (e.g. `"VP Engineering"`, `"Head of Product"`) |
| `skills` | `string[]` | No | 3 | Supporting keywords (e.g. `"SaaS"`, `"Python"`, `"AWS"`) |
| `locations` | `string[]` | ✅ Yes | 3 | Cities, regions, or countries (e.g. `"Bangalore"`, `"Remote"`) |
| `timeFrame` | `string` | No | — | `r86400` = 24h · `r172800` = 48h · `r604800` = 7 days · `r1296000` = 15 days · `""` = any time |
| `liAtCookie` | `string` | No | — | Optional LinkedIn session cookie for authenticated access |

#### Example — Senior engineering leadership roles across India

```json
{
  "roles": ["VP Engineering", "Head of Engineering", "Engineering Director"],
  "skills": ["SaaS", "Platform"],
  "locations": ["Bangalore", "Pune", "Hyderabad"],
  "timeFrame": "r604800"
}
```

This runs **3 searches** (one per location), each using the combined query:

```
"VP Engineering" OR "Head of Engineering" OR "Engineering Director" SaaS Platform
```

#### Example — Full-stack developer roles

```json
{
  "roles": ["Full Stack Developer", "Software Engineer"],
  "skills": ["React", "Node.js", "AWS"],
  "locations": ["Remote"],
  "timeFrame": "r86400"
}
```

***

### Output

Each result is saved as a flat JSON record to the Apify dataset. Fields:

```json
{
  "title": "Vice President of Engineering",
  "company": "Acme Corp",
  "location": "Bangalore, Karnataka, India",
  "link": "https://www.linkedin.com/jobs/view/4426507200",
  "postedAt": "2026-06-28",
  "jobId": "4426507200",
  "searchLocation": "Bangalore",
  "searchKeywords": "\"Vice President\" AI",
  "roles": ["Vice President"],
  "skills": ["AI"]
}
```

| Field | Description |
|---|---|
| `title` | Job title as listed on LinkedIn |
| `company` | Hiring company name |
| `location` | Location as shown on the job card |
| `link` | Direct URL to the job posting |
| `postedAt` | ISO date the job was posted (if available) |
| `jobId` | LinkedIn's internal job ID — used as the deduplication key |
| `searchLocation` | Which of your input locations produced this result |
| `searchKeywords` | The exact keyword string sent to LinkedIn's search API |
| `roles` | Echo of your input roles |
| `skills` | Echo of your input skills |

Results are ready to pipe directly into spreadsheets, databases, Slack alerts, ATS systems, or downstream AI pipelines.

***

### How it works (technical)

This Actor uses **Crawlee's `CheerioCrawler`** — plain HTTP requests with server-side HTML parsing — instead of a full browser. This is possible because LinkedIn's guest job search API (`/jobs-guest/jobs/api/seeMoreJobPostings/search`) returns server-rendered HTML fragments that contain the full job card data without requiring JavaScript execution.

**Search strategy (cost-optimised):**

1. All input roles are OR-combined into a single keyword string (e.g. `"Role A" OR "Role B"`).
2. Skills are appended as plain supporting terms.
3. One HTTP request is fired per location using this combined keyword string.
4. Results are paginated using the `start` offset parameter (10 results per page).
5. Pagination stops as soon as a page returns no new unique results, or the global 100-job cap is hit.
6. All results are deduplicated globally using `jobId` first, then `link` as a fallback.

This means a run with 3 roles + 3 skills + 3 locations fires a **maximum of ~33 HTTP requests** total (3 locations × up to ~11 pages) — compared to browser-based scrapers that may fire hundreds of rendering/network events per page.

***

### Performance benchmarks

Tested on Apify platform, 512 MB memory, development build:

| Input | Results | Duration | Cost |
|---|---|---|---|
| 1 role · 1 skill · 2 locations · last 24h | 48 jobs | 10 seconds | $0.001 |
| 1 role · 1 skill · 2 locations · last 7 days | 100 jobs | 65 seconds | $0.003 |

> 💡 **Tip:** Set memory to **256 MB** in your actor settings. This Actor does not launch a browser and needs very little RAM — halving the memory allocation reduces cost further without affecting reliability.

***

### Use cases

- **Recruiters & talent teams** — monitor fresh job postings by competitor companies or target roles on a schedule
- **Job seekers** — run daily searches across multiple cities and pipe results to a spreadsheet or email alert
- **Market researchers** — track hiring trends by role, skill, and region over time
- **HR analytics** — benchmark job title conventions and skill demand across companies
- **AI pipelines** — feed structured job data into LLM workflows for summarisation, matching, or classification
- **ATS integrations** — pull and deduplicate job listings before importing into applicant tracking systems

***

### Scheduling & automation

This Actor works well on a schedule. To run it daily:

1. Open the Actor on Apify and click **Schedule**.
2. Set your preferred cron (e.g. `0 8 * * *` for 8am daily).
3. Connect the output dataset to a Google Sheet, webhook, or downstream actor using Apify integrations.

Use `timeFrame: "r86400"` (last 24 hours) on daily schedules to avoid collecting the same jobs repeatedly.

***

### Limitations

- Returns up to **100 unique jobs per run**. If you need more, split searches across multiple runs with different location or role combinations.
- Does **not** visit individual job detail pages — full job descriptions are not included. Only data visible on the search results card is captured.
- LinkedIn's guest search is publicly accessible but subject to rate limiting. If you're running this at high frequency, provide a valid `li_at` session cookie for more reliable access.
- LinkedIn's HTML structure can change without notice. If results drop to 0 unexpectedly, check the Actor's run log for login wall or block warnings.

***

### FAQ

**Does this require a LinkedIn account?**
No. It works without authentication using LinkedIn's public guest job search. The optional `liAtCookie` field is available for high-frequency use cases where authenticated access improves reliability.

**Why do I sometimes get more results than LinkedIn's UI shows?**
LinkedIn's "X jobs" count in the UI is an estimate and varies based on your login state, IP location, and personalization. The Actor hits the raw search API directly and often returns more listings than the UI count suggests.

**Can I search for remote jobs?**
Yes — add `"Remote"` as one of your locations.

**Can I run this on a schedule?**
Yes — use Apify's built-in scheduler. Pair with `timeFrame: "r86400"` on a daily schedule to only collect new postings each day.

**Is this Actor maintained?**
Yes — it is actively maintained. If LinkedIn changes its HTML structure and the Actor stops returning results, open an issue or check for an updated version on the Apify Store.

***

### Input schema reference

```json
{
  "roles": ["VP Engineering", "Head of Engineering"],
  "skills": ["SaaS", "B2B"],
  "locations": ["Bangalore", "Pune"],
  "timeFrame": "r604800",
  "liAtCookie": ""
}
```

`timeFrame` values:

| Value | Meaning |
|---|---|
| `r86400` | Last 24 hours |
| `r172800` | Last 48 hours |
| `r604800` | Last 7 days |
| `r1296000` | Last 15 days |
| `""` (empty) | Any time |

# Actor input Schema

## `roles` (type: `array`):

Add up to 3 job titles/roles to search for (e.g. "Software Engineer", "Backend Developer"). At least one is required.

## `skills` (type: `array`):

Add up to 3 supporting skills or keywords (e.g. "React", "AWS"). Optional.

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

Add up to 3 locations to search in. At least one is required.

## `timeFrame` (type: `string`):

Choose how recent the jobs should be.

## `liAtCookie` (type: `string`):

Optional LinkedIn li\_at cookie for authenticated access. Strongly recommended — LinkedIn heavily rate-limits and blocks unauthenticated scraping.

## Actor input object example

```json
{
  "roles": [
    "Software Engineer"
  ],
  "locations": [
    "India"
  ],
  "timeFrame": "r86400"
}
```

# Actor output Schema

## `results` (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 = {
    "roles": [
        "Software Engineer"
    ],
    "locations": [
        "India"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("dineshwadhwani/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 = {
    "roles": ["Software Engineer"],
    "locations": ["India"],
}

# Run the Actor and wait for it to finish
run = client.actor("dineshwadhwani/linkedin-jobs-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "roles": [
    "Software Engineer"
  ],
  "locations": [
    "India"
  ]
}' |
apify call dineshwadhwani/linkedin-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=dineshwadhwani/linkedin-jobs-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/jAfDoHmVF7V6pD6HC/builds/p3P0XjxyBDUgDDu2q/openapi.json
