# LinkedIn Jobs Scraper – No Login, Salary & Apply URL (`inovaflow/linkedin-jobs-scraper`) Actor

Scrape public LinkedIn job postings by title, location and company without a LinkedIn account or cookies. Typed JSON per job: salary, seniority, employment type, posted date, description, apply link — plus a companies-hiring summary.

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

## Pricing

from $1.00 / 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?

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

If you need LinkedIn job postings as data — for a job board, a recruiting pipeline, a market-salary study, or a list of companies that are hiring right now — you usually hit the same wall: LinkedIn wants you logged in, and tools that log in with your account get it restricted. **LinkedIn Jobs Scraper** reads the **public** LinkedIn job search that anyone can open in a private browser window. No LinkedIn account, no cookies, nothing of yours at risk — and every job comes back as clean, typed JSON.

- **Recruiters & sourcers** — fresh postings by title, city and seniority; see which companies are actively hiring.
- **Job boards & aggregators** — a scheduled feed of new roles with descriptions and salary where disclosed.
- **Sales & B2B teams** — hiring is a buying signal: the run's companies-hiring summary ranks employers by open roles.
- **Analysts & job seekers** — salary transparency data, remote share, demand by skill and region.

### What does LinkedIn Jobs Scraper do?

Give it job titles or keywords and locations (or LinkedIn company IDs, or a LinkedIn search URL copied from your browser) and it returns the matching **public LinkedIn job postings** — each with title, company, location, posted date, **salary (when the employer discloses it)**, **seniority level, employment type, job function, industries**, applicant count, the full description as text and HTML, the LinkedIn job URL and the apply type. Duplicates across searches are removed, and a **companies-hiring summary** is written next to the dataset. It works as a **LinkedIn jobs API**: call it from the Apify API, schedule it, or use it from an AI agent through MCP.

### Why use this LinkedIn jobs scraper?

- **No login, no cookies, ever.** It only reads what LinkedIn shows to logged-out visitors, so there is no account to get flagged.
- **Typed fields, not blobs.** Salary is parsed into `salaryMin` / `salaryMax` / `salaryCurrency` / `salaryPeriod`; seniority and employment type are separate fields; the posted date is ISO.
- **All of LinkedIn's public filters.** Posted within 24h / week / month, remote / hybrid / on-site, experience level, job type, Easy Apply, under-10-applicants, company IDs, sort by date.
- **Companies hiring.** Besides the jobs, you get `COMPANIES` — employers ranked by open roles with titles, locations, seniority mix and newest posting — a ready-made hiring-intent signal.
- **Pay per job, no minimums.** Most alternatives force 100+ results per run; here 5 jobs cost 5 jobs.
- **Built for pipelines.** Deduplicated across searches, `titleInclude` / `titleExclude` / `companyExclude` to cut noise, CSV exports, webhooks and 20+ export formats via Apify.

### What data does it extract?

| Field | Description |
| --- | --- |
| `id`, `url` | LinkedIn job ID and public job URL |
| `title`, `company`, `companyUrl`, `companySlug` | Job and employer identity |
| `location`, `isRemote` | Location as posted; remote flag |
| `postedAt`, `postedText` | ISO date and LinkedIn's relative text ("3 days ago") |
| `salaryText`, `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod` | Salary when the employer discloses it |
| `seniorityLevel`, `employmentType`, `jobFunction`, `industries` | LinkedIn's job criteria |
| `applicants`, `applicantsText` | Applicant count as shown ("Over 200 applicants", "Be among the first 25") |
| `description`, `descriptionHtml` | Full job description as readable text and as HTML |
| `applyType`, `applyUrl`, `externalLinks` | `easy_apply` (on LinkedIn) or `offsite`; the LinkedIn apply page, plus any application link the employer put in the description |
| `benefits` | Benefits teaser from the search card, when present |
| `searchKeywords`, `searchLocation`, `scrapedAt` | Provenance |

### How to scrape LinkedIn jobs without logging in

