# 🏆 AI Powered Linkedin Job Scraper⚡ Advanced Data Best Value 🏆 (`claygenius/cheapest-linkedin-job-scrapper`) Actor

Scrape public LinkedIn job search results by keyword/location or search URL. Includes job description, criteria, and company info — no login required.

- **URL**: https://apify.com/claygenius/cheapest-linkedin-job-scrapper.md
- **Developed by:** [Muhammad Shamshad Aslam](https://apify.com/claygenius) (community)
- **Categories:** Jobs, Automation, AI
- **Stats:** 3 total users, 2 monthly users, 97.3% runs succeeded, 8 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

## LinkedIn Jobs Scraper

Scrape job listings from LinkedIn's **public** job search — no login, no account risk, no browser overhead. Uses LinkedIn's guest API directly, so it's fast and returns clean structured data.

### Features

- ✅ **Two input modes** — paste LinkedIn search URLs (all filters preserved) or use keyword + location
- 🖼️ **Free card data** — `companyLogo`, `benefits` ("Actively Hiring"), `trackingId` and `inputUrl` come straight from the search card, so they cost no extra requests
- ✅ **Standard field names** — `id`, `link`, `companyName`, `salaryInfo`, `postedAt`, `descriptionText`, `industries`, `applicantsCount`, `companyEmployeesCount`, `applyMethod`
- ✅ **Full job details** — description (plain text + HTML), seniority level, employment type, job function, industries, applicant count, **salary** (read from the job page, where LinkedIn actually publishes it)
- ✅ **Deep company data** — website, **domain**, industry, employee count, headquarters, type, founding year, specialties, description. One cached request per company, so an employer with 30 jobs costs one fetch
- ✅ **Job poster** — recruiter name, title, and LinkedIn profile URL when the listing shows one
- 🚫 **Recruitment-agency filter** — flag or remove staffing firms. Employers whose LinkedIn **industry** is Staffing and Recruiting, Human Resources or similar are caught instantly with no AI call; optional OpenAI (`gpt-4o-mini`) reads company descriptions for the rest
- 🧹 **Data cleanup** — keep or drop jobs by words in the title, description or company description. **Whole-word matching**, so `software` never hits `Softwareentwickler`
- 🏢 **Max jobs per employer** — stop one large company flooding your results
- ⚡ **Per-job webhook** — each job POSTed to your endpoint as it is scraped, ready for Clay, n8n, Make or Zapier
- ✅ **Duplicate-free** — deduplicated by LinkedIn job ID, before enrichment, so a duplicate never costs a request
- ✅ **No login required** — scrapes the public jobs search, so no account can get banned

### Limits

LinkedIn caps **every** search at roughly 1,000 results, however many jobs match. That ceiling is LinkedIn's, not this Actor's, and it cannot be bypassed without corrupting relevance — filter-slicing tricks make LinkedIn loosen the keyword match, so a "software engineer" search starts returning unrelated intern postings. To collect more than 1,000, run several genuinely different searches — narrower keywords (`backend engineer`, `java developer`) or separate cities rather than a whole country — and deduplicate on `id`.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `searchUrls` | array | US software-engineer search | LinkedIn jobs search URLs; overrides keyword/location and every Option 2 field. Build them in an incognito window |
| `keyword` | string | — | Job title or keyword. Used only when no search URL is given |
| `location` | string | — | City, region, or country. Used only when no search URL is given |
| `postedWithin` | select | Any time | Past 24 hours / week / month |
| `easyApplyOnly` | boolean | `false` | Only jobs you can apply to without leaving LinkedIn |
| `maxItems` | integer | `0` | Jobs needed. `0` = scrape **all** results. Counts jobs actually kept when a cleanup filter is on |
| `maxJobsPerCompany` | integer | `0` | `0` = every job from every employer. Set 2–10 to spread results across more companies |
| `includeWords` / `includeIn` | array | — | Keep only jobs containing any of these words, in the fields you choose |
| `excludeWords` / `excludeIn` | array | — | Drop jobs containing any of these words. Exclude always beats include |
| `recruitmentAgencyHandling` | select | `off` | `off` / `flag` / `exclude`. Industry-based screen is free; OpenAI key optional |
| `openaiApiKey` | string | — | Your key, billed to you. Used only with `gpt-4o-mini` |
| `webhookUrl` | string | — | POST each job to your URL as it is scraped |
| `proxyConfig` | proxy | Residential | Residential is the default; datacenter is untested on these endpoints |

#### Example input

```json
{
  "searchUrls": ["https://www.linkedin.com/jobs/search?keywords=Software%20Engineer&location=United%20States&geoId=103644278"],
  "maxItems": 200
}
```

Only employers hiring directly, no interns, one job per company:

```json
{
  "keyword": "software engineer",
  "location": "United States",
  "maxItems": 500,
  "maxJobsPerCompany": 1,
  "excludeWords": ["intern", "graduate"],
  "excludeIn": ["title"],
  "recruitmentAgencyHandling": "exclude"
}
```

### Output

```json
{
  "id": "4425772390",
  "trackingId": "W6KvEeviLxwIxFaCblOv5Q==",
  "refId": null,
  "title": "Senior Structural Analysis Engineer",
  "companyName": "Turkish Aerospace",
  "companyLinkedinUrl": "https://tr.linkedin.com/company/turkishaerospace",
  "companyLogo": "https://media.licdn.com/dms/image/v2/.../company-logo_100_100",
  "benefits": ["Actively Hiring"],
  "location": "Islāmābād, Pakistan",
  "salaryInfo": "Base pay range $100,000.00/yr - $225,000.00/yr",
  "postedAt": "2026-06-15",
  "postedRelative": "4 weeks ago",
  "link": "https://www.linkedin.com/jobs/view/4425772390",
  "applyUrl": "https://www.linkedin.com/jobs/view/4425772390/",
  "applyMethod": "easy_apply",
  "jobPosterName": "ALP DÜNDAR",
  "jobPosterTitle": "Recruitment Specialist at Türk Havacılık ve Uzay Sanayii",
  "jobPosterProfileUrl": "https://tr.linkedin.com/in/alp-d%C3%BCndar-a98a9a1a1",
  "descriptionText": "We are proud to work for a company that we believe has a great purpose. …",
  "descriptionHtml": "<p><strong>Hello!</strong></p><p>…</p>",
  "seniorityLevel": "Associate",
  "employmentType": "Full-time",
  "jobFunction": "Engineering",
  "industries": "Engineering Services, Defense and Space Manufacturing, and Airlines and Aviation",
  "applicantsCount": "Over 200 applicants",
  "companyWebsite": "https://www.tusas.com/",
  "companyDomain": "tusas.com",
  "companyIndustry": "Aviation and Aerospace Component Manufacturing",
  "companyEmployeesCount": "10,001+ employees",
  "companyHeadquarters": "Ankara",
  "companyType": "Privately Held",
  "companyFounded": "1973",
  "companySpecialties": "aerospace, aviation, and defence",
  "companyDescription": "Türk Uçak Sanayii Anonim Ortaklığı (TUSAŞ) …",
  "isRecruitmentAgency": false,
  "recruitmentAgencyReason": null,
  "recruitmentAgencyMatchedBy": null,
  "inputUrl": "https://www.linkedin.com/jobs-guest/jobs/api/seeMoreJobPostings/search?keywords=..."
}
```

### Use cases

- **Lead generation** — companies actively hiring are companies actively buying; pair with a contact-finder for outreach
- **Recruiting & sourcing** — monitor fresh openings by market, function, or seniority
- **Market research** — hiring trends, in-demand skills, remote-work share by industry
- **Competitor analysis** — track who your competitors are hiring

### Tips

- Search URLs must come from the **public** (logged-out) jobs search — open an incognito window, set your filters, and copy the URL. Logged-in search URLs use different filters that the public API ignores.
- Full job **and** company details are always fetched — they are the point of the Actor, not an option.
- `recruitmentAgencyHandling` catches most agencies for free from LinkedIn's industry label; the OpenAI key only matters for firms whose industry and name give nothing away.
- Export as CSV, Excel, or JSON from the dataset tab, or pull via the Apify API.

# Actor input Schema

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

One or more LinkedIn jobs search URLs — every filter in the URL (keyword, location, date, remote, etc.) is preserved. Tip: build the URL in an incognito window; public results differ from logged-in results.

## `keyword` (type: `string`):

Job title or keyword to search for. Ignored when a Start URL is given — the URL already carries it.

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

City, region, or country (e.g. Berlin, Germany, United States). Ignored when a Start URL is given — the URL already carries it.

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

Only jobs posted within this time window. Ignored when a Start URL is given — the URL already carries it.

## `easyApplyOnly` (type: `boolean`):

Only jobs you can apply to without leaving LinkedIn. Off by default. Ignored when a Start URL is given — put the filter in the URL instead.

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

Maximum number of unique job listings to scrape. Leave empty (or 0) to scrape ALL results.

## `maxJobsPerCompany` (type: `integer`):

0 by default, which means ALL jobs from every employer. Set 2-10 to stop one large company flooding your results and spread the same number of jobs across far more employers. Skipped jobs do not count towards Max Items.

## `fetchCompanyDetails` (type: `boolean`):

ON by default. Adds companyEmployeesCount, companyWebsite, companyDomain, companyIndustry, companyHeadquarters, companyType, companyFounded, companySpecialties and companyDescription. Costs ONE extra request per employer (cached, so 30 jobs at one company is one request). Switch OFF to roughly halve the requests and finish much faster: the company page is an ordinary LinkedIn web page, not the guest API, and it is the request that gets blocked with HTTP 999. Job data — description, recruiter, salary, applicants — is unaffected either way. Note the staffing filter loses its industry and domain signals when this is off.

## `removeStaffingCompanies` (type: `boolean`):

Drops any job whose COMPANY NAME, DOMAIN or LINKEDIN INDUSTRY contains one of the words below. Free and instant — no OpenAI key, no AI call. Whole words only, so Staffordshire, Cheshire and Jobst are safe. WARNING: deliberately aggressive. The default list includes 'job', 'jobs' and 'talent', which will also remove real employers with those words in their name. Edit the word list below if that costs you too much.

## `staffingWords` (type: `array`):

Used by the toggle above. Leave as-is for the default list, or edit it — whatever is here REPLACES the default entirely. Remove 'job', 'jobs' or 'talent' if they catch real employers; add your own, e.g. a local agency chain. Whole-word matching, not case-sensitive, and dots and hyphens count as breaks so 'elite-staffing.com' matches.

## `staffingMatchIn` (type: `array`):

Which company fields the words above are checked in. All three by default. Industry is the strongest — it catches agencies whose name reveals nothing but whose LinkedIn industry is 'Staffing and Recruiting'. Untick a field to stop matching there, e.g. drop Company name if it is removing real employers.

## `includeWords` (type: `array`):

Add words one at a time. A job is kept if ANY of them appears in the fields you pick below. Whole words only — 'software' matches "Software Engineer" and "software-engineer", but not "Softwareentwickler". Not case-sensitive. Punctuation is fine: c++, .net, node.js.

## `includeIn` (type: `array`):

Which fields to search for the words above. Pick more than one to widen the net.

## `excludeWords` (type: `array`):

Add words one at a time. A job is dropped if ANY of them appears in the fields you pick below — e.g. intern, graduate, unpaid. Same whole-word matching.

## `excludeIn` (type: `array`):

Which fields to search for the words above.

## `recruitmentAgencyHandling` (type: `string`):

Works out whether an employer is a staffing agency or headhunter rather than a company hiring for itself. Model used: OpenAI gpt-4o-mini — the cheapest one that classifies this reliably, and the only model this Actor uses. A free name screen runs first and needs no key; add an OpenAI key below and it also reads company descriptions. Start with Flag only so you can check the verdicts before trusting it.

## `openaiApiKey` (type: `string`):

Your own key (sk-...), billed to your OpenAI account. Used only for the filter above, and only with gpt-4o-mini — the cheapest model that classifies this reliably. Without a key the free name screen still runs.

## `webhookUrl` (type: `string`):

Optional. Each job is POSTed to your URL as JSON, one request per job, as it is scraped. Leave empty to skip.

## `proxyConfig` (type: `object`):

Residential proxies recommended for large scrapes.

## Actor input object example

```json
{
  "searchUrls": [
    "https://www.linkedin.com/jobs/search?keywords=Software%20Engineer&location=United%20States&geoId=103644278&position=1&pageNum=0"
  ],
  "postedWithin": "",
  "easyApplyOnly": false,
  "maxItems": 10,
  "maxJobsPerCompany": 0,
  "fetchCompanyDetails": true,
  "removeStaffingCompanies": false,
  "staffingWords": [
    "staff",
    "staffing",
    "staffed",
    "recruit",
    "recruits",
    "recruiter",
    "recruiters",
    "recruiting",
    "recruitment",
    "talent",
    "talents",
    "hire",
    "hiring",
    "hires",
    "headhunter",
    "headhunters",
    "headhunting",
    "personnel",
    "employment",
    "placement",
    "placements",
    "manpower",
    "workforce",
    "resourcing",
    "temp",
    "temps",
    "temping",
    "job",
    "jobs",
    "jobot",
    "hr",
    "human resources",
    "humanresources"
  ],
  "staffingMatchIn": [
    "company",
    "companyDomain",
    "companyIndustry"
  ],
  "includeWords": [],
  "includeIn": [
    "title"
  ],
  "excludeWords": [],
  "excludeIn": [
    "title"
  ],
  "recruitmentAgencyHandling": "off",
  "webhookUrl": "",
  "proxyConfig": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `jobListings` (type: `string`):

Scraped job postings with title, company, location, description, and apply link

# 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 = {
    "searchUrls": [
        "https://www.linkedin.com/jobs/search?keywords=Software%20Engineer&location=United%20States&geoId=103644278&position=1&pageNum=0"
    ],
    "maxItems": 10,
    "proxyConfig": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("claygenius/cheapest-linkedin-job-scrapper").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 = {
    "searchUrls": ["https://www.linkedin.com/jobs/search?keywords=Software%20Engineer&location=United%20States&geoId=103644278&position=1&pageNum=0"],
    "maxItems": 10,
    "proxyConfig": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("claygenius/cheapest-linkedin-job-scrapper").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 '{
  "searchUrls": [
    "https://www.linkedin.com/jobs/search?keywords=Software%20Engineer&location=United%20States&geoId=103644278&position=1&pageNum=0"
  ],
  "maxItems": 10,
  "proxyConfig": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call claygenius/cheapest-linkedin-job-scrapper --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

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