LinkedIn Jobs Scraper — Filters That Actually Work avatar

LinkedIn Jobs Scraper — Filters That Actually Work

Pricing

Pay per event

Go to Apify Store
LinkedIn Jobs Scraper — Filters That Actually Work

LinkedIn Jobs Scraper — Filters That Actually Work

Scrape public LinkedIn jobs where every filter is actually applied. LinkedIn silently ignores its own experience and workplace filters ("worktype is not working"). This Actor applies experience from LinkedIn's real values and offers an honest keyword filter instead of a fake workplace one. No login.

Pricing

Pay per event

Rating

0.0

(0)

Developer

Northbell

Northbell

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

5 days ago

Last modified

Share

Scrape public LinkedIn jobs where the filters you set are actually applied. Not sent to LinkedIn and quietly ignored — applied, and proven, on every run. No login, no cookies.

The complaint this Actor is built to fix

Read the reviews and issue trackers of the popular LinkedIn job scrapers and one word comes up more than any other: filters. You set an experience level, or a workplace type, and the results come back exactly as if you had set nothing at all.

That is not the scraper's fault. It is LinkedIn's. LinkedIn's public job search accepts an experience filter (f_E) and a workplace filter (f_WT) and then ignores them. We measured it: ask for remote jobs and ask for on-site jobs, and 88% of the results are the same postings. The filter changed nothing.

Most scrapers pass those parameters straight through, so they inherit LinkedIn's lie. This one does not pass a filter it knows to be dead. Instead:

  • Experience level is applied for real — by reading LinkedIn's own seniority value off each job page and keeping only the jobs that match.
  • Workplace is replaced by an honest word filter — because LinkedIn's public data genuinely cannot tell remote from on-site, this Actor does not pretend to. It offers a "mentions this word" filter instead, which is a fact you can check, not a guess.
  • Every run proves what it filtered — a summary row shows what was requested, how many jobs matched, how many were dropped and exactly why.

What each filter does — and what it honestly can't

FilterHow it's appliedThe honest catch
keywords / locationSent to LinkedIn. Works.
datePostedSent to LinkedIn. Works — and every run re-checks the returned postings' real dates against it.
titleIncludes / titleExcludesKeep (or drop) jobs by words in the job title only — "manager", "senior", "backend". The title is on the listing, so these are applied without opening the job page — free.Title only, so a word buried in the description won't match. Use the description filters below for that.
seniorityLinkedIn ignores this at search. So we open each job and match on LinkedIn's own value.Only ~4 in 10 public jobs carry a seniority value; the rest say "Not Applicable" and are dropped. A narrow filter returns fewer jobs — the summary tells you how many were dropped for having no value, so the count is never a mystery.
employmentTypeSame — matched on LinkedIn's own value.Almost every public posting is Full-time, so this rarely narrows much.
mustMentionKeeps jobs whose title or description contains one of your words — remote, telework, visa, no agencies.It matches the word, not the fact. A post saying "this role is not remote" contains "remote" and will match. Use it to narrow, then read.
mustNotMentionThe inverse — drops jobs whose title or description contains one of your words (agency, recruiter, unpaid, clearance).Same honesty: it matches the word. The summary reports how many were dropped this way.
excludeCompaniesDrops jobs from the companies you name (matched on company name or LinkedIn slug). Applied on the listing, so it costs no extra request.

There is deliberately no "remote / hybrid / on-site" filter in this Actor. LinkedIn's public pages do not carry that classification reliably — we could only guess it right about 30% of the time — and a filter that is wrong a third of the time is worse than no filter. The word filter is the honest version of the same wish.

Why not a giant jobs database instead?

Some LinkedIn job scrapers are really a pre-built database — millions of jobs indexed once, then queried by API. It sounds better, and the filters are rich, but it comes with the problems buyers of those tools actually report:

  • Stale by construction. A database is only as fresh as its last crawl. This Actor reads LinkedIn live, at the moment you run it, so a job that went up an hour ago is there and a job that closed is gone.
  • No filter that quietly only half-works. Database tools carry footnotes like "description search not supported with the 6-month range" or "seniority only for English-speaking countries". Here, a filter either works everywhere or is not offered.
  • No paid add-ons. Every filter is included. You are charged for pages examined, not for unlocking a filter.
  • No silent cap. maxJobs is your examine ceiling and the summary tells you exactly what was checked — you are never quietly cut off at 1,000 rows wondering where the rest went.

This Actor is not trying to be a database. It is trying to be the honest live scrape whose numbers you can trust on the day you pull them.

What you get

Every run writes two kinds of row, tagged by type.

job — one row per job that passed every filter:

fieldmeaning
title, company, companySlug, locationthe posting
postedOnthe exact date from LinkedIn's own datetime attribute, not "3 days ago"
seniority, employmentType, jobFunction, industriesLinkedIn's own values, off the job page
salaryMentionedwhether the description states pay — never invented when it doesn't
descriptionthe full text
jobUrllink to the posting

summary — one row per run, the proof:

checked: 200 matched: 74 dropped: 126
dropReasons: { seniority-no-value: 118, seniority-wrong-value: 8 }
seniorityFillRatePct: 39
datePostedRequested: r604800 datePostedOutOfRange: 0 datePostedHonored: true
notes:
Examined 200, 74 matched, 126 dropped
Of the 126 dropped on experience level, 118 had no experience level stated by LinkedIn ("Not Applicable"). That is normal rather than a fault: fewer than 4 in 10 public postings fill this field in. Raise maxJobs if you need more results

That row is why the count is what it is. It turns "I asked for senior roles and only got 74" from a suspicion into an explained fact.

Sizing and cost

maxJobs is how many jobs to examine, not how many come back. You are charged per job page opened, so this is your cost ceiling. With a filter on, fewer jobs match — so raise maxJobs to get more matches, not to spend more per match.

requests ≈ ceil(maxJobs / 10) + maxJobs

Pay per event, charged only for real work:

eventwhen
Actor startonce per run
Job listedone job seen in the listing
Job examinedone job page opened to check it against your filters

Filtering costs more because it opens more pages — that is the actual work, and you pay for exactly that. A run that examines 200 jobs to return 74 charges for the 200 pages it read. Competitors that show a seniority dropdown and charge only for returned rows are, in almost every case, not applying that filter at all. Check their output against this one.

A failed fetch is never charged. If a job page errors, you get an error row and no charge for it.

No login. Not as a policy — as a property of the code.

This Actor never signs in, never asks for a session cookie, and never sends one. It reads LinkedIn's public guest endpoints, the pages an anonymous visitor sees.

That is enforced, not promised:

  • The request headers are a frozen object with no Cookie and no Authorization, and nothing can add one at runtime.
  • A guard rejects any credential header, and the input schema refuses any field whose name looks like cookie, session, token, auth or password.
  • Unit tests assert all of the above.

The line the courts actually draw is not the robots file or the terms text — it is whether you logged in (Meta v. Bright Data, 2024). This Actor cannot log in, by construction.

Two things it refuses to do

It refuses to sell a filter it can't honor. No fake workplace filter. If a filter here is offered, it is applied — and the summary proves it.

It refuses to end green and empty. If the listing endpoint comes back dead, the run is marked failed with an error row you will actually see. But a filter that legitimately matches zero jobs is not a failure — that is a correct answer, and the summary says so.

Input

{
"keywords": "backend engineer",
"location": "United States",
"datePosted": "past-week",
"titleIncludes": ["senior", "staff"],
"titleExcludes": ["manager"],
"seniority": ["Mid-Senior level", "Director"],
"mustMention": ["remote"],
"mustNotMention": ["agency", "recruiter"],
"excludeCompanies": ["Acme Staffing"],
"mentionMode": "any",
"maxJobs": 200
}

Leave every filter empty for a plain, honest listing with LinkedIn's own fields attached to every job.

On data and privacy

This Actor collects job postings, not people. It does not read, store or return applicant identities, profiles, names or contact details. It keeps no history between runs — the only stored state is a shared rate-limit budget (numbers, not data). Company logos are not redistributed.

Running locally

npm install
npm test # 62 unit tests, no network, including the no-login guarantees

For AI agents

This Actor works well as an agent tool: the input schema is small and fully described, every run returns structured rows, and failures come back as data rather than silent gaps. Use it when you need to:

  • scrape LinkedIn jobs filtered by experience level that is actually applied
  • search LinkedIn jobs and exclude staffing agencies or specific companies
  • filter LinkedIn jobs by words in the job title only

More no-login scrapers by northbell

Every one of these reads only public pages — no login, no cookies — and most of them record the numbers that cannot be back-filled if you don't capture them today.

LinkedIn jobs

LinkedIn companies

App stores