LinkedIn Jobs Search Scraper — Experience Filter That Works avatar

LinkedIn Jobs Search Scraper — Experience Filter That Works

Pricing

from $1.20 / 1,000 jobs

Go to Apify Store
LinkedIn Jobs Search Scraper — Experience Filter That Works

LinkedIn Jobs Search Scraper — Experience Filter That Works

LinkedIn jobs search scraper 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.

Pricing

from $1.20 / 1,000 jobs

Rating

0.0

(0)

Developer

Northbell

Northbell

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

0

Monthly active users

3 days ago

Last modified

Security

Limited permissions

Share

LinkedIn Jobs Scraper — Filters That Actually Work

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.

Try it now. The input is already filled in. Press Start: software engineer jobs in the United States — 20 job pages read in full, for about $0.05. It takes 2 to 8 minutes: when LinkedIn asks it to slow down (HTTP 429), it waits and carries on (on 2 Nov 2026 the same run took 7½ minutes).

titlecompanylocationpostedOnseniorityemploymentType
Software Engineer 1, BackendRobinhoodNew York, NY2026-10-14Not ApplicableFull-time
Software Development Engineer, AWS IAM Distributed Database SystemAmazon Web Services (AWS)Seattle, WA2026-10-01Mid-Senior levelFull-time
Senior Software Engineer – Go (Golang)General MotorsWarren, MI2026-10-04Not ApplicableFull-time

The example sets no filter, so all 20 come back, each with LinkedIn's own seniority and employment type, job function, industries and the full description, plus a summary row of what was checked (LinkedIn filled in the experience level on 7 of these 20). Set a filter — experience level, words the job must or must not mention, companies to leave out — and it is checked on every job page and counted in the summary. Jobs to examine is your cost ceiling; the form starts at 20.

$1.90 per 1,000 jobs read — 100 jobs cost $0.20 including the run start.

New: jobs that don't need the local language. Set Only jobs that don't need the local language and only jobs whose text says so come back. A real run on 15 Oct 2026 — software engineer in Tokyo, Japan, 60 jobs examined in about 2.5 minutes, about $0.12 — returned 5, including:

titlecompanylocalLanguageNeedevidence (from the job text)
Senior Software Engineer (Backend/Infrastructure)HENNGEnot-requiredFluency in English (Japanese is not required)
Senior Software Engineer (Python/Web Dev)HENNGEnot-requiredFluency in English (Japanese is not required)
Java Microservices Sr EngineerWipropreferredEnglish (Proficient), Japanese is plus.

Of the other 55, 9 asked for Japanese and 46 said nothing about it. Silence is not "not required", so those were left out and counted in the summary row. Works the same for German, French, Spanish, Italian, Dutch and Polish (details below).

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.—
localLanguageFilterKeeps only jobs whose text says the local language is not required (or, with the second option, only a plus) — "jobs in Tokyo that don't need Japanese", "jobs in Berlin that don't need German". Read from what the description says, not from the language it is written in: "fluent German (C1) required" in an English post counts as required. The country comes from the job's location (Germany, Austria, Switzerland, Japan, France, Italy, the Netherlands, Belgium, Spain, Poland, Latin America).Most posts say nothing about it, and silence is not "not required": those are dropped and counted separately in the summary. Expect a short list.

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, industriesLinkedIn's own values, off the job page
jobFunctionalways null — LinkedIn no longer shows "Job function" on public job pages (checked 7 Oct 2026); kept so existing integrations keep working
salaryMentionedwhether the posting states pay (LinkedIn's own pay box or an amount in the description, in any currency) — never invented when it doesn't. For the numbers, use LinkedIn Jobs Salary Data
descriptionthe full text
jobUrllink to the posting
localLanguage, localLanguageNeed, localLanguageLevelthe local language of the job's country, whether the text asks for it (required, preferred, one-of, not-required, or unknown when it says nothing; null when no text was read), and the level it names (native, fluent, good, basic, a CEFR level such as B2, a JLPT level such as N2)
languagesRequiredevery language the text names: { language, level, need, matched, evidence } — matched are the words that decided it and evidence the sentence, so you can check each call

Changed 2026-10-15: the four language columns were added at the end of each job row; existing columns and their order are unchanged.

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. The $0.01 run-start fee is the one charge every run pays, even one that returns nothing.

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

Our own running cost is about **$0.093 per 1,000 rows** of Apify platform usage at 512 MB (22-run measurement, Sept 2026 — [how it was measured](https://dev.to/apify/i-measured-what-my-11-actors-cost-to-run-the-96x-spread-was-mostly-one-config-field-hoj)) — already included in the price; nothing is billed on top.