LinkedIn Jobs Scraper avatar

LinkedIn Jobs Scraper

Pricing

from $0.60 / 1,000 job delivereds

Go to Apify Store
LinkedIn Jobs Scraper

LinkedIn Jobs Scraper

Export LinkedIn jobs with company enrichment, CV keyword matching, applicant counts and annualised salary. No login, no cookie, no proxy needed.

Pricing

from $0.60 / 1,000 job delivereds

Rating

0.0

(0)

Developer

丂卩ㄖㄖҜㄚ

丂卩ㄖㄖҜㄚ

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

3 days ago

Last modified

Share

Export LinkedIn job listings with company enrichment, CV keyword matching, applicant counts and salary parsing. No login, no session cookie, no proxy required.

🔍 What does LinkedIn Jobs Scraper do?

It runs a job search on LinkedIn and returns every listing it finds as a clean row of data, ready for a spreadsheet, a database or your own product.

No login, no session cookie, no proxy required. This reads LinkedIn's public guest endpoints, the ones that serve logged out job browsing, so there is no session to expire and no account of yours at risk.

On top of the listing itself you can add company details, filter on applicant counts and score every job against your own CV keywords.

📊 What data can I extract from LinkedIn?

One row per job:

FieldNotes
jobId, title, company, companyUrl, locationThe listing and who posted it
postedAt, urlPosting date and a direct link
applicants, applicantsBasis, isEarlyApplicantHow many have applied, and how firm that number is
applyType, seniority, employmentTypeHow to apply and at what level
jobFunction, industriesLinkedIn's own categorisation
description, descriptionLengthFull job description
salaryFrom, salaryTo, salaryCurrency, salaryPeriod, salaryRaw, hasSalaryPay as published
listingSalarySalary exactly as written on the listing, before any parsing
salaryAnnualisedFrom, salaryAnnualisedToAnnual equivalent so hourly, daily and annual roles compare in one column
payBand, paySchemeNHS style pay bands, kept as facts rather than converted
keywordMatchScore, matchedKeywords, unmatchedKeywordsCV match, when resumeKeywords is set
companyName, companyEmployees, companyFollowers, companyIndustry, companyWebsite, companyDescription, companyCity, companyCountryAdded by enrichCompanyData
searchKeywords, searchLocation, retrievedAtThe search that produced the row, and when

💡 Why scrape LinkedIn jobs?

Jobseekers. Filter to early applicant roles matching your CV, at direct employers rather than agencies.

Recruiters. Track competitor hiring by company, size and industry.

Salary benchmarking. Annualised pay by title and region, with NHS bands handled properly.

Market research. Hiring volume by sector, company size and location over time.

🚀 How do I use LinkedIn Jobs Scraper?

  1. Click Try for free.
  2. Enter what you are searching for and where, for example software engineer in London.
  3. Add any filters you want, such as date posted, workplace type or company size.
  4. Cap the run with maxResults, then click Start.
  5. Download the results as JSON, CSV or Excel, or pull them from the API.

⬇️ Input

{
"keywords": "software engineer",
"location": "London",
"maxResults": 100
}

Set at least one of keywords or location.

FieldTypeDefaultWhat it does
keywordsstringJob title or keywords
locationstringCity, region or country
datePostedstringanyAny time, past 24 hours, past week or past month
workplaceTypestringanyOn site, remote or hybrid
jobTypestringanyFull time, part time, contract, temporary, internship or volunteer
experienceLevelstringanyInternship through executive
sortBystringrelevanceRelevance or most recent
requireSalaryInfobooleanfalseOnly jobs that state pay or an NHS band
maxResultsinteger100Hard cap on billable results

Many more filters are available including company size, industry, applicant count and CV keyword matching, see the input schema in the Console.

Company filters

FieldDescription
companyIncludeOnly these employers. Suffixes like Ltd and PLC are ignored when matching
companyExcludeDrop these employers
excludeRecruitingAgenciesDrop agency listings and keep direct employers
enrichCompanyDataAdd employee count, followers, industry, website and location
companySizeMin / companySizeMaxFilter by employee count
companyFollowersMin / companyFollowersMaxFilter by LinkedIn following

