# LinkedIn Job Search Scraper – Jobs & Filters (`scrapingmonkey/linkedin-job-search-scraper`) Actor

Search public LinkedIn jobs by keyword with location, company, and other filters. Export job titles, employers, posting dates, and links with pagination.

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

## Pricing

from $1.00 / 1,000 results

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?

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

Search public LinkedIn vacancies by role or keyword and collect the results as individual job rows. **LinkedIn Job Search Scraper** returns job titles, employers, locations, posting dates, links, and visible listing labels, with filters and pagination for each query.

Use it to explore hiring demand, build vacancy alerts, or gather job IDs for deeper enrichment. The original search query stays on every result so you can compare the terms that produced it.

| At a glance | Details |
|---|---|
| 📥 Input | One or more job-search phrases |
| 📤 Output | One row per collected job, with its source query |
| 🔎 Filters | Location, company, posting time, workplace, seniority, job type, and more |
| 📄 Pagination | Configurable pages per query; normally 10 job cards per page |
| 🔐 Login required | No LinkedIn credentials or cookies |

### What the LinkedIn job search scraper collects 🔎

Each public job card can provide:

- Job ID, job URL, and title
- Company ID, name, page URL, and logo
- Location, posting date, and relative posting-time text
- Benefits and visible listing labels
- Actively hiring and promoted indicators
- The original query used to find the result

These fields are useful for selecting and comparing vacancies. Full descriptions, qualifications, application links, and salary fields can be collected separately with LinkedIn Job Details Scraper.

### How to scrape LinkedIn job search results 🚀

1. Enter job-search phrases such as `software engineer`.
2. Set `pagesPerQuery` and optional filters such as `location`.
3. Run the Actor and review the returned job rows.
4. Export the dataset or enrich selected job IDs.

```json
{
  "inputList": [
    "software engineer"
  ],
  "pagesPerQuery": 2,
  "location": "Singapore"
}
```

Filters apply to all queries in the run. Repeated job IDs are removed within each query's pagination. A job found by two different queries can appear twice, preserving both search contexts.

### Job search fields and output 📦

| Field group | Included data |
|---|---|
| Search context | Original query and success or failed status |
| Job identity | ID, URL, and title |
| Employer | Company ID, name, URL, and logo when exposed |
| Location and timing | Location, posting date, and relative posting text |
| Listing labels | Benefits, actively hiring indicator, and promoted indicator |

Complete illustrative output item with all 15 fields. The employer, vacancy, dates, and values below are fictional examples, not a live result:

```json
{
  "input": "software engineer",
  "status": "success",
  "id": "9876543210",
  "url": "https://www.linkedin.com/jobs/view/9876543210/",
  "title": "Software Engineer",
  "company.id": "12345678",
  "company.name": "Example Analytics",
  "company.url": "https://www.linkedin.com/company/example-analytics/",
  "company.logo": "https://example.com/company-logo.png",
  "location": "Singapore",
  "posted_at": "2026-09-06",
  "posted_time": "2 days ago",
  "benefits": [
    "Actively Hiring",
    "Medical insurance"
  ],
  "actively_hiring": true,
  "is_promoted": false
}
```

Each result is one row in one table. JSON uses the same literal field names, including `company.name`. Benefits remain an ordered list within the row.

Successful cards may contain empty strings or empty lists for omitted data. Hiring and promoted indicators reflect the labels present on the card; missing labels produce `false`. Failed results retain the search `input`, set `status` to `failed`, and use `null` for the other fields.

### Input requirements and search coverage ⚙️

| Parameter | Type | Default | Rules and meaning |
|---|---|---|---|
| `inputList` | array of strings | Required | At least one nonempty job-search phrase |
| `pagesPerQuery` | integer | `1` | Minimum 1; no fixed upper limit |
| `location` | string | `""` | LinkedIn location text |
| `geoId` | string | `""` | LinkedIn geographic ID |
| `companyId` | string | `""` | Company ID from LinkedIn's `f_C` URL parameter |
| `timePosted` | string | `""` | Posting-time value from LinkedIn's `f_TPR` URL parameter |
| `workplaceType` | string | `""` | Workplace value from LinkedIn's `f_WT` URL parameter |
| `experienceLevel` | string | `""` | Experience-level value from LinkedIn's `f_E` URL parameter |
| `jobType` | string | `""` | Job-type value from LinkedIn's `f_JT` URL parameter |
| `sortBy` | string | `""` | Sort value from LinkedIn's `sortBy` URL parameter |
| `distance` | integer | `0` | Minimum 0; zero leaves the distance filter unset |
| `easyApply` | boolean | `false` | Request LinkedIn's Easy Apply filter |
| `earlyApplicant` | boolean | `false` | Request LinkedIn's early-applicant filter |

Enter search text in `inputList`. The Actor treats it as a keyword phrase; it does not extract filters from a pasted search URL. Coded filter strings are passed through to LinkedIn, so use LinkedIn's values rather than the corresponding display labels. To reuse filters from a LinkedIn job-search URL, copy its parameter values into the corresponding input fields.

Pagination advances in groups of 10 and stops at your page limit, on a page containing fewer than 10 parsed jobs, or when no new job IDs appear. A first page with no jobs produces a failed row. An empty later page ends that query without adding a failure.

Page count is a request budget, not a promised number of jobs or exhaustive coverage of LinkedIn's search results.

### LinkedIn job search use cases 🎯

#### Hiring-demand research

Collect comparable samples for a role and location, then group titles and employers to study the vacancies visible in each run.

#### Vacancy alerts

Schedule repeat searches and compare job IDs with your saved results to identify newly observed openings.

#### Search-query comparisons

Evaluate how different role phrases overlap. Preserve the input query while consolidating repeated job IDs in your analysis.

#### Job-detail collection

Choose relevant search results by employer or location, then send their IDs to Job Details Scraper for description and criteria enrichment.

### Pricing and billable job rows 💰

Check the Actor's **Pricing** tab for the current charging model and rate.

Under dataset-item pricing, every saved job row can count as a result. Saved failed rows can also count, including a search with no jobs on its first page. Request retries do not independently create additional rows.

More queries and pages can increase the result count. Duplicate jobs within a query are removed, but the same job found under different queries can create separate rows.

### LinkedIn job search API and integrations 🔌

Use the Actor ID from the API tab for `$ACTOR_ID` and your Apify token for `$APIFY_TOKEN`:

```bash
curl -X POST "https://api.apify.com/v2/acts/$ACTOR_ID/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"inputList":["software engineer"],"pagesPerQuery":2,"location":"Singapore"}'
```

Trigger runs through REST, Apify client libraries, schedules, or webhooks. Export the default dataset to a spreadsheet or connect it to Make, Zapier, a vacancy database, or a reporting service.

### Reliability and search limits ⚠️

The Actor processes up to five requests concurrently and retries temporary request failures. One invalid query or unavailable page does not prevent other queries from being processed.

A later-page failure leaves any previously saved jobs intact and adds a failed row for that query. Empty later pages or repeated cards end pagination normally.

LinkedIn may change ranking, hide results, or return different public results between runs. Narrow filters can produce no matches. Visible card fields are not a complete job description, and missing company IDs or labels should remain missing in downstream reporting.

### Frequently asked questions ❓

#### Should I paste a LinkedIn search URL?

Enter the search phrase in `inputList` and set filters in their own fields. A pasted URL is treated as search text, not as a search configuration.

#### Can I filter by employer?

Yes. Set `companyId` to a LinkedIn company ID. Use Company Jobs Scraper when starting from a list of company slugs or URLs.

#### Why did the same job appear under two queries?

Duplicates are removed within each query. Separate queries retain their own results and original input, even when they find the same job.

#### Does it return all matches or exactly 10 jobs per page?

Neither is guaranteed. A page normally contains 10 cards, but limited public results, short pages, and duplicate IDs can reduce the number collected.

#### Why is an empty search marked failed?

No jobs on the first page produces a failed row for the query. An empty page after successful results simply stops pagination.

### Support, responsible use, and related actors 🛟

For a reproducible issue, share the run ID, search phrase, filters, and expected behavior through the Actor's support channel.

Use public vacancy data in accordance with applicable privacy, copyright, contractual, and platform requirements.

# Actor input Schema

## `inputList` (type: `array`):

Add one job search keyword or phrase per item. Every query produces success rows or one failed row.

## `pagesPerQuery` (type: `integer`):

Number of result pages to request. Each page normally contains 10 jobs.

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

Optional LinkedIn location text.

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

Optional LinkedIn geographic ID.

## `companyId` (type: `string`):

Optional numeric LinkedIn company ID.

## `timePosted` (type: `string`):

Optional LinkedIn f\_TPR filter value.

## `workplaceType` (type: `string`):

Optional LinkedIn workplace type filter value.

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

Optional LinkedIn experience level filter value.

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

Optional LinkedIn job type filter value.

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

Optional LinkedIn sort value.

## `distance` (type: `integer`):

Optional distance filter.

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

Return Easy Apply jobs only.

## `earlyApplicant` (type: `boolean`):

Apply LinkedIn's early applicant filter.

## Actor input object example

```json
{
  "inputList": [
    "software engineer"
  ],
  "pagesPerQuery": 1,
  "location": "",
  "geoId": "",
  "companyId": "",
  "timePosted": "",
  "workplaceType": "",
  "experienceLevel": "",
  "jobType": "",
  "sortBy": "",
  "distance": 0,
  "easyApply": false,
  "earlyApplicant": false
}
```

# Actor output Schema

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

Actor dataset rows. Use the status column to distinguish successful extraction from failed processing.

# 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 = {
    "inputList": [
        "software engineer"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapingmonkey/linkedin-job-search-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 = { "inputList": ["software engineer"] }

# Run the Actor and wait for it to finish
run = client.actor("scrapingmonkey/linkedin-job-search-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 '{
  "inputList": [
    "software engineer"
  ]
}' |
apify call scrapingmonkey/linkedin-job-search-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapingmonkey/linkedin-job-search-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/ENYPl5xGSe1aA7L5j/builds/t2Q6ZUyxiRAogafrJ/openapi.json
