# LinkedIn Jobs Scraper: Every Job, Counted (`luzivanlt/linkedin-jobs-scraper`) Actor

Every job a LinkedIn search lists, checked against LinkedIn's own count, with its full description. $1 per 1,000 jobs. Applicants, poster and company id.

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

## Pricing

from $0.70 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

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

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## LinkedIn Jobs Scraper: Every Job, Counted

Get every job a LinkedIn job search lists, checked against the count LinkedIn
shows for that search, and each job with its full description, the number of
applicants, seniority, employment type, industry, the job poster when shown,
the company's id and how you apply. One row per job. No LinkedIn account,
login or cookies needed.

Tested daily. Latest check: 56 of 56 Golang jobs in Da Nang, Vietnam, matching
LinkedIn's own count; 800 jobs from two United States searches, each read to
its 400-job limit.

### What does this Actor do?

You give it keywords and a location, or links to LinkedIn job searches. For
each search it reads LinkedIn's results and then each job's own page, and
saves one row per job, until it reaches your limit or the search has no more
jobs. When the search shows an exact count (up to 1,000 jobs), the run's
final status tells you whether every job was read: "That is every job the
site lists for Golang in Da Nang, Vietnam (56 of 56)."

Each job comes with:

- its title, link, location and the date it was posted
- the company's name, page, logo and LinkedIn id
- the full description, as text and as HTML
- the applicant count ("43 applicants", or "Be among the first 25
  applicants" for new jobs)
- seniority level, employment type and industries
- whether you apply on LinkedIn (Easy Apply) or on the company's site, and
  whether the job still takes applications
- the salary, when the job shows one
- the job poster's name, headline, profile and photo, when the job shows them
- the highlights LinkedIn puts on the job ("Actively Hiring", "Be an early
  applicant")
- the search it was found in, and how many jobs LinkedIn counted for it

### Why use it?

- **Know you have the whole search.** When LinkedIn shows an exact count
  (up to 1,000 jobs), the run checks the search against it, so a short read
  is never silent.
- **Find hiring companies and recruiters.** Every row carries the company id,
  and the poster's profile when the job shows one.
- **Track a job market.** Run it daily on your keywords with "Past 24 hours"
  and collect each day's new jobs.
- **Use the data anywhere.** Download as JSON, CSV or Excel, or send it to
  your own tools through Apify's API and integrations.

### How to use it

1. Type one or more **Keywords** and a **Location**, or paste **Search
   links**, or both.
2. Optionally, choose **Date posted**, **Easy Apply only** and **Max jobs per
   search**.
3. Click **Start**. When the run ends, open the **Output** tab to view the
   jobs or download them.

#### Links that work

A LinkedIn job search, such as
`https://www.linkedin.com/jobs/search?keywords=data%20engineer&location=Germany`.
Its keywords, location, date posted and Easy Apply filters are used. Other
filters in the link (remote, job type, experience level, company) are not
applied. Other LinkedIn pages (a single job, a company, a profile) and other
sites stop the run before anything is read, naming the link.

### Input

| Field                                        | What it does                                                                | Default |
| -------------------------------------------- | --------------------------------------------------------------------------- | ------- |
| **Keywords** (`keywords`)                    | Job titles or skills to search for, one search each                         | —       |
| **Location** (`location`)                    | A country, region or city, used for every keyword. Empty = LinkedIn chooses | —       |
| **Date posted** (`datePosted`)               | `any`, `24h`, `week` or `month`, for the keywords                           | `any`   |
| **Easy Apply only** (`easyApplyOnly`)        | Only jobs you apply to on LinkedIn, for the keywords                        | `false` |
| **Search links** (`startUrls`)               | LinkedIn job search links                                                   | —       |
| **Max jobs per search** (`maxJobsPerSearch`) | Stop each keyword or link after this many jobs, 1 to 1,000                  | `100`   |

Give at least one keyword or one link. Example input:

```json
{
  "keywords": ["data engineer", "analytics engineer"],
  "location": "Germany",
  "datePosted": "week",
  "maxJobsPerSearch": 200
}
```

### Output

One row per job. An example, with made-up values:

```json
{
  "job_id": "4400000001",
  "url": "https://www.linkedin.com/jobs/view/4400000001",
  "job_url": "https://de.linkedin.com/jobs/view/data-engineer-at-example-gmbh-4400000001",
  "title": "Data Engineer",
  "company_name": "Example GmbH",
  "company_url": "https://de.linkedin.com/company/example-gmbh",
  "company_id": "12345678",
  "company_logo_url": "https://media.licdn.com/dms/image/…",
  "location": "Berlin, Germany",
  "posted_date": "2026-10-14",
  "posted_ago": "2 days ago",
  "is_new": false,
  "benefits": ["Actively Hiring"],
  "salary": "€60,000.00/yr - €75,000.00/yr",
  "applicants_count": 43,
  "applicants_text": "43 applicants",
  "apply_type": "offsite",
  "accepting_applications": true,
  "seniority_level": "Mid-Senior level",
  "employment_type": "Full-time",
  "industries": "Software Development",
  "description_text": "About the role\nYou will build our data platform…",
  "description_html": "<strong>About the role</strong><br>You will build our data platform…",
  "job_poster_name": "Alex Example",
  "job_poster_title": "Technical Recruiter at Example GmbH",
  "job_poster_url": "https://de.linkedin.com/in/alex-example",
  "job_poster_photo_url": "https://media.licdn.com/dms/image/…",
  "search_keyword": "data engineer",
  "search_location": "Germany",
  "search_url": "https://www.linkedin.com/jobs/search?keywords=data%20engineer&location=Germany",
  "search_total": 1840,
  "search_total_exact": true
}
```

| Field                    | What it holds                                                                                                 |
| ------------------------ | ------------------------------------------------------------------------------------------------------------- |
| `job_id`                 | LinkedIn's id for the job                                                                                     |
| `url`                    | The job's page                                                                                                |
| `job_url`                | The same page under the link LinkedIn shows, with the job's title in it                                       |
| `title`                  | The job's title                                                                                               |
| `company_name`           | The company hiring                                                                                            |
| `company_url`            | The company's LinkedIn page                                                                                   |
| `company_id`             | LinkedIn's numeric id for the company                                                                         |
| `company_logo_url`       | The company's logo                                                                                            |
| `location`               | Where the job is                                                                                              |
| `posted_date`            | The day the job was posted, `YYYY-MM-DD`                                                                      |
| `posted_ago`             | How long ago, as LinkedIn says it ("3 days ago")                                                              |
| `is_new`                 | `true` when LinkedIn marks the job as new                                                                     |
| `benefits`               | The highlights LinkedIn shows on the job ("Actively Hiring", "Be an early applicant"). Often empty            |
| `salary`                 | The pay range, as shown. Empty on most jobs: only some jobs show one                                          |
| `applicants_count`       | The number of applicants. "Over 200 applicants" gives 200. Empty for "Be among the first 25 applicants"       |
| `applicants_text`        | The applicant line exactly as LinkedIn shows it                                                               |
| `apply_type`             | `easy_apply` (apply on LinkedIn) or `offsite` (apply on the company's site). Empty when the job shows neither |
| `accepting_applications` | `false` when LinkedIn says the job no longer takes applications                                               |
| `seniority_level`        | Internship, Entry level, Associate, Mid-Senior level, Director, Executive or Not Applicable                   |
| `employment_type`        | Full-time, Part-time, Contract, Temporary, Internship, Volunteer or Other                                     |
| `industries`             | The industries LinkedIn gives the job                                                                         |
| `description_text`       | The full description, as plain text                                                                           |
| `description_html`       | The full description, with its formatting                                                                     |
| `job_poster_name`        | The person who posted the job. Empty when the job does not show one                                           |
| `job_poster_title`       | That person's headline                                                                                        |
| `job_poster_url`         | That person's profile                                                                                         |
| `job_poster_photo_url`   | That person's photo                                                                                           |
| `search_keyword`         | The keyword the job was found under                                                                           |
| `search_location`        | The location searched                                                                                         |
| `search_url`             | The search on LinkedIn                                                                                        |
| `search_total`           | How many jobs LinkedIn counted for the search ("5,000+" gives 5000)                                           |
| `search_total_exact`     | `false` when LinkedIn showed the count as "5,000+", a floor rather than a count                               |

Not included: the apply link (LinkedIn shows it only to signed-in members),
the company's size, website and description, and the job function (LinkedIn
did not show it on any job we tested).

### How much does it cost?

You pay for what you get:

- **$1 per 1,000 jobs** ($0.001 a job). Each job is charged once per run, even when two of your searches list it.
- No start fee, and nothing per search.

So a search that lists 400 jobs costs $0.40. If you set a maximum cost for a run, the Actor stops when it reaches it and keeps every job saved so far.

### How long does a run take?

About 56 jobs in under a minute; 800 jobs from two searches in about 10
minutes.

### Good to know

- **At most 1,000 jobs per search.** That is what LinkedIn lists for one
  search. For more, split it: several cities instead of one country, or
  "Past 24 hours" every day.
- **LinkedIn decides what matches.** For a rare keyword LinkedIn also lists
  jobs that only loosely match it, and the count includes them; the Actor
  returns what LinkedIn lists.
- **Counts move.** LinkedIn's count for a big search changes from minute to
  minute, and its list can change order while it is read, so a big search
  can read a few jobs short of the count. The final status says so, by
  search.
- **One job, one row.** A job found again in the same run, under the same or
  another search, is not saved again. It counts for the search that found it
  first, so a later search that shares jobs with it can read short of its own
  count.
- **A search that cannot be read** is reported in the run's status; the other
  searches carry on. The run fails only when none could be read. A job taken
  down while the run reads it makes its search count as not read; the jobs
  already saved from that search stay saved.
- **Running it daily?** Remove repeats by `job_id`.

### FAQ

**Do I need a LinkedIn account or cookies?**
No. The Actor reads what anyone can see on LinkedIn without signing in.

**Can I filter by remote, job type or experience level?**
Not yet. Date posted and Easy Apply are the filters it applies.

**Can I run it from my own code, on a schedule, or with other apps?**
Yes. Like any Apify Actor, you can start it through the Apify API, run it on a
schedule, and connect it to other apps with Apify's integrations and webhooks.

### Is it legal to scrape LinkedIn?

This Actor reads only public job postings. A posting can name the person who
posted it; treat that as personal data. Check LinkedIn's terms and the laws
that apply to you (such as the GDPR) before you use the data. If you are
unsure, ask a lawyer.

### Support

Found a problem or missing a feature? Open an issue on the **Issues** tab and
include the run's link.

***

This Actor is not affiliated with, endorsed by or sponsored by LinkedIn or
Microsoft.

# Actor input Schema

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

Job titles or skills to search for, one search per keyword (for example "data engineer").

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

Where the jobs are, as you would type it on LinkedIn: a country, a region or a city (for example "Germany" or "Austin, Texas, United States"). Used for every keyword. Leave it empty and LinkedIn picks the area itself.

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

Only jobs posted in this time, for the keywords above.

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

Only jobs you can apply to on LinkedIn itself, for the keywords above.

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

Links to LinkedIn job searches (linkedin.com/jobs/search?keywords=…\&location=…). The link's keywords, location, date posted and Easy Apply filters are used; other filters in the link are not.

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

Stop each keyword or link after this many jobs. LinkedIn lists at most 1,000 jobs for one search.

## Actor input object example

```json
{
  "keywords": [
    "Golang"
  ],
  "location": "Da Nang, Vietnam",
  "datePosted": "any",
  "easyApplyOnly": false,
  "maxJobsPerSearch": 100
}
```

# Actor output Schema

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

Every job the run saved, one row per job.

# 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": [
        "Golang"
    ],
    "location": "Da Nang, Vietnam",
    "maxJobsPerSearch": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("luzivanlt/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": ["Golang"],
    "location": "Da Nang, Vietnam",
    "maxJobsPerSearch": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("luzivanlt/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": [
    "Golang"
  ],
  "location": "Da Nang, Vietnam",
  "maxJobsPerSearch": 100
}' |
apify call luzivanlt/linkedin-jobs-scraper --silent --output-dataset

```

## MCP server setup

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