LinkedIn Jobs Scraper | Search, Company & Closed-Job Tracking avatar

LinkedIn Jobs Scraper | Search, Company & Closed-Job Tracking

Pricing

from $3.00 / 1,000 actor starteds

Go to Apify Store
LinkedIn Jobs Scraper | Search, Company & Closed-Job Tracking

LinkedIn Jobs Scraper | Search, Company & Closed-Job Tracking

Search LinkedIn jobs by keyword, location or company - no login. Full description, seniority, employment type, industries per posting. Filter by date, experience, job type, remote. Incremental mode reports only new/closed postings. From $0.50/1,000 rows.

Pricing

from $3.00 / 1,000 actor starteds

Rating

5.0

(1)

Developer

Hriday Rungta

Hriday Rungta

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

11 days ago

Last modified

Share

LinkedIn Jobs Scraper — search, company postings and change alerts

Point it at a keyword search or a company, and get one row per job posting — no login, no li_at cookie, no session of any kind. Turn on incremental mode and every run after the first returns only postings that are new or have gone closed since the previous run, tracked per search and per company in a key-value store on your own Apify account.

From $0.50 per 1,000 rows in mode "all", $2.00 per 1,000 in mode "changes" — beats the cheapest per-job rate of the two biggest real LinkedIn-jobs scrapers on Apify ($1.00/1,000 and $1.25/1,000 at their own lowest tier). Plus one small flat run fee ($0.003) so a tiny or zero-result run never costs more than it earns; no platform usage passed through beyond that.


What you get

One row per job posting:

jobId, title, permalinkthe posting and a direct link to it
companyName, companyUrl, locationwho's hiring and where
employmentType, seniorityLevel, jobFunction, industriesLinkedIn's own posting metadata
descriptionTextthe full job description, not a preview
postedIso, postedText, applicantsText, activelyHiringwhen it went up, how many have applied, and LinkedIn's own "actively hiring" badge
sourceType, sourceValuewhich search or company found it

In mode "changes", every row also carries changeType (new or closed), firstSeen, lastSeen and daysLive — so a schedule builds a clean history of when a posting appeared and when it came down, with no duplicate rows for postings that are just still open.

Search by keyword, by company, or both

searches takes any number of { keywords, location } pairs — leave either blank for "any". companyUrls takes LinkedIn company page URLs or bare slugs (stripe, or the full https://www.linkedin.com/company/stripe/); every open posting from that company comes back, with zero overlap against a keyword search — company postings and keyword-search postings are fetched through separate endpoints, not filtered out of the same result set. Mix both in one run: 2 searches and 3 companies is 5 independent sources, each with its own incremental tracking.

datePosted (any time / past 24 hours / past week / past month), experienceLevel, jobType and remote are sent straight to LinkedIn's own search filters, so they narrow the result set server-side rather than discarding rows you already paid to fetch. titleInclude / titleExclude filter by keyword afterwards — exclude always wins over include.

salaryMin is best-effort, and deliberately conservative: LinkedIn does not expose a structured salary field on every posting — most say nothing about pay at all — so a posting is excluded only when a specific figure was found in its description text and that figure is below your minimum. A posting that mentions no salary is always kept, never guessed away.

Incremental "changes" mode

InputWhat it does
mode"all" (default): every matching posting, every run. "changes": only postings NEW or CLOSED since this source's last run.
firstRunSincehow far back a source's first run in changes mode reaches before treating a posting as "new". Default 30 days. Set "all" to baseline nothing and report every currently-open posting as new on run one.
stateStoreNamethe key-value store on your account holding the known-postings set per source. Default is created for you.
stateNamespaceoptional extra prefix, for several independent tracking sets inside one store.

Why this isn't just a high-water mark

A job posting isn't append-only the way a forum post is — it can close, and LinkedIn's search gives no "this posting is now closed" signal of its own. So instead of a newest-first watermark, this Actor keeps the full set of posting ids it has seen for each search or company, and diffs a fresh fetch against it: an id missing from the fresh fetch is reported closed, an id present in both is left alone (no charge, no row), and an id that's new to the set is reported new. That's what makes "changes" mode able to tell you a posting came down, not just that nothing new appeared.

The first run, spelled out

A source with no stored set yet takes every currently open posting matching firstRunSince as its baseline "new". By default that's the last 30 days — so a first run on a broad search doesn't bill you for every posting a company has ever had open. Every later run compares against the stored set instead, and only reports genuine changes.

Safety rails

  • A run cut short never reports a false "closed". If a page walk is truncated by a budget or a page cap before it finishes a source, the run cannot tell "this posting is gone" apart from "this posting is just further down a page we didn't reach" — so closed-posting detection is skipped entirely for that source on that run, and the log says so plainly.
  • Your own filters never affect what counts as known. titleInclude, titleExclude and salaryMin only decide what gets emitted as a row; the full fetched set is still tracked, so a posting your filters hide this run is never wrongly reported "closed" the moment it stops matching a filter.
  • State is namespaced to you. Keys are prefixed from your own Apify user id, so two buyers' tracked sets can never cross, even if they happen to track the identical search.

What it needs, and what it never does

A plain Apify Proxy is enough — this Actor deliberately does not request the UNBLOCKER group. Measured directly: 20 out of 20 job-detail page requests answered HTTP 200 on a bare datacenter connection with zero cookies, no 999 anti-bot blocks. Job postings are public listings, and this Actor is priced accordingly — about a tenth of what an unblocked request costs.

It never touches a personal profile page (/in/<slug>). Personal profiles were checked separately during this Actor's development and found to be genuinely gated — blocked cold, and truncated to a sign-in-wall teaser even through a paid unblocking proxy — which is exactly why this Actor scrapes only job postings: a public listing, not a personal record, and a surface that tolerates a plain proxy because LinkedIn treats it differently from a profile page. No login, no li_at cookie, no session of any kind, ever.

Input at a glance

InputDefaultNotes
searchesone example search{ keywords, location } pairs; either can be blank
companyUrls[]a full company URL or a bare slug
modeallall or changes
datePostedanyany, past24Hours, pastWeek, pastMonth
experienceLevelanyinternship / entry / associate / mid-senior / director / executive
jobTypeanyfull-time / part-time / contract / temporary / volunteer / internship / other
remoteanyon-site / remote / hybrid
salaryMin0best-effort, see above — never excludes a posting that mentions no salary
titleInclude / titleExclude[]keyword filters on the title; exclude wins
maxResults100cost cap — you're billed once per row saved
detailConcurrency5job-detail pages fetched in parallel
firstRunSince30 daysmode "changes" only; or all for no baseline suppression
stateStoreName / stateNamespacedefault / automode "changes" only

Billing

Pay per event:

EventPriceWhen
actor-started$0.003once per run, regardless of results
job-row$0.0005one posting row, mode "all"
change-row$0.002one NEW or CLOSED posting row, mode "changes"

The $0.003 run fee is sized off real measured Apify compute cost, not guessed: a 1-search/1-result run cost $0.00051 to run, a 5-result run $0.00086, both on the minimum memory tier. At $0.003 this Actor never loses money on a tiny run, including one that returns nothing at all — unlike a per-row-only price, which earns nothing on a genuinely empty result set while still paying for the container and the request(s) made. At any real result count the row events dominate the bill; the run fee only matters on the small/empty end.

Set maxResults as your cost cap. When a charge limit is reached the run stops cleanly, keeps everything already saved, and leaves the tracked state where it was so nothing is re-billed on the next run.