1. Open the Actor and click **Try for free**.
2. Enter **Job titles / keywords** (one per line) and **Locations** (one per line). Every keyword is searched in every location.
3. Optionally narrow with **Posted within**, **Workplace type**, **Experience level**, **Job type**, or paste a LinkedIn search URL from your browser.
4. Set **Max jobs per search** and click **Start**. 50 jobs with full details take about a minute.
5. Read the **Output** tab, download **JSON / CSV / Excel**, or open `COMPANIES` to see who is hiring most.

#### How to track new LinkedIn jobs every day

Set **Posted within** to *Past 24 hours*, **Sort by** to *Most recent*, and schedule the Actor daily in Apify. Add a webhook or an integration (Slack, Google Sheets, Make, Zapier, n8n) to push new rows where your team works.

#### How to find companies that are hiring

Run a broad search — e.g. keyword `sales development representative`, location `United States`, posted within *Past week*, 500 jobs — and open `COMPANIES`: employers sorted by open roles with their titles, locations and newest posting date.

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

Pay-per-event: **$0.001 per job delivered** plus a small run-start fee — **1,000 jobs for about $1**. Duplicates, filtered-out jobs and failed searches are never charged. Residential proxies (recommended for LinkedIn) are billed by Apify at your plan's rate; a job with details transfers about 60 KB.

### Input

Example — two roles in two cities, remote or hybrid, posted this week:

```json
{
    "keywords": ["python developer", "data engineer"],
    "locations": ["Berlin, Germany", "Amsterdam, Netherlands"],
    "postedWithin": "week",
    "workplace": ["remote", "hybrid"],
    "maxJobsPerSearch": 100,
    "titleExclude": ["senior", "lead"]
}
```

See the **Input** tab for every option. Company-only search: leave `keywords` empty and set `companyIds` (the number in `linkedin.com/company/<id>` or the `f_C` parameter of a LinkedIn search URL).

### Output

One dataset item per job:

```json
{
    "id": "4406548495",
    "title": "Software Engineer",
    "company": "Chalk",
    "companyUrl": "https://www.linkedin.com/company/chalkai",
    "location": "New York, NY",
    "isRemote": false,
    "postedAt": "2026-09-08",
    "postedText": "18 hours ago",
    "url": "https://www.linkedin.com/jobs/view/4406548495",
    "applyType": "easy_apply",
    "applyUrl": "https://www.linkedin.com/jobs/view/4406548495",
    "salaryText": "$170,000.00/yr - $280,000.00/yr",
    "salaryMin": 170000,
    "salaryMax": 280000,
    "salaryCurrency": "USD",
    "salaryPeriod": "year",
    "seniorityLevel": "Mid-Senior level",
    "employmentType": "Full-time",
    "jobFunction": "Engineering and Information Technology",
    "industries": "Software Development",
    "applicantsText": "Over 200 applicants",
    "applicants": 200,
    "description": "Chalk is building the data platform for machine learning…",
    "searchKeywords": "software engineer",
    "searchLocation": "New York"
}
```

The key-value store holds **`COMPANIES`** (JSON) and **`COMPANIES.csv`** — the companies-hiring summary — plus **`JOBS.csv`** and the **`OUTPUT`** run summary.

### Tips

- LinkedIn's public search returns at most about 1,000 jobs per search; split broad searches by city or seniority to go deeper.
- Set **Fetch full job details** off when you only need a title/company/location feed — runs are then 2–3× faster.
- Use `titleInclude` to tighten LinkedIn's fuzzy matching (`["python"]` keeps only titles that literally mention Python).
- `companyExclude: ["recruit", "staffing", "talent"]` removes most agency postings.

### FAQ

#### Do I need a LinkedIn account or cookies?

No. Nothing here logs in, so your own LinkedIn account is never involved or at risk.

#### Why is the apply link sometimes the LinkedIn page and not the company site?

LinkedIn shows the external application form only to logged-in members. For `offsite` jobs the Actor returns the LinkedIn job page and, when the employer put an application link in the description, that link in `externalLinks` / `applyUrl`.

#### Why do some jobs have no salary?

Only what the employer publishes is returned. Salary appears when it is disclosed on LinkedIn (common in the US, UK and parts of the EU).