Company pages are fetched once per company and cached within a run, so a hundred jobs at one employer costs one extra request, not a hundred.

Job filters

FieldDescription
excludeJobTitlesDrop titles containing any of these words
underTenApplicantsLinkedIn's own low competition filter, applied server side
maxApplicantsOnly jobs with an exact count at or below this
easyApplyOnlyApply without leaving LinkedIn, applied server side
requireSalaryInfoOnly jobs that state pay or an NHS band

CV matching

Paste your skills into resumeKeywords and every job is scored on how many appear in the title and description. You get keywordMatchScore as a percentage plus matchedKeywords and unmatchedKeywords, so you can see exactly why a job scored what it did. Set minKeywordScore to drop anything below a threshold.

⬆️ Output

Table view

Results arrive as a table you can sort and filter in the Console. There is a second view, Salary detail, that lines up the parsed and annualised pay figures for comparing listings against each other.

Every run also writes a RUN_SUMMARY to the key value store showing how many jobs were scanned, how many were kept, and exactly which filter rejected the rest. If a run returns nothing you can see why rather than guessing.

JSON

A typical row:

{
"title": "Product Manager",
"company": "Example Group",
"location": "Reading, England, United Kingdom",
"postedAt": "2026-08-07T00:00:00.000Z",
"url": "https://uk.linkedin.com/jobs/view/product-manager-4448545238",
"jobId": "4448545238",
"companyUrl": "https://uk.linkedin.com/company/examplegroup"
}

Download it from the run as JSON, CSV or Excel, or read it straight from the API.

Two things this does that others do not

Salary you can actually compare. Pay is published as hourly, daily or annual with no consistency. Every job gets salaryPeriod plus salaryAnnualisedFrom and salaryAnnualisedTo, so one column sorts correctly across the whole result set.

UK healthcare pay bands. NHS roles state pay as "Band 5" rather than a number, so generic scrapers report no salary at all. On a sample of 20 UK nursing jobs, numeric salary extraction found pay on 1. Adding band detection took that to 9. You get payBand and payScheme as facts. The actor deliberately does not convert bands into salary figures, because published Agenda for Change tables disagree with each other materially and inventing a number would be worse than leaving it out.

Honest limits

1,000 results per search. LinkedIn's own ceiling, not a bug. Verified: offset 975 returns jobs, offset 1000 returns an error. Narrow the search with filters or date ranges to get past it.

Applicant counts are bucketed below 25. LinkedIn will filter to low competition jobs server side via underTenApplicants, but its guest view still reports "be among the first 25 applicants" rather than the exact figure. So you can select those jobs, you just cannot see the true number. The applicantsBasis field marks every count as exact, upper-bound or minimum so you always know which you are looking at.

Salary is often absent. LinkedIn does not require employers to publish pay, and most do not. requireSalaryInfo filters to those that do.

Empty results are not a failure

If nothing passes your filters the run finishes successfully with an empty dataset, and RUN_SUMMARY holds the rejection breakdown, so you can see which filter ate everything.

An earlier version threw an error here. That recorded a failed run against the actor's success rate for what was a filter choice, and it discarded the diagnostic you needed.

When a filter cannot be evaluated

If a detail page fails to load, the job's function, industry and applicant count are unknown. By default those jobs are kept, so a network problem is never mistaken for a filter decision. detailFailures in RUN_SUMMARY tells you how often it happened.

Set strictFilters to reject them instead.

Speed

concurrency controls how many detail and company pages are read at once, default 6, maximum 10. It does not change how many requests are made, only how long you wait. Ten detail pages take under a second at the default, against roughly eight seconds one at a time.

Salary

Salary is read from the listing's own pay field, or from a labelled pay section on the detail page. It is never extracted from the body of a job description: a project budget or a revenue figure in the copy would otherwise become the salary and then feed salaryMin filtering. Where there is no pay information, hasSalary is false rather than a number being guessed.

💰 How much does it cost?

Three events, because the cost of a run is not proportional to how many jobs survive your filters.

