# LinkedIn Jobs Scraper — Full Descriptions & New Jobs Monitor (`agnes.developer.queen/linkedin-jobs-scraper`) Actor

LinkedIn jobs scraper for public job postings with full descriptions, company, location and posted date. Filter by title and schedule a new-jobs monitor that skips jobs you already have. No login or cookies. $2 per 1,000 complete jobs.

- **URL**: https://apify.com/agnes.developer.queen/linkedin-jobs-scraper.md
- **Developed by:** [Agnes Maina](https://apify.com/agnes.developer.queen) (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 $2.00 / 1,000 complete jobs

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?

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: full job descriptions and a new-jobs monitor

[![Run on Apify](https://apify.com/actor-badge?actor=agnes.developer.queen/linkedin-jobs-scraper)](https://apify.com/agnes.developer.queen/linkedin-jobs-scraper)

This LinkedIn jobs scraper collects public LinkedIn job postings with the full job description (not the truncated search card), plus company, location and posted date, with no login or cookies. You can filter by job title, schedule a new-jobs monitor that only returns postings you have not seen, and you pay $2 per 1,000 complete jobs.

In the September 27, 2026 cloud benchmark, 20 searches across the US, UK, Canada, Germany and Australia delivered 500 jobs from 506 attempted detail pages (98.81%). All 50 independently re-checked job identities matched. These are measured results from that benchmark, not a promise of exhaustive LinkedIn coverage.

### What you get

Every delivered row is one LinkedIn job with these fields:

| Field | What it holds |
| --- | --- |
| `jobId` | LinkedIn job ID |
| `url` | Canonical job URL |
| `title` | Job title from the detail page |
| `companyName` | Hiring company |
| `companyUrl` | Company LinkedIn page |
| `location` | Location as shown on the posting |
| `descriptionText` | Full job description text (at least 100 characters, or the job is not delivered) |
| `postedAtRaw` | Date text as displayed, for example "2 weeks ago" |
| `postedAt` | Source date `YYYY-MM-DD`, or an approximate timestamp from relative text |
| `postedAtPrecision` | `day`, `approximate` or null |
| `postedAtEstimated` | `true` when the date is an estimate |
| `salaryText` | Salary text when the posting publishes it |
| `employmentType` | Full-time, part-time, contract and so on |
| `seniorityLevel` | Seniority level as published |
| `jobFunction` | Job function as published |
| `industries` | Industries as published |
| `jobPosterName` | Name of the job poster when shown |
| `jobPosterUrl` | Job poster profile URL when shown |
| `scrapedAt` | Collection timestamp |

Missing optional values are `null`. The actor does not guess emails, hiring managers, salaries or applicant counts. Each run also writes `RUN_SUMMARY` (per-search counts and stop reasons) and `DIAGNOSTICS` (errors) to the key-value store, outside the job dataset.

### Try it in 30 seconds

1. Open the actor on [Apify](https://apify.com/agnes.developer.queen/linkedin-jobs-scraper) and click Try for free.
2. Keep the prefilled `software engineer` / `United States` search or type your own keywords and location.
3. Set `maxJobs` to 25 and click Start.
4. Download the dataset as JSON, CSV or Excel when the run finishes.

Apify's free plan includes $5 of monthly credit. At $0.002 per job that covers about 2,500 jobs a month before you pay anything.

### Example output

A real record from the September 27, 2026 benchmark. `descriptionText` is shortened here; the delivered row holds the full text.

```json
{
  "jobId": "4419969671",
  "url": "https://www.linkedin.com/jobs/view/4419969671/",
  "title": "Senior Software Engineer \u2013 Go (Golang)",
  "companyName": "General Motors",
  "companyUrl": "https://www.linkedin.com/company/general-motors/",
  "location": "Warren, MI",
  "descriptionText": "(full job description text)",
  "postedAtRaw": "2 weeks ago",
  "postedAt": "2026-09-12",
  "postedAtPrecision": "day",
  "postedAtEstimated": false,
  "salaryText": null,
  "employmentType": "Full-time",
  "seniorityLevel": "Not Applicable",
  "jobFunction": null,
  "industries": null,
  "jobPosterName": null,
  "jobPosterUrl": null,
  "scrapedAt": "2026-09-27T15:31:19.157Z"
}
```

Fields shown as `null` in this example are placeholders; on a real posting they are filled whenever LinkedIn publishes them.

### Pricing

Pay per event, no subscription:

| Event | Price |
| --- | --- |
| `job` (one complete job delivered) | $0.002 |
| `apify-actor-start` (per run, per GB of memory, minimum one) | $0.00005 |

Worked example: 1,000 jobs cost 1,000 x $0.002 = $2.00, plus $0.00005 for the run start at the default 256 MB memory. Proxy costs are included. There is no extra platform-usage charge.

A job is charged only when the delivered row has a confirmed job ID, title, company identity, canonical URL and a description of at least 100 characters. Incomplete records, duplicates and jobs rejected by your title filters are skipped and never charged. Set a maximum cost per run in Apify and the actor stops cleanly at that limit.

### How this compares

Live Apify Store data, checked September 30, 2026. Prices are per job result for each actor's free tier and its lowest paid tier. "Not stated" means the Store listing did not say either way.

| Actor | Price per job | Start fee | Full description? | New-jobs monitor / schedule? | Login required? |
| --- | --- | --- | --- | --- | --- |
| This actor (agnes.developer.queen/linkedin-jobs-scraper) | $0.002 on every tier | $0.00005 | Yes, required for a job to be charged | Built-in `onlyNew` with 90-day history, plus Apify Schedules | No |
| curious\_coder/linkedin-jobs-scraper | $0.002 free tier, $0.001 paid tiers | $0.00005 | Yes (`descriptionText`, `descriptionHtml`) | Schedule a "Last 24 hours" search URL; cross-run history not stated | Not stated |
| cheap\_scraper/linkedin-job-scraper | $0.0007 free tier, down to $0.00035 on Gold+ | $0.005 | Not stated | Apify scheduler; removes duplicate jobs, cross-run history not stated | Not stated |
| bebity/linkedin-jobs-scraper | $0.0015 free tier, down to $0.001 on Gold+ | $0.00005 | Yes | Apify scheduler | No |

If lowest price per row is all you need, the other actors are cheaper on paid tiers. This one is built for recurring feeds: you get only unseen jobs, and you are never charged for a row without a full description.

### Input

| Field | Type | Default | What it does |
| --- | --- | --- | --- |
| `searchUrls` | array of strings | none | Up to 20 HTTPS LinkedIn `/jobs/search` URLs. Use these or the keyword fields, not both. Unsupported URL filters are rejected. |
| `keywords` | string | prefill `software engineer` | Search phrase. Use with `location`. |
| `location` | string | prefill `United States` | Location sent to LinkedIn. LinkedIn controls geographic matching. |
| `companyIds` | array of strings | none | Numeric LinkedIn company IDs used as the source's company filter. |
| `postedWithinDays` | integer | any age | Posting-age filter. Accepts 1, 7 or 30. |
| `titleIncludes` | array of strings | none | Case-insensitive phrases. A delivered title must contain at least one. |
| `titleExcludes` | array of strings | none | Reject titles containing any of these phrases. |
| `maxJobs` | integer | 100 | Total cap across all searches, 1 to 10,000. Incomplete and duplicate jobs do not count. |
| `onlyNew` | boolean | false | Skip job IDs delivered by the same search and filter configuration in the previous 90 days. |
| `proxyConfiguration` | object | Apify datacenter proxy | Direct, datacenter or residential access. Direct requests can be rate-limited. |

Example input:

```json
{
  "keywords": "software engineer",
  "location": "United States",
  "titleIncludes": ["engineer"],
  "titleExcludes": ["intern"],
  "maxJobs": 100
}
```

Results come newest first. Each search scans at most 1,000 source candidates or 100 pages. LinkedIn can return fewer results, repeat pages or limit access, and the run summary says when a search stopped early.

### Use cases

- Recruiting pipelines: pull every new "data engineer" posting in your target cities each morning and push it into your ATS or a spreadsheet.
- Job boards and aggregators: feed a niche board with full descriptions instead of one-line snippets.
- Hiring-intent signals for sales: a company posting five DevOps roles this week is a warm lead for infrastructure tooling. Filter by `companyIds` or title and route rows to your CRM.
- Labour-market research: track posting volume by title, location and seniority over time.
- Salary and skills analysis: parse `salaryText` and `descriptionText` for pay ranges, required tools and years of experience.

### Integrations

Run it from the Apify API and get the jobs back in one call:

```bash
curl -X POST "https://api.apify.com/v2/acts/agnes.developer.queen~linkedin-jobs-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keywords":"data analyst","location":"London","maxJobs":50}'
```

Python:

```python
from apify_client import ApifyClient
import os

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("agnes.developer.queen/linkedin-jobs-scraper").call(
    run_input={"keywords": "data analyst", "location": "London", "maxJobs": 50}
)
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(job["title"], job["companyName"], job["url"])
```

Node.js:

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('agnes.developer.queen/linkedin-jobs-scraper').call({
    keywords: 'data analyst',
    location: 'London',
    maxJobs: 50,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, 'jobs');
```

No-code tools: use the Apify app in Make, n8n or Zapier, pick "Run actor", enter `agnes.developer.queen/linkedin-jobs-scraper` and map the dataset items to Google Sheets, Airtable, Slack or your CRM.

AI agents: the Apify MCP server exposes this actor as a tool. Point your MCP client at `https://mcp.apify.com?actors=agnes.developer.queen/linkedin-jobs-scraper`.

#### Schedule the new-jobs monitor

1. In Apify Console, open the actor and save this input as a task:

```json
{
  "keywords": "software engineer",
  "location": "United States",
  "postedWithinDays": 1,
  "titleIncludes": ["engineer"],
  "titleExcludes": ["intern"],
  "maxJobs": 100,
  "onlyNew": true
}
```

2. Go to Schedules, create a schedule (for example daily at 07:00), and add the task.
3. Optionally add a webhook or a Make/n8n/Zapier trigger on "run succeeded" to send the new rows wherever you need them.

The first run delivers matching jobs up to the cap. Later runs skip job IDs already delivered under the same search and title filters within 90 days. Capped or failed jobs are not marked as seen, so they can arrive on the next run. History lives in your named key-value store `linkedin-jobs-monitor-v1`; deleting it resets the baseline. Run one schedule per configuration and do not let runs overlap.

### FAQ

#### Can you scrape LinkedIn jobs without login?

Yes. The actor reads public LinkedIn job pages. It needs no LinkedIn account, cookies or session, and no third-party data API.

#### Does it include the full job description?

Yes. `descriptionText` holds the full description from the job detail page. A job without a description of at least 100 characters is not delivered and not charged.

#### How do I get only new jobs?

Set `onlyNew` to `true`, save the input as a task and schedule it. Each run skips job IDs delivered under the same configuration in the last 90 days. Adding `postedWithinDays: 1` keeps each daily run focused on fresh postings.

#### How often can I run it?

As often as you like, as long as runs for the same monitor configuration do not overlap. Daily or a few times a day works well for most searches. Different searches can run in parallel because they keep separate histories.

#### Does it work outside the US?

Yes. The benchmark covered the United States, United Kingdom, Canada, Germany and Australia. Put any location LinkedIn accepts in `location`. LinkedIn decides geographic matching.

#### Why was a job not charged?

Only complete jobs are charged. Rows with missing identity data, descriptions under 100 characters, duplicates, and titles rejected by `titleIncludes` or `titleExcludes` are skipped for free. In the benchmark, 6 of 506 detail pages came back incomplete and were not charged.

#### How many jobs can I get per run?

Up to 10,000 with `maxJobs`. Each individual search scans at most 1,000 source candidates or 100 pages, so split broad searches into several narrower ones or several `searchUrls` for more volume.

#### Can I use my own LinkedIn search URL?

Yes. Build a search on LinkedIn, copy the `/jobs/search` URL into `searchUrls` (up to 20). Unsupported filters in the URL are rejected with a clear error before scraping starts.

#### Is it legal to scrape LinkedIn job postings?

The actor only collects job postings that LinkedIn shows publicly without a login, and it does not collect emails or private profile data. You are responsible for how you use the data, including compliance with LinkedIn's terms, GDPR, CCPA and any other laws that apply to you. If in doubt, ask a lawyer.

### Support

Open an issue on the actor's Issues tab. Include the run ID, your input and what you expected to see. Bug reports with a run ID usually get fixed fastest.

Docs and examples: https://github.com/agnesthedeveloper/linkedin-jobs-scraper

# Actor input Schema

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

Up to 20 HTTPS LinkedIn /jobs/search URLs. Use these OR the keyword/location fields. Unsupported filters are rejected. Results are sorted newest first.

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

Search phrase. Use with location, without search URLs.

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

Location sent to LinkedIn. LinkedIn controls geographic matching; this is not a verified geographic boundary.

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

Optional numeric LinkedIn company IDs sent as the source's company filter. Use with keywords/location.

## `postedWithinDays` (type: `integer`):

Source posting-age filter. Omit for any age. Use with keywords/location.

## `titleIncludes` (type: `array`):

Case-insensitive literal phrases. A delivered title must contain at least one, if supplied.

## `titleExcludes` (type: `array`):

Reject titles containing any of these case-insensitive literal phrases.

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

Global delivery cap across all searches; incomplete and duplicate jobs do not count.

## `onlyNew` (type: `boolean`):

Suppress IDs delivered by this same search/filter configuration in the previous 90 days. Requires access to named storage. Use one non-overlapping schedule per configuration.

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

Apify datacenter proxy by default. Direct requests and residential proxy are optional; direct requests can be rate-limited. Proxy costs are included in event pricing.

## Actor input object example

```json
{
  "keywords": "software engineer",
  "location": "United States",
  "maxJobs": 100,
  "onlyNew": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

No description

## `summary` (type: `string`):

No description

## `diagnostics` (type: `string`):

No description

# 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"
};

// Run the Actor and wait for it to finish
const run = await client.actor("agnes.developer.queen/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",
}

# Run the Actor and wait for it to finish
run = client.actor("agnes.developer.queen/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": "software engineer",
  "location": "United States"
}' |
apify call agnes.developer.queen/linkedin-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,agnes.developer.queen/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/TXzPBmagxRli2yfhX/builds/ILmpsXdEhybBDAWYp/openapi.json