#### Can it scrape recruiter names or applicant profiles?

No. The Actor extracts job postings and company names only — no personal data.

#### Is this legal?

Our Actors are ethical and do not extract any private user data. They only extract what employers have chosen to publish publicly on LinkedIn. Use the data in line with LinkedIn's terms and applicable law; consult a lawyer if you are unsure.

### Support

Open an issue in the **Issues** tab with your run ID and the search that misbehaved. The **API** tab shows how to call this Actor from code or from any MCP-capable AI agent.

# Actor input Schema

## `keywords` (type: `array`):

One search per line, as you would type it into LinkedIn Jobs — e.g. `python developer`, `account executive`, `registered nurse`. Boolean syntax works too: `"data engineer" AND (spark OR databricks)`.

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

Cities, regions or countries — every keyword is searched in every location. E.g. `Berlin, Germany`, `New York`, `United States`, `Worldwide`. Leave empty to search everywhere.

## `maxJobsPerSearch` (type: `integer`):

Cap per keyword × location combination. LinkedIn's public search exposes up to about 1,000 jobs per search; duplicates across searches are removed automatically.

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

Only jobs posted in this window.

## `workplace` (type: `array`):

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

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

Leave empty for all levels.

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

Leave empty for all types.

## `easyApply` (type: `boolean`):

Only jobs that can be applied to directly on LinkedIn.

## `under10Applicants` (type: `boolean`):

Only fresh jobs with fewer than 10 applicants.

## `companyIds` (type: `array`):

Numeric LinkedIn company IDs to restrict the search to specific employers (the number in `linkedin.com/company/<id>` URLs or in the `f_C` parameter of a LinkedIn search URL). Works with or without keywords.

## `titleInclude` (type: `array`):

Keep only jobs whose title contains at least one of these words (case-insensitive). Applied after LinkedIn's own matching, which can be loose.

## `titleExclude` (type: `array`):

Drop jobs whose title contains any of these words — e.g. `senior`, `intern`, `manager`.

## `companyExclude` (type: `array`):

Drop jobs from companies whose name contains any of these words — handy for skipping staffing agencies.

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

Opens every job page for the description, seniority level, employment type, job function, industries, salary and applicant count (one extra request per job). Turn off for a fast title/company/location list.

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

Order in which LinkedIn returns the results (affects which jobs make it under the cap).

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

Paste `linkedin.com/jobs/search/?keywords=…&location=…&f_TPR=…` URLs from your browser — their keywords, location and filters are used as additional searches.

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

How many LinkedIn pages are fetched in parallel. LinkedIn rate-limits aggressively; 5 is a safe default with residential proxies.

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

LinkedIn rate-limits datacenter IPs quickly, so residential proxies are the default. Keep it unless you have your own proxies.

## Actor input object example

```json
{
  "keywords": [
    "python developer"
  ],
  "locations": [
    "Berlin, Germany"
  ],
  "maxJobsPerSearch": 20,
  "postedWithin": "any",
  "workplace": [],
  "experienceLevels": [],
  "jobTypes": [],
  "easyApply": false,
  "under10Applicants": false,
  "includeDetails": true,
  "sortBy": "relevance",
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

One row per job posting with typed fields.

## `companies` (type: `string`):

Employers ranked by open roles found in this run — titles, locations, seniority mix, newest posting (hiring-intent signal).

## `jobsCsv` (type: `string`):

Spreadsheet-ready jobs export (first 5,000 rows).

## `summary` (type: `string`):

Counts: jobs delivered, with salary, companies hiring, filtered, duplicates, blocked requests.

# 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": [
        "python developer"
    ],
    "locations": [
        "Berlin, Germany"
    ],
    "maxJobsPerSearch": 20,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("inovaflow/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": ["python developer"],
    "locations": ["Berlin, Germany"],
    "maxJobsPerSearch": 20,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("inovaflow/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": [
    "python developer"
  ],
  "locations": [
    "Berlin, Germany"
  ],
  "maxJobsPerSearch": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call inovaflow/linkedin-jobs-scraper --silent --output-dataset

```

## MCP server setup

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