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

Scrape public LinkedIn job postings — title, company, location, salary, description and applicant count. No login, no cookies, no proxies.

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

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-usage

## 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

Scrape **public LinkedIn job postings** into a clean dataset — job title, company, location, posting date, salary, full description, seniority, job function and applicant count. **No login, no cookies, no session tokens and no proxies.**

### What does LinkedIn Jobs Scraper do?

LinkedIn shows job listings to signed-out visitors, and this Actor reads exactly what that visitor sees — nothing private, nothing behind a login.

Search the way you would on the site: type **keywords** and a **location**, then narrow with the same filters LinkedIn gives you — date posted, job type, experience level, and on-site/remote/hybrid. You get back **one clean row per job**, ready for a spreadsheet, a database or a model.

Turn on **Include full job details** and each posting is opened to add the complete description text, seniority level, job function, industries, applicant count and the salary range where the employer published one.

Because it needs no authenticated session, there is nothing to configure, no cookie to paste, and no account to put at risk.

### Why use LinkedIn Jobs Scraper?

- **Recruiting and sourcing** — see which companies are hiring for a role, where, and how much competition each posting has.
- **Lead generation** — a company posting five sales roles is a company buying sales tools. Hiring is a buying signal.
- **Market and salary research** — build a real compensation dataset from published ranges rather than survey guesses.
- **Competitive intelligence** — track a rival's headcount plans by watching what they advertise.
- **Job boards and aggregators** — feed a niche board with fresh, structured listings.
- **AI and RAG pipelines** — assemble a labelled corpus of job descriptions.

### How to use LinkedIn Jobs Scraper

1. Click **Try for free**.
2. Enter **Keywords** (e.g. `data engineer`) and a **Location** (e.g. `United States`).
3. Optionally narrow by **date posted**, **job type**, **experience level** or **workplace type**.
4. Tick **Include full job details** if you want descriptions and salary ranges.
5. Set **Maximum results**, then click **Start**.
6. Download as **JSON, CSV, Excel, HTML or XML**.

### Input

```json
{
  "keywords": "data engineer",
  "location": "United States",
  "datePosted": "pastWeek",
  "workplaceTypes": ["remote"],
  "jobTypes": ["fullTime"],
  "sortBy": "date",
  "scrapeDetails": true,
  "maxResults": 200
}
```

| Field | Type | Description |
| --- | --- | --- |
| `keywords` | string | What to search for, as you'd type it on LinkedIn |
| `location` | string | Where to search — use LinkedIn's own wording |
| `datePosted` | string | Any time, past 24 hours, past week, past month |
| `sortBy` | string | Relevance, or most recent |
| `jobTypes` | array | Full-time, part-time, contract, temporary, internship, volunteer, other |
| `experienceLevels` | array | Internship, entry, associate, mid-senior, director, executive |
| `workplaceTypes` | array | On-site, remote, hybrid |
| `scrapeDetails` | boolean | Open each posting for description, salary and criteria |
| `maxResults` | integer | Hard cap on records returned |
| `searchQueries` | array | Run several searches in one go (advanced) |
| `geoId`, `companyId` | string | LinkedIn's own numeric IDs, for precise targeting |
| `requestIntervalMs` | integer | Delay between requests, default 500ms |

### Output

```json
{
  "jobId": "4443916725",
  "title": "Data Engineer",
  "company": "Harnham",
  "companyUrl": "https://uk.linkedin.com/company/harnham",
  "location": "Redwood City, CA",
  "postedAt": "2026-08-12",
  "postedRelative": "2 days ago",
  "salaryRange": "$120,000.00/yr - $150,000.00/yr",
  "seniorityLevel": "Associate",
  "employmentType": "Full-time",
  "jobFunction": "Engineering and Analyst",
  "industries": "Technology, Information and Media",
  "applicants": "Over 200 applicants",
  "applicantCount": 200,
  "description": "Data Engineer Location: Redwood City, CA (Hybrid) …",
  "jobUrl": "https://www.linkedin.com/jobs/view/data-engineer-at-harnham-4443916725",
  "companyLogo": "https://media.licdn.com/dms/image/…",
  "searchKeywords": "data engineer",
  "searchLocation": "United States",
  "scrapedAt": "2026-08-15T12:33:26.871Z"
}
```

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

#### Data fields

| Field | Description |
| --- | --- |
| `jobId`, `title`, `jobUrl` | Job identity and a clean link, with tracking parameters stripped |
| `company`, `companyUrl`, `companyLogo` | The employer |
| `location` | As published on the posting |
| `postedAt`, `postedRelative` | Machine-readable date, plus LinkedIn's "2 days ago" wording |
| `salaryRange` | Published range — only where the employer disclosed one |
| `seniorityLevel`, `employmentType`, `jobFunction`, `industries` | LinkedIn's own classification *(details only)* |
| `applicants`, `applicantCount` | How many have applied *(details only)* |
| `description` | Full posting text, tags stripped *(details only)* |
| `searchKeywords`, `searchLocation`, `scrapedAt` | Which search produced the row, and when |

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

Priced **per job record returned**, capped by **Maximum results**. No proxies and no browser, so the platform cost underneath is small.

Without details, a run collects roughly **1,000 jobs per minute**. With **Include full job details** on, each job needs one extra request, so the same run takes about **10× longer** — budget accordingly.

### Tips

- **LinkedIn caps any single search at 1,000 results.** This is LinkedIn's limit, not the Actor's — paging stops there. To go wider, split the work: several cities instead of one country, or several job titles instead of one broad term. Use **Multiple searches** to do that in a single run.
- **Use "Most recent" for monitoring.** Relevance ordering shuffles between runs; date ordering puts genuinely new postings first.
- **`salaryRange` needs details turned on**, and only appears where the employer published a range. Many postings have none — that is the employer's choice, not missing data.
- **Deduplicate on `jobId`.** Results are already deduplicated within a run, including across multiple searches.
- **Repeating the same search can return new jobs.** LinkedIn's result set is not stable — a second pass often surfaces postings the first missed.
- **`applicantCount` is a floor, not exact.** LinkedIn reports "Over 200 applicants" once a posting passes that mark, so 200 means "at least 200".
- **Rate limiting is handled for you.** The default 500ms spacing runs cleanly; if LinkedIn does push back, the Actor pauses, widens the gap and carries on rather than failing the run.

### FAQ

**Do I need a LinkedIn account, cookie or session token?**
No. This reads only what LinkedIn serves to signed-out visitors, so there is no account to connect and no account to get restricted.

**Do I need proxies?**
No. It runs from Apify's own infrastructure without a proxy. If you run very large volumes you may want to add one, but nothing here requires it.

**Can it scrape people, profiles or contact details?**
No. This Actor returns job postings only — it does not touch profiles, connections or personal contact information.

**Why did I get fewer results than the number LinkedIn shows?**
LinkedIn's headline count is an estimate for the whole query; the pageable set is capped at 1,000. Narrow the search and run several instead.

**Why is the description empty?**
Turn on **Include full job details**. Without it, the Actor only reads search result cards, which do not carry description text.

**Can I schedule it?**
Yes — use Apify Schedules. Sort by **Most recent**, and deduplicate on `jobId` against what you already stored.

### Legal note

This Actor collects only publicly accessible job postings — no login, no private data, no personal profiles. Job postings are published by employers for public consumption. You are responsible for how you use the data, including compliance with LinkedIn's terms and any applicable data-protection law in your jurisdiction.

### Support

Found a bug or need another field? Open an issue on the **Issues** tab. Custom data pipelines and bespoke scraping work are available on request.

# Actor input Schema

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

What to search for, exactly as you would type it into LinkedIn — e.g. "data engineer", "product manager". Leave blank to get everything matching your other filters.

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

Where to search, e.g. "United States", "London, England, United Kingdom", "Remote". Use the same wording LinkedIn suggests in its own location box.

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

Only return jobs posted within this window.

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

Relevance is LinkedIn's own ranking. Most recent is better for monitoring, since new postings appear first.

## `jobTypes` (type: `array`):

Employment types to include. Leave empty for all.

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

Seniority levels to include. Leave empty for all.

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

On-site, remote or hybrid. Leave empty for all.

## `scrapeDetails` (type: `boolean`):

Open each posting to add the full description, seniority level, job function, industries, applicant count and salary range. This makes the run roughly 10x longer, because it needs one extra request per job.

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

Hard cap on job records returned. You are charged per record. Note that LinkedIn returns at most 1,000 jobs for any single search — add more searches to go beyond that.

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

Run several searches in one go. Each entry is an object like {"keywords": "data engineer", "location": "Berlin"}. Filters above apply to every search unless the entry overrides them. Results are deduplicated by job ID.

## `geoId` (type: `string`):

Optional. LinkedIn's internal location ID, taken from the geoId parameter in a LinkedIn jobs search URL. More precise than a location name when a place name is ambiguous.

## `companyId` (type: `string`):

Optional. Restrict results to one company, using the numeric ID from the f\_C parameter of a LinkedIn jobs search URL.

## `requestIntervalMs` (type: `integer`):

How long to wait between requests. The default of 500ms is the fastest pace that runs cleanly. Raise it if you see rate-limit warnings in the log.

## Actor input object example

```json
{
  "keywords": "data engineer",
  "location": "United States",
  "datePosted": "any",
  "sortBy": "relevance",
  "jobTypes": [],
  "experienceLevels": [],
  "workplaceTypes": [],
  "scrapeDetails": false,
  "maxResults": 200,
  "requestIntervalMs": 500
}
```

# Actor output Schema

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

No description

## `runSummary` (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": "data engineer",
    "location": "United States"
};

// Run the Actor and wait for it to finish
const run = await client.actor("bercikgroup/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": "data engineer",
    "location": "United States",
}

# Run the Actor and wait for it to finish
run = client.actor("bercikgroup/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": "data engineer",
  "location": "United States"
}' |
apify call bercikgroup/linkedin-jobs-scraper --silent --output-dataset

```

## MCP server setup

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