# LinkedIn Jobs Scraper — Filter-Based, No Login (`scrapesage/linkedin-jobs-scraper`) Actor

Scrape LinkedIn jobs from public guest pages with friendly filters — no URL building, no login. Complete public job + company data, dedup + repost detection, a 1000-result-cap workaround, and an incremental "new jobs since last run" monitor. Excludes recruiter personal data by design.

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

## Pricing

$5.00 / 1,000 job results

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 a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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 — Postings, Salaries & Company Data (No Login)

Extract **complete public job data** from [LinkedIn Jobs](https://www.linkedin.com/jobs) — across **any keyword, location, seniority, job type, workplace type, salary band and date range**. Get the fields that actually matter: **job title, company, location, on-site/remote/hybrid, full job description (text + HTML), parsed salary range, applicants count, posting date, seniority, employment type, job function and industries** — plus optional **company size, industry, follower count, website and HQ** on every result.

No login, no cookies, no browser automation — fast direct-from-guest-page extraction with the richest job dataset in the category, and **zero recruiter / poster personal data by design**.

### Why this LinkedIn jobs scraper?

Most LinkedIn job scrapers return a thin row — title, company, a URL — and either leave the rest empty or scrape recruiter personal data that gets the dataset (and you) delisted. This actor pulls LinkedIn's public guest job pages and ships **every business field on the posting**, deduped and repost-aware, while a hard guard keeps named-individual data out:

| Data | Typical scrapers | This actor |
|---|---|---|
| Title, company, location | ✅ | ✅ |
| On-site / remote / hybrid | partial | ✅ |
| Full job description (text **and** HTML) | partial | ✅ |
| Parsed salary range (min/max/currency/period) | ❌ | ✅ |
| Applicants count | ❌ | ✅ |
| Seniority, employment type, job function, industries | partial | ✅ all |
| Posting date (ISO + epoch) | partial | ✅ |
| Easy-Apply / promoted / **repost** flags | ❌ | ✅ |
| Cross-search de-duplication | partial | ✅ |
| Company size, industry, followers, website, HQ | ❌ | ✅ (optional) |
| 1,000-result-cap workaround (auto-split) | ❌ | ✅ |
| Incremental "new jobs since last run" monitor | ❌ | ✅ |
| Recruiter / job-poster personal data | ⚠️ often scraped — delisting & legal risk | ✅ **excluded by design** |

### Use cases

- **Talent sourcing & recruiting intelligence** — see who's hiring, for which roles, where, and at what seniority. Filter by keyword, location, experience level or specific companies and build a live map of open roles — without touching any individual's personal data.
- **Job-market & salary intelligence** — track asking salaries, demand by role/location/industry, and the remote-vs-on-site mix. The parsed `salaryRange` turns free-text pay into structured min/max you can chart.
- **Hiring-intent signals for B2B sales** — a company opening roles is a growth/budget signal. Pull postings by `companyIds`, enrich with company size and industry, and prioritize accounts that are expanding. Account-level signal only — no contacts harvested.
- **Job boards, aggregators & ATS tools** — feed clean, structured, deduped postings (with full descriptions) straight into your own search app, alerting product or pricing model.
- **Competitor & market monitoring** — watch a competitor's open roles, headcount range and follower growth over time to read expansion, freezes and new-market entry.

### How to use

1. [Sign up for Apify](https://console.apify.com/sign-up) — the free plan is enough to try this actor.
2. Open the **LinkedIn Jobs Scraper**, enter a **keyword + location** (or paste a LinkedIn jobs search URL), set any filters you want, and click **Start**.
3. Watch results stream into the dataset table as each job is parsed.
4. **Export** as JSON, CSV, Excel, XML, or RSS — or pull results programmatically via the [Apify API](https://docs.apify.com/api/v2).

#### Tip: any LinkedIn jobs search URL works

Open [LinkedIn Jobs](https://www.linkedin.com/jobs/search), set **any** filters in the UI — keywords, location, date posted, experience level, job type, remote, salary, and so on — then copy the URL from your browser and paste it into **searchUrls**. The scraper reproduces that exact search and paginates through every page. No need to learn parameters or build URLs by hand.

### Input

You can drive the actor two ways: the **friendly filter builder** (keywords + location + filters) or by **pasting LinkedIn search URLs**. Mix and match as many searches as you like.

```json
{
    "keywords": "software engineer",
    "location": "United States",
    "datePosted": "pastWeek",
    "workplaceType": ["remote", "hybrid"],
    "experienceLevel": ["midsenior", "director"],
    "salaryBucket": "120k",
    "sortBy": "date",
    "maxJobs": 500,
    "scrapeJobDetails": true,
    "scrapeCompanyDetails": true,
    "splitByLocation": false,
    "incrementalMode": false
}
````

Core fields:

- **keywords** — the job search terms (e.g. `software engineer`, `product manager`). Optional if you supply `searchUrls`.
- **location** — free-text place (`United States`, `London`, `Toronto, ON`). Pair with **distanceMiles** to widen the radius, or set **geoId** for an exact LinkedIn location.
- **searchUrls** — one or more LinkedIn jobs search/listing pages with whatever filters you want, used instead of (or alongside) the builder.
- **datePosted** — `any`, `pastHour`, `past24h`, `pastWeek`, `pastMonth`.
- **workplaceType** — any of `onsite`, `remote`, `hybrid`.
- **jobType** — any of `fulltime`, `parttime`, `contract`, `temporary`, `internship`, `volunteer`, `other`.
- **experienceLevel** — any of `internship`, `entry`, `associate`, `midsenior`, `director`, `executive`.
- **salaryBucket** — minimum-pay floor: `any`, `40k`, `60k`, `80k`, `100k`, `120k`, `140k`, `160k`, `180k`, `200k`.
- **companyIds** — restrict to specific companies by their numeric LinkedIn company IDs.
- **sortBy** — `relevance` (default) or `date` (newest first — best for monitoring).
- **scrapeJobDetails** *(default true)* — open each posting for the full description, salary, applicants, seniority and more. Turn off for a faster, card-only pull.
- **scrapeCompanyDetails** *(default false)* — also fetch each company's size, industry, followers, website and HQ. Adds the eight `company*` detail fields.
- **maxJobs** *(prefilled 100)* — overall cap across all searches. Clear it (or raise it) for a full pull; the prefill just keeps the example run fast.
- **maxJobsPerSearch** — per-search cap, useful when splitting.
- **splitByLocation** / **splitCountry**, **splitByDateWindow** — the 1,000-cap workarounds (see below).
- **incrementalMode** / **monitorKey** / **firstRunLookback** — the "only new jobs" monitor (see below).
- **searchConcurrency** *(default 4)* — how many searches run in parallel, each on its own residential IP.
- **proxyConfiguration** *(default Apify Residential)* — required; LinkedIn guest pages block datacenter IPs. Keep the default.

### Output

One record per job posting:

```json
{
    "id": "4416101000",
    "jobId": "4416101000",
    "url": "https://www.linkedin.com/jobs/view/4416101000",
    "title": "Senior Software Engineer, Backend",
    "companyName": "Acme Cloud",
    "companyLinkedinUrl": "https://www.linkedin.com/company/acme-cloud",
    "companyLogo": "https://media.licdn.com/dms/image/v2/abc123/company-logo_200_200/0/...",
    "location": "New York, NY",
    "workplaceType": "hybrid",
    "employmentType": "Full-time",
    "seniorityLevel": "Mid-Senior level",
    "experienceLevel": "Mid-Senior level",
    "jobFunctions": ["Engineering", "Information Technology"],
    "industries": ["Software Development"],
    "salary": "$160,000/yr - $210,000/yr",
    "salaryRange": { "min": 160000, "max": 210000, "currency": "USD", "period": "year" },
    "applicantsCount": 47,
    "easyApply": false,
    "isPromoted": false,
    "isReposted": false,
    "postedAt": "2026-06-12T00:00:00.000Z",
    "postedAtTimestamp": 1749686400000,
    "applyMethod": "offsite",
    "jobUrl": "https://www.linkedin.com/jobs/view/4416101000",
    "descriptionText": "We're hiring a Senior Backend Engineer to design and scale our event pipeline. You'll own services end to end…",
    "descriptionHtml": "<p>We're hiring a <strong>Senior Backend Engineer</strong> to design and scale our event pipeline…</p>",

    "companyWebsite": "https://www.acmecloud.com",
    "companyDescription": "Acme Cloud builds real-time data infrastructure for modern applications…",
    "companySlogan": "Ship data faster.",
    "companyEmployeesCount": 1280,
    "companyEmployeeCountRange": "1,001-5,000 employees",
    "companyIndustries": ["Software Development"],
    "companyFollowerCount": 84211,
    "companyAddress": { "country": "US", "locality": "New York", "region": "NY", "postalCode": "10001", "street": "100 Example Ave" },

    "sourceType": "search",
    "sourceQuery": "\"software engineer\" in New York, NY",
    "status": "ok",
    "statusReason": "",
    "scrapedAt": "2026-06-14T01:30:00.000Z"
}
```

The eight company detail fields (`companyWebsite` through `companyAddress`) are included **only** when `scrapeCompanyDetails` is on — a default run omits them entirely rather than padding empty columns. Every run also appends one **run-summary row** (`recordType: run-summary`) with totals; rows that legitimately have no job carry a plain-English `status` explaining why, so an empty search reads as a clean result, not a failure.

**No personal data.** Recruiter, job-poster and hiring-team fields — and any LinkedIn member-profile (`/in/`) URL, even inside description HTML — are stripped at the single output chokepoint by construction and a defensive guard. The dataset is business and posting data only.

### Monitoring — get only new jobs

Turn on **incrementalMode** and the actor remembers which postings it has already returned for each search (in a persistent key-value store) and, on the next run, outputs **only jobs that are new since last time**.

This is a pure result filter — the actor still runs once and exits, so it works **with**, not against, the Apify scheduler:

1. Set `incrementalMode: true`, a stable `monitorKey` (e.g. `frontend-remote-us`), and `sortBy: date`.
2. Use `firstRunLookback` (`past24h` / `pastWeek` / `pastMonth`) to bound how far back the very first run reaches.
3. Create an Apify [Schedule](https://docs.apify.com/platform/schedules) (e.g. hourly) for the actor.
4. Add a [webhook](https://docs.apify.com/platform/integrations/webhooks) or [Zapier](https://docs.apify.com/platform/integrations/zapier) step to push each new posting into your ATS, Slack or a spreadsheet.

A run that finds nothing new finishes as a clean success with zero duplicates — you get a live feed of just-posted roles and nothing you've already seen.

### Automate & schedule

Run this actor on autopilot and pull results into your own stack:

- **[Apify API](https://docs.apify.com/api/v2)** — start runs, fetch datasets, and manage schedules over REST.
- **[apify-client for JavaScript](https://docs.apify.com/api/client/js/)** and **[apify-client for Python](https://docs.apify.com/api/client/python/)** — official SDKs.
- **[Schedules](https://docs.apify.com/platform/schedules)** — run it hourly/daily to monitor new roles in a market, function, or salary band.
- **[Webhooks](https://docs.apify.com/platform/integrations/webhooks)** — trigger downstream actions (ATS import, Slack alert, email sequence) the moment a run finishes.

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'MY_APIFY_TOKEN' });

const run = await client.actor('scrapesage/linkedin-jobs-scraper').call({
    keywords: 'software engineer',
    location: 'United States',
    datePosted: 'pastWeek',
    maxJobs: 500,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(`Got ${items.length} job postings`);
```

### Integrate with any app

Connect the dataset to 5,000+ apps — no code required:

- **[Make](https://docs.apify.com/platform/integrations/make)** — multi-step automation scenarios.
- **[Zapier](https://docs.apify.com/platform/integrations/zapier)** — push new postings straight into your ATS or CRM.
- **[Slack](https://docs.apify.com/platform/integrations/slack)** — get notified when a monitored search finds new roles.
- **[Google Drive / Sheets](https://docs.apify.com/platform/integrations/drive)** — auto-export every run to a spreadsheet.
- **[Airbyte](https://docs.apify.com/platform/integrations/airbyte)** — pipe results into your data warehouse.
- **[GitHub](https://docs.apify.com/platform/integrations/github)** — trigger runs from commits or releases.

### Use with AI assistants (MCP)

The output is clean, LLM-ready JSON. You can call this actor from Claude, ChatGPT, or any agent framework through the **[Apify MCP server](https://docs.apify.com/platform/integrations/mcp)** — ask your assistant to "find remote senior software engineer roles posted in the last week in the US and summarize the salary ranges" and let it run this scraper for you.

### More scrapers from scrapesage

Build a complete market-intelligence and lead-gen data stack:

- **[99.co Property Scraper](https://apify.com/scrapesage/99-co-property-scraper)** — Singapore property listings, prices and agent leads.
- **[SGCarMart Used Car Scraper](https://apify.com/scrapesage/sgcarmart-used-car-scraper)** — Singapore used-car prices, COE/OMV/ARF and dealer leads.
- **[Airbnb Scraper & Market Monitor](https://apify.com/scrapesage/airbnb-scraper)** — short-stay listings, prices and availability.
- **[Eventbrite Scraper](https://apify.com/scrapesage/eventbrite-scraper)** — events + organizer leads with contact data.
- **[Facebook Ad Library Scraper](https://apify.com/scrapesage/facebook-ad-library-scraper)** — competitor ad intelligence.
- **[Google Ads Transparency Scraper](https://apify.com/scrapesage/google-ads-transparency-scraper)** — who's advertising what on Google.
- **[Telegram Scraper](https://apify.com/scrapesage/telegram-scraper)** — channels, messages, media and search.

### Tips

- **Exhaust a big search**: LinkedIn caps guest search at ~1,000 results. To pull a large market fully, turn on **splitByLocation** (with **splitCountry**) to fan a country out into cities, or **splitByDateWindow** to slice by time — each sub-search paginates independently and runs in parallel on its own IP, multiplying what you can reach.
- **Cheapest, freshest feed**: combine `sortBy: date` + `incrementalMode` + an hourly [Schedule](https://docs.apify.com/platform/schedules) so each run scrapes only the handful of brand-new postings.
- **Keep the residential proxy**: LinkedIn guest pages block datacenter IPs; the default Apify Residential proxy is required.
- **Structured pay**: read `salaryRange` (min/max/currency/period) for analysis and keep the raw `salary` string for display — the parser recovers ranges from free-text descriptions and corrects misread hourly/annual values.
- **Skip the detail fetch** for a quick inventory: set `scrapeJobDetails: false` to list cards fast, then re-run with details on the slice you care about.

### FAQ

**Which jobs does it cover?** Any public LinkedIn job search — every keyword, location, seniority, job type, workplace type, salary band and date range, via either the filter builder or a pasted search URL.

**Do I need to learn URL parameters?** No. Apply filters on LinkedIn normally and paste the resulting URL into `searchUrls`, or just use the keyword/location/filter inputs. The scraper mirrors your search exactly.

**Does it scrape recruiter or applicant personal data?** No — and it can't. Job-poster, recruiter and hiring-team fields, plus any member-profile (`/in/`) URL, are removed at the only point where records are built, with a defensive guard as backstop. You get business and posting data only, which keeps the dataset compliant and delisting-safe.

**Where does the salary come from?** From the posting itself. When LinkedIn shows pay, the actor keeps the raw text in `salary` and parses a structured `salaryRange`; when a posting omits pay, both are `null`.

**How do I get only new jobs?** Turn on `incrementalMode` with a stable `monitorKey` and sort by `date`, then schedule the actor. Each run outputs only postings not seen on previous runs — no duplicates.

**Can I export to Google Sheets, CSV, or Excel?** Yes — one click in the dataset view, or automatically on every run via the [Google Drive integration](https://docs.apify.com/platform/integrations/drive).

**Is scraping LinkedIn legal?** This actor collects publicly available job-posting data only and excludes personal data by design. You are responsible for using the data in compliance with applicable laws and LinkedIn's terms.

**A field is `null` — why?** Some postings genuinely omit a value (no listed salary, no applicants count, a company that hides its size). Fields are `null` only when the data isn't published, never because the scraper skipped them. Company detail fields are absent (not null) unless `scrapeCompanyDetails` is on.

**A search returned fewer than I expected.** LinkedIn's ~1,000-result guest cap is per search — split by location or date window to go deeper. Any empty or capped run still finishes as a success with a `status` explaining exactly what happened.

### Need help?

Open an issue on the actor's **Issues** tab, or visit the [Apify help center](https://help.apify.com/). Feature requests are welcome — this actor is actively maintained.

# Actor input Schema

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

Job title / keywords. Supports boolean operators in UPPERCASE and "exact phrases", e.g. python AND django, "product manager", developer NOT senior.

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

Plain-text location, e.g. "United States", "London", "Berlin". Works on its own; pair it with a Geo ID for precision.

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

Numeric LinkedIn geo id (the tail of urn:li:geo:<id>). Overrides location precision. Examples: United States 103644278, United Kingdom 101165590, Worldwide 92000000, London 102257491, San Francisco Bay Area 90000084.

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

How recently the job was posted.

## `workplaceType` (type: `array`):

On-site, remote, and/or hybrid. Select any combination.

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

Employment type. Select any combination.

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

Seniority. Select any combination.

## `salaryBucket` (type: `string`):

Filter to jobs that publicly state at least this base salary. US-market oriented; sharply reduces result counts because most postings don't expose salary.

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

Restrict to specific companies by numeric LinkedIn company id (f\_C). One id per line.

## `distanceMiles` (type: `integer`):

Search radius around the location/geo id. Common values: 10, 25, 50, 100.

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

Relevance (LinkedIn default) or most recent first. Incremental mode always uses most-recent internally.

## `searchUrls` (type: `array`):

Raw LinkedIn public job-search URLs, one per line.

## `scrapeJobDetails` (type: `boolean`):

Fetch each job's detail page for the full description, criteria (seniority, employment type, job function, industries), apply URL, applicant count, salary, and validity dates. One extra request per job. Turn off for a faster, card-only run.

## `scrapeCompanyDetails` (type: `boolean`):

Also fetch each company's public page for business data (website, description, employee count/range, industries, HQ address, followers). Extra requests. Company data only — never personal data.

## `maxJobs` (type: `integer`):

Hard cap on total job results across all searches. Leave empty for no overall cap. Prefilled to 100 so the example/automated run finishes fast — raise it (or clear it) for a full pull.

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

Cap per individual search (per city/window when splitting). Leave empty for no per-search cap.

## `splitByLocation` (type: `boolean`):

Break a country-wide search into per-city searches. Requires a Split country below.

## `splitCountry` (type: `string`):

Country whose major cities are used when 'Split by location' is on.

## `splitByDateWindow` (type: `boolean`):

Alternative cap workaround: run the search across several recency windows (24h / week / month) and dedupe. Note: LinkedIn's date filter is cumulative, so this mainly improves fresh-first coverage rather than guaranteeing >1000 — location split is the reliable lever.

## `incrementalMode` (type: `boolean`):

Only return jobs not seen on a previous run (matched by Monitor key).

## `monitorKey` (type: `string`):

A stable name for this monitor (e.g. "react-devs-london"). State (seen job ids + watermarks) persists across runs under this key. Required when Incremental mode is on.

## `firstRunLookback` (type: `string`):

On the very first run of a monitor, how far back to seed. Subsequent runs return only new jobs.

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

Parallel requests. 12 is tested safe on Residential; raise toward 20 only if blocks stay at zero in the run summary.

## `maxRequestsPerMinute` (type: `integer`):

Global rate limit. 240 is tested block-free on Residential and ~4x faster than a polite crawl; lower it if you ever see blocks, raise toward 300 if you don't.

## `searchConcurrency` (type: `integer`):

How many searches to run at once when you split by location/date. Each search is still paged completely; running several in parallel fills the request-rate budget instead of stacking end-to-end. Has no effect on a single (un-split) search. The global rate limit above is the real ceiling, so 4 is plenty; lower it to 1 if parallel runs draw blocks.

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

Proxy settings. Apify Residential is strongly recommended — datacenter IPs get blocked by LinkedIn almost immediately.

## `debugDumpDetailHtml` (type: `boolean`):

Diagnostic only. Saves the raw HTML of the first job-detail page to the run's key-value store under DEBUG\_DETAIL\_HTML, so selector coverage can be checked against real markup. Leave off for normal runs.

## Actor input object example

```json
{
  "keywords": "software engineer",
  "location": "United States",
  "datePosted": "any",
  "workplaceType": [],
  "jobType": [],
  "experienceLevel": [],
  "salaryBucket": "any",
  "sortBy": "relevance",
  "scrapeJobDetails": true,
  "scrapeCompanyDetails": false,
  "maxJobs": 100,
  "splitByLocation": false,
  "splitByDateWindow": false,
  "incrementalMode": false,
  "firstRunLookback": "pastWeek",
  "maxConcurrency": 12,
  "maxRequestsPerMinute": 240,
  "searchConcurrency": 4,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "debugDumpDetailHtml": false
}
```

# Actor output Schema

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

All job postings scraped in this run, stored in the run's default dataset.

# 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",
    "maxJobs": 100,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapesage/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",
    "maxJobs": 100,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

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

```

## MCP server setup

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

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "LinkedIn Jobs Scraper — Filter-Based, No Login",
        "description": "Scrape LinkedIn jobs from public guest pages with friendly filters — no URL building, no login. Complete public job + company data, dedup + repost detection, a 1000-result-cap workaround, and an incremental \"new jobs since last run\" monitor. Excludes recruiter personal data by design.",
        "version": "1.0",
        "x-build-id": "KGwMRokJGKkqrrrEM"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/scrapesage~linkedin-jobs-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-scrapesage-linkedin-jobs-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/scrapesage~linkedin-jobs-scraper/runs": {
            "post": {
                "operationId": "runs-sync-scrapesage-linkedin-jobs-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/scrapesage~linkedin-jobs-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-scrapesage-linkedin-jobs-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "keywords": {
                        "title": "Keywords",
                        "type": "string",
                        "description": "Job title / keywords. Supports boolean operators in UPPERCASE and \"exact phrases\", e.g. python AND django, \"product manager\", developer NOT senior."
                    },
                    "location": {
                        "title": "Location",
                        "type": "string",
                        "description": "Plain-text location, e.g. \"United States\", \"London\", \"Berlin\". Works on its own; pair it with a Geo ID for precision."
                    },
                    "geoId": {
                        "title": "Geo ID (optional)",
                        "type": "string",
                        "description": "Numeric LinkedIn geo id (the tail of urn:li:geo:<id>). Overrides location precision. Examples: United States 103644278, United Kingdom 101165590, Worldwide 92000000, London 102257491, San Francisco Bay Area 90000084."
                    },
                    "datePosted": {
                        "title": "Date posted",
                        "enum": [
                            "any",
                            "pastHour",
                            "past24h",
                            "pastWeek",
                            "pastMonth"
                        ],
                        "type": "string",
                        "description": "How recently the job was posted.",
                        "default": "any"
                    },
                    "workplaceType": {
                        "title": "Workplace type",
                        "type": "array",
                        "description": "On-site, remote, and/or hybrid. Select any combination.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "onsite",
                                "remote",
                                "hybrid"
                            ],
                            "enumTitles": [
                                "On-site",
                                "Remote",
                                "Hybrid"
                            ]
                        },
                        "default": []
                    },
                    "jobType": {
                        "title": "Job type",
                        "type": "array",
                        "description": "Employment type. Select any combination.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "fulltime",
                                "parttime",
                                "contract",
                                "temporary",
                                "internship",
                                "volunteer",
                                "other"
                            ],
                            "enumTitles": [
                                "Full-time",
                                "Part-time",
                                "Contract",
                                "Temporary",
                                "Internship",
                                "Volunteer",
                                "Other"
                            ]
                        },
                        "default": []
                    },
                    "experienceLevel": {
                        "title": "Experience level",
                        "type": "array",
                        "description": "Seniority. Select any combination.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "internship",
                                "entry",
                                "associate",
                                "midsenior",
                                "director",
                                "executive"
                            ],
                            "enumTitles": [
                                "Internship",
                                "Entry level",
                                "Associate",
                                "Mid-Senior level",
                                "Director",
                                "Executive"
                            ]
                        },
                        "default": []
                    },
                    "salaryBucket": {
                        "title": "Minimum salary (USD)",
                        "enum": [
                            "any",
                            "40k",
                            "60k",
                            "80k",
                            "100k",
                            "120k",
                            "140k",
                            "160k",
                            "180k",
                            "200k"
                        ],
                        "type": "string",
                        "description": "Filter to jobs that publicly state at least this base salary. US-market oriented; sharply reduces result counts because most postings don't expose salary.",
                        "default": "any"
                    },
                    "companyIds": {
                        "title": "Company IDs (optional)",
                        "type": "array",
                        "description": "Restrict to specific companies by numeric LinkedIn company id (f_C). One id per line.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "distanceMiles": {
                        "title": "Distance (miles)",
                        "minimum": 0,
                        "maximum": 100,
                        "type": "integer",
                        "description": "Search radius around the location/geo id. Common values: 10, 25, 50, 100."
                    },
                    "sortBy": {
                        "title": "Sort by",
                        "enum": [
                            "relevance",
                            "date"
                        ],
                        "type": "string",
                        "description": "Relevance (LinkedIn default) or most recent first. Incremental mode always uses most-recent internally.",
                        "default": "relevance"
                    },
                    "searchUrls": {
                        "title": "Search URLs",
                        "type": "array",
                        "description": "Raw LinkedIn public job-search URLs, one per line.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "scrapeJobDetails": {
                        "title": "Scrape job detail pages",
                        "type": "boolean",
                        "description": "Fetch each job's detail page for the full description, criteria (seniority, employment type, job function, industries), apply URL, applicant count, salary, and validity dates. One extra request per job. Turn off for a faster, card-only run.",
                        "default": true
                    },
                    "scrapeCompanyDetails": {
                        "title": "Scrape company details",
                        "type": "boolean",
                        "description": "Also fetch each company's public page for business data (website, description, employee count/range, industries, HQ address, followers). Extra requests. Company data only — never personal data.",
                        "default": false
                    },
                    "maxJobs": {
                        "title": "Max jobs (overall)",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Hard cap on total job results across all searches. Leave empty for no overall cap. Prefilled to 100 so the example/automated run finishes fast — raise it (or clear it) for a full pull."
                    },
                    "maxJobsPerSearch": {
                        "title": "Max jobs per search",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Cap per individual search (per city/window when splitting). Leave empty for no per-search cap."
                    },
                    "splitByLocation": {
                        "title": "Split by location (per city)",
                        "type": "boolean",
                        "description": "Break a country-wide search into per-city searches. Requires a Split country below.",
                        "default": false
                    },
                    "splitCountry": {
                        "title": "Split country",
                        "enum": [
                            "US",
                            "GB",
                            "CA",
                            "DE",
                            "IN",
                            "AU",
                            "FR",
                            "NL",
                            "IE",
                            "ES"
                        ],
                        "type": "string",
                        "description": "Country whose major cities are used when 'Split by location' is on."
                    },
                    "splitByDateWindow": {
                        "title": "Split by date window",
                        "type": "boolean",
                        "description": "Alternative cap workaround: run the search across several recency windows (24h / week / month) and dedupe. Note: LinkedIn's date filter is cumulative, so this mainly improves fresh-first coverage rather than guaranteeing >1000 — location split is the reliable lever.",
                        "default": false
                    },
                    "incrementalMode": {
                        "title": "Incremental mode",
                        "type": "boolean",
                        "description": "Only return jobs not seen on a previous run (matched by Monitor key).",
                        "default": false
                    },
                    "monitorKey": {
                        "title": "Monitor key",
                        "type": "string",
                        "description": "A stable name for this monitor (e.g. \"react-devs-london\"). State (seen job ids + watermarks) persists across runs under this key. Required when Incremental mode is on."
                    },
                    "firstRunLookback": {
                        "title": "First-run lookback",
                        "enum": [
                            "past24h",
                            "pastWeek",
                            "pastMonth"
                        ],
                        "type": "string",
                        "description": "On the very first run of a monitor, how far back to seed. Subsequent runs return only new jobs.",
                        "default": "pastWeek"
                    },
                    "maxConcurrency": {
                        "title": "Max concurrency",
                        "minimum": 1,
                        "maximum": 20,
                        "type": "integer",
                        "description": "Parallel requests. 12 is tested safe on Residential; raise toward 20 only if blocks stay at zero in the run summary.",
                        "default": 12
                    },
                    "maxRequestsPerMinute": {
                        "title": "Max requests per minute",
                        "minimum": 1,
                        "maximum": 300,
                        "type": "integer",
                        "description": "Global rate limit. 240 is tested block-free on Residential and ~4x faster than a polite crawl; lower it if you ever see blocks, raise toward 300 if you don't.",
                        "default": 240
                    },
                    "searchConcurrency": {
                        "title": "Parallel searches",
                        "minimum": 1,
                        "maximum": 8,
                        "type": "integer",
                        "description": "How many searches to run at once when you split by location/date. Each search is still paged completely; running several in parallel fills the request-rate budget instead of stacking end-to-end. Has no effect on a single (un-split) search. The global rate limit above is the real ceiling, so 4 is plenty; lower it to 1 if parallel runs draw blocks.",
                        "default": 4
                    },
                    "proxyConfiguration": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Proxy settings. Apify Residential is strongly recommended — datacenter IPs get blocked by LinkedIn almost immediately.",
                        "default": {
                            "useApifyProxy": true,
                            "apifyProxyGroups": [
                                "RESIDENTIAL"
                            ]
                        }
                    },
                    "debugDumpDetailHtml": {
                        "title": "Dump one raw detail-page HTML (debug)",
                        "type": "boolean",
                        "description": "Diagnostic only. Saves the raw HTML of the first job-detail page to the run's key-value store under DEBUG_DETAIL_HTML, so selector coverage can be checked against real markup. Leave off for normal runs.",
                        "default": false
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