EventCharged when
job-detail-fetchedA job detail page is read
company-enrichedA company page is read, once per company
job-resultA job passes every filter and is delivered

Deep filtering asks this actor to read a thousand jobs to find fourteen. Under a single per-result charge, the better your filters the worse the economics, which is the wrong incentive on the one feature that sets this apart. Splitting the events means you pay for the reading you asked for and the results you got, and both are visible before you run.

Cheap filters, company name, title exclusions and agency exclusion, are applied before any detail request, so they never cost you a fetch.

What a run actually costs

The listing shows the result price. That is the cheap end, and deep filtering costs more, so here are both plainly:

RunCost
100 jobs, no detail filtersabout $0.10
1,000 scanned, 14 delivered after salary, applicant and keyword filtersabout $2.01

The second run reads a thousand detail pages to find fourteen jobs. That reading is the work you asked for and it is charged at job-detail-fetched, not hidden in the result price. Filters that need no detail page, company name, title exclusions and agency exclusion, are applied first and cost nothing.

If you want the cheap end, leave the detail filters off.

🔌 Integrations

Send results straight to Google Sheets, Slack, Airtable, Zapier, Make or your own webhook using Apify integrations. You can also trigger a run whenever something happens in another tool.

🔗 Using LinkedIn Jobs Scraper with the Apify API

curl -X POST "https://api.apify.com/v2/acts/spookyweb~linkedin-jobs/run-sync-get-dataset-items?token=YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"keywords": "software engineer", "location": "London", "maxResults": 100}'

Or with the Apify client:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('spookyweb/linkedin-jobs').call({
keywords: 'software engineer',
location: 'London',
maxResults: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();

Full detail is in the Apify API reference, and every run is also callable from the Python and JavaScript clients.

❓ FAQ

No, and that is the point. This reads the public guest job pages, so there is no login step, no cookie to paste and no session of yours to expire or get flagged. You also do not need a proxy.

Up to 1,000, which is LinkedIn's own ceiling rather than a limit set here. Past that the guest endpoint returns a 400, which the run treats as a normal end and finishes successfully with what it has. To cover more of a market, split the search by location, by job title or by date range.

What is CV keyword matching?

Put your skills into resumeKeywords and every job is scored on how many of them appear in its title and description. The row carries keywordMatchScore as a percentage plus matchedKeywords and unmatchedKeywords, so the score is auditable. minKeywordScore drops anything below a threshold.

Does it get applicant counts?

Yes, in applicants, with applicantsBasis saying whether the figure is exact, an upper-bound or a minimum. Below 25 applicants LinkedIn's guest view reports a bucket rather than a number, so you can still select those jobs with underTenApplicants, you just cannot see the true count.

What does enrichCompanyData add?

Employee count, followers, industry, website, description and location for the hiring company. Company pages are read once per company and cached within the run, so a hundred jobs at one employer costs one extra request rather than a hundred.

Why is a job kept when a lookup fails?

Because a network problem is not a filter decision. If a detail page will not load, the job's function, industry and applicant count are unknown, and dropping it would quietly turn an outage into a filtered result. Those jobs are kept by default and counted in detailFailures. Set strictFilters to reject them instead.

This reads LinkedIn's public guest job pages, the ones anyone can see without an account. It never logs in, it never uses a cookie, and it never touches anything behind authentication. Job adverts are business information published by employers to be seen.

Scraping public data is lawful in the UK, the EU and the US, and the hiQ v LinkedIn line of cases went to public data specifically. If you go on to combine this data with personal data, that is on you to handle under GDPR. Apify's ethical scraping guide covers the wider picture.

👍 Your feedback

Found a bug, or want a field that is not here yet? Open an issue on the Actor's Issues tab. Requests that make the data more useful get built, and problems get fixed quickly.

🔎 You might also like

ActorWhat it does
Indeed Jobs ScraperIndeed listings with parsed salary figures, past the 15 job sign in wall
Reed Jobs ScraperUK jobs from Reed with applicant counts and annualised salaries
Totaljobs ScraperUK jobs from Totaljobs with duplicate agency repostings removed