# LinkedIn Jobs Scraper - No Login, With Applicant Counts (`renzomacar/linkedin-jobs-scraper`) Actor

Scrape LinkedIn job listings without a login or cookies. Title, company, location, posting date, salary and the direct job URL - plus the full description, seniority, employment type, industry and how many people have applied.

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

## Pricing

from $4.00 / 1,000 job scrapeds

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

## LinkedIn Jobs Scraper — no login, with applicant counts

Scrape LinkedIn job listings without an account, a password or a cookie. Give it a job title and
a location and it returns every posting LinkedIn shows to a logged-out visitor, with the direct
link to each job.

It also answers the question most job scrapers leave out: **how many people have already
applied.**

### What you get per job

| Field | Example |
|---|---|
| `title` | Data Engineer, AWS DC Central Operations |
| `company` | Amazon Web Services (AWS) |
| `location` | Seattle, WA |
| `postedAt` | 2026-08-29 |
| `applicants` | 31 |
| `salary` | $120,000/yr - $160,000/yr *(when LinkedIn shows it)* |
| `url` | direct link to the posting |
| `isPromoted` | `true` when the company paid to place it |

Switch on **Fetch the full job page** and each row also carries the complete job description,
`seniority`, `employmentType`, `jobFunction` and `industries`.

### Why the applicant count matters

A role with 12 applicants and one with 600 are not the same opportunity, and LinkedIn only shows
that number on the job page itself. If you are tracking a market, sourcing candidates or deciding
where to apply, it is the single most useful number on the page — and it is in every row here.

`isPromoted` works the same way: promoted jobs sit at the top because someone paid for the slot,
not because they match your search better. Knowing which is which changes how you read the list.

### What it will not do

Said here rather than discovered halfway through a run:

- **The remote-work filter does not work.** LinkedIn ignores `f_WT` on the logged-out endpoint —
  the same ten jobs come back with and without it — so this Actor does not offer a filter it
  cannot honour. Filter `location` for "Remote" instead, or filter the output yourself.
- **LinkedIn caps a search at roughly 400-500 jobs.** Past that it stops serving results no
  matter what you ask for. To go wider, split the search: more keywords, or narrower locations.
- **No private data.** Only what LinkedIn publishes to visitors who are not signed in. No
  recruiter contacts, no candidate profiles, no gated fields.

The date filter *does* work: "Past 24 hours" returns a completely different set of postings.

### Typical uses

- **Recruiters** watching which companies are hiring for a role, and how much competition each
  posting already has.
- **Job seekers** filtering for postings from the last 24 hours with few applicants.
- **Market and salary research** — pull a title across several cities and compare the salary
  ranges LinkedIn discloses.
- **Competitor tracking** — watch a rival's open roles to see which teams they are growing.

### Input

```json
{
  "keywords": ["data engineer", "machine learning engineer"],
  "location": "United States",
  "maxResultsPerKeyword": 100,
  "includeDescription": true,
  "datePosted": "r86400",
  "sortBy": "DD"
}
```

Each keyword is searched separately. A job that appears in more than one page of the same search
is delivered — and charged — only once.

### Output

```json
{
  "jobId": "4460531927",
  "title": "Data Engineer, AWS DC Central Operations",
  "company": "Amazon Web Services (AWS)",
  "location": "Seattle, WA",
  "postedAt": "2026-08-29",
  "isPromoted": false,
  "applicants": 31,
  "seniority": "Not Applicable",
  "employmentType": "Full-time",
  "industries": "IT Services and IT Consulting",
  "descriptionLength": 5323,
  "url": "https://www.linkedin.com/jobs/view/..."
}
```

### Pricing

Pay per event: a small charge to start the run, then a charge per job delivered. A job that comes
back twice in the same search is charged once. If a run delivers nothing, there is nothing to
charge for.

### Notes on reliability

LinkedIn serves this endpoint to residential connections and turns away datacenter traffic, so
the Actor routes every request through a residential proxy and retries from a different IP when
it is refused. That is why a run occasionally takes longer than you would expect: it is working
through refusals rather than returning you an empty result.

### Common jobs to be done

Each of these is a real query someone types into a scraper marketplace. The Actor answers all of
them from the same run, so they are listed plainly rather than buried in a feature table.

#### Find LinkedIn jobs posted in the last 24 hours

Set `datePosted` to "Past 24 hours" and `sortBy` to "Most recent". Verified: the filter returns a
completely different set of postings, not the same list reordered. Useful for a daily digest of
new openings before the applicant count climbs.

#### See how many people applied to a LinkedIn job

Turn on **Fetch the full job page**. Every row then carries `applicants`. A posting with 12
applicants and one with 600 are different opportunities, and LinkedIn only shows that number on
the job page itself.

#### Scrape LinkedIn jobs without an account or cookies

No login, no session cookie, no password. The Actor reads only what LinkedIn serves to
logged-out visitors, through its public jobs endpoint.

#### Export LinkedIn job descriptions in bulk

With the full job page enabled, `description` carries the complete posting text — around 5,700
characters on average — plus `seniority`, `employmentType`, `jobFunction` and `industries`.

#### Track which companies are hiring for a role

Run the same keyword across several locations and group by `company`. Because `isPromoted` is
exposed, you can tell a company that paid for placement from one that simply matches the search.

#### Compare salaries across cities for the same job title

Run one keyword with different `location` values. `salary` is filled whenever LinkedIn discloses
a range on the card; many postings do not publish one, so treat it as a bonus field rather than a
guaranteed one.

#### Monitor a competitor's open roles

Use the company name as the keyword. New postings appear as they are published, with the date
each one went live.

#### Build a recruiting pipeline from LinkedIn job data

Every row carries `jobId` and a direct `url`, so runs can be diffed against each other to detect
new, changed or removed postings between days.

# Actor input Schema

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

Job titles or terms to search for. Each one is searched separately.

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

City, region or country as you would type it on LinkedIn, e.g. "United States", "Berlin, Germany". Leave empty to search worldwide.

## `maxResultsPerKeyword` (type: `integer`):

LinkedIn stops serving results at roughly 400-500 jobs per search, so anything above that returns whatever it has.

## `includeDescription` (type: `boolean`):

Adds the full description, seniority, employment type, job function, industry and the applicant count. Costs one extra request per job, so the run takes longer.

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

Filter by how recently the job was posted.

## `jobType` (type: `string`):

Contract type.

## `experienceLevel` (type: `string`):

How much experience the role asks for, as LinkedIn classifies it.

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

Order of the results: newest postings first, or the ones LinkedIn considers most relevant.

## `proxyCountry` (type: `string`):

Country for the residential proxy. LinkedIn serves different results by country.

## Actor input object example

```json
{
  "keywords": [
    "data engineer",
    "product manager"
  ],
  "location": "United States",
  "maxResultsPerKeyword": 50,
  "includeDescription": true,
  "datePosted": "any",
  "jobType": "",
  "experienceLevel": "",
  "sortBy": "DD",
  "proxyCountry": "US"
}
```

# Actor output Schema

## `results` (type: `string`):

One row per job: title, company, location, posting date, direct URL and, when requested, the full description and applicant count.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

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

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "keywords": [
        "data engineer"
    ],
    "location": "United States",
    "maxResultsPerKeyword": 50,
    "includeDescription": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("renzomacar/linkedin-jobs-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "keywords": ["data engineer"],
    "location": "United States",
    "maxResultsPerKeyword": 50,
    "includeDescription": True,
}

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

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

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

```

## CLI example

```bash
echo '{
  "keywords": [
    "data engineer"
  ],
  "location": "United States",
  "maxResultsPerKeyword": 50,
  "includeDescription": true
}' |
apify call renzomacar/linkedin-jobs-scraper --silent --output-dataset

```

## MCP server setup

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