LinkedIn Jobs Scraper with Applicant Count — No Cookies
Pricing
from $1.20 / 1,000 job with applicant counts
LinkedIn Jobs Scraper with Applicant Count — No Cookies
Scrape public LinkedIn job posts with their applicant count — by search, or paste the job URLs you already have. Keep the series to see how fast applicants arrive and how long each job stays open. No login, no cookies.
Pricing
from $1.20 / 1,000 job with applicant counts
Rating
0.0
(0)
Developer
Northbell
Maintained by CommunityActor stats
0
Bookmarked
5
Total users
2
Monthly active users
a few seconds ago
Last modified
Categories
Share
LinkedIn Jobs Scraper with Applicant Counts
Scrape public LinkedIn job posts — title, company, location, exact posting date, seniority, employment type, description, and how many people have applied.
$2.30 per 1,000 jobs with applicant counts — 100 jobs cost $0.24 including the run start.
Then keep running it, and you get the thing a single scrape cannot give you: how fast those applicants are arriving.
The number that goes stale in an hour
A job post showing 141 applicants tells you almost nothing on its own. 141 applicants on a job posted yesterday is a stampede. 141 applicants on a job posted five weeks ago is a role nobody wants.
Same number. Opposite meaning. The difference is time, and LinkedIn does not keep it for you.
Applicant counts cannot be back-filled. There is no endpoint that tells you what a job's count was last Tuesday. If you did not record it that day, that day is gone. This Actor records it and keeps the series:
job 4419969671 Senior Software Engineer — General MotorsAug 20 21 applicants postedAug 21 58 applicantsAug 22 97 applicantsAug 24 141 applicants → 30 per day, still open, 4 days
What you get
Every run appends to your dataset. Rows are tagged by type.
job — one row per job.
| field | meaning |
|---|---|
applicants | how many have applied right now |
applicantsPerDay | the rate, measured across your own observations |
applicantsPerDayReliable | false when the figure cannot be trusted — see below |
applicantsAddedSinceLastRun | new applicants since you last looked |
postedOn | the exact date from LinkedIn's own datetime attribute, not "3 days ago" |
daysOpen | how long it has been live |
closed / closedOn | it stopped accepting applications, and when you first saw that |
isRepost, timesPosted, daysSincePreviousPosting | the same company advertising the same role again |
observations | how many times you have sampled this job |
title, company, companySlug, location, seniority, employmentType, industries, description, salaryMentioned | the posting itself |
jobFunction | always null — LinkedIn no longer shows "Job function" on public job pages (checked 7 Oct 2026); kept so existing integrations keep working |
vanished — a job that was in your results last run and is not there now, with its last known applicant count. A job disappearing silently is indistinguishable from a broken scraper, so it gets a row.
unchanged — only with Only jobs whose applicant count changed (onlyChanged): one row listing the jobs whose count is the same as at your last run, each with its jobId, title, company and count. They were checked and billed like any other job (listing + detail); they just do not get a row of their own. The unchanged row itself is free.
error — anything that failed, written where you will actually see it.
Three questions this answers that a snapshot cannot
"Is it worth applying?" — 141 applicants in one day means you are competing with a crowd. 12 applicants in fourteen days means the posting is quiet and your application will actually be read.
"Is this company really hiring?" — a role that closes in four days was filled. A role reposted three times in six months was not. isRepost and timesPosted separate genuine demand from a job ad that has become furniture.
"How fast is this market moving?" — applicants-per-day, aggregated by company, seniority or region, is a hiring-velocity signal you own outright, because you are the one who recorded it.
Numbers this Actor refuses to give you
LinkedIn stops counting precisely at a threshold and starts saying Over 200 applicants. Subtracting one rounded figure from another produces a rate that looks precise and is not.
So: when either end of an interval is capped, applicantsPerDayReliable is false and applicantsPerDayNote says capped-by-linkedin-so-this-is-a-lower-bound. The same applies on a job's first observation (first-observation — one point has no slope), within the first half-day (observed-for-less-than-half-a-day — dividing by a tiny interval explodes), and when a count goes down (count-went-down).
A rate without that flag is a rate that will eventually lie to you.
The other end has the same problem. A new job often shows Be among the first 25 applicants — that means fewer than 25, not 25. For those jobs applicants is null and applicantsFewerThan is 25 (last column). The rate starts from the first real count. (Until 17 October 2026 this Actor reported such jobs as exactly 25; if you stored those rows, treat applicants: 25 with applicantsCapped: false from before that date as "under 25".)
No login. Not as a policy — as a property of the code.
This Actor never signs in, never asks you for a session cookie, and never sends one. It reads LinkedIn's public guest endpoints, the same pages an anonymous visitor sees.
That is enforced, not promised:
- The request headers are a frozen object with no
Cookieand noAuthorizationfield, and nothing can add one at runtime. - A guard rejects any attempt to attach a credential header, and the input schema refuses any field whose name looks like
cookie,session,token,authorpassword. - Unit tests assert all of the above, including that no session identifier ever reaches your dataset.
If a competing tool asks you to paste your li_at cookie, it is operating your LinkedIn account on your behalf. This one cannot, by construction.
Two more things it gets right
A failed fetch becomes a row, not a log line. Nobody reads run logs. If the listing endpoint refuses, you get an error row in the dataset, and the run is marked failed when a whole requested feature came back empty. A green run with an empty dataset is the worst outcome for anything you check once a day.
A closed job is data, not a failure. When a job's page returns 404 the Actor records closed: true with the date — that is the end of the job's life, which is exactly what you were measuring. It does not count as an error and does not fail the run.
Input
A city in the keywords is searched as the location. LinkedIn ignores a city written inside the keywords: data engineer austin with Location United States returned jobs from all over the US. When the keywords contain a major city and Location is empty or that city's country, the search runs as data engineer in Austin, United States (live: 10 of 10 jobs in Austin) and the log says so. A Location set to a city or another country, and a geoId, are never changed. (Fixed 22 Oct 2026.)
Already have the job URLs? Paste them into Job URLs (jobs) instead of searching — for example the URLs you send to the free LinkedIn Job Details one at a time. Each job gets its applicant count at the same price (up to 1,000 URLs per run; a line that is not a job URL is skipped on a free error row and the rest still run); call it again on the same jobs and every row also carries applicantsAddedSinceLastRun and applicantsPerDay, because the history of each job is kept in your account between runs. Added 2026-10-17.
{"jobs": ["https://www.linkedin.com/jobs/view/4419969671"]}
Or search:
{"keywords": "software engineer","location": "United States","maxJobs": 50,"datePosted": "past-24-hours","fetchDetails": true,"onlyTrackedJobs": false,"onlyChanged": false,"maxRequestsPerMinute": 20}
Sizing a run
Ten jobs per listing request, plus one request per job for the applicant count.
requests = ceil(maxJobs / 10) + maxJobs
50 jobs is 55 requests, about three minutes at the default rate. When LinkedIn asks it to slow down (HTTP 429), it waits and carries on, so the same run can take longer: 49 jobs took 8 minutes on the evening of 17 Oct 2026. The example in the input form is 20 jobs.
Set fetchDetails: false for a listing-only run at a tenth of the requests — you lose the applicant count, which is the point of this Actor, but it is there if you only want the postings.
Daily runs
Set onlyTrackedJobs: true. New jobs are still listed, but only jobs you already have a history for get their detail page fetched — so you pay to keep watching the series you are building, not to re-scrape the whole market every morning.
Only the jobs that moved. Set onlyChanged: true when the rows go to an alert, a sheet or an agent: a job whose applicant count is the same as at your last run gets no row of its own (it is listed in one unchanged row, and it is still checked and billed like any other job), so what arrives each morning is the new jobs, the jobs that gained applicants, the closed ones and the reposts. Added 30 Oct 2026.
Put it on a schedule. The history lives in your account, so the same input run every day builds the series by itself:
- Apify Console: save the input as a task, then add a Schedule (for example every day at 08:00).
- API:
POST https://api.apify.com/v2/acts/northbell~linkedin-jobs-applicants-scraper/runswith the same JSON body each time, from cron or any scheduler. - n8n, Make, Zapier or an MCP client: the Apify node or app (or
https://mcp.apify.com?tools=northbell/linkedin-jobs-applicants-scraper) with the same input; read the dataset when the run finishes.
What you pay for
Pay per event, charged only for results actually delivered:
| event | when |
|---|---|
| Actor start | once per run that returns a job |
| Job listed | one job from the listing |
| Job detail | one job's applicant count, seniority, type and description recorded |
What you never pay for:
- A failed fetch. If the detail page errors, you get the listing row and the listing charge only. You are paying for data, not attempts.
- A run that returns nothing. The $0.01 run-start fee is charged together with the first job, so a search with no results, or a run LinkedIn refuses outright, costs $0.
- Rows you did not receive. Every row is saved to your dataset before it is charged. A run you stop early keeps everything it charged for, and a run near its time limit stops 90 seconds early and lists the jobs it did not read in a free row, so you can run just those again. (With
onlyChangedon, unchanged jobs are checked and billed like any other job and listed in theunchangedrow.)
Limits worth knowing
- Ten jobs per listing page. Deep result sets thin out; LinkedIn does not serve unlimited pages to guests.
- Applicant counts are capped above a threshold. See above.
daysOpenuses the posting date when LinkedIn gives one, and falls back to the date you first saw the job.daysOpenBasistells you which, every time.- The rate-limit budget persists in a key-value store, so overlapping runs of this Actor share one budget rather than stacking up.
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 — the applicant figure is a count and nothing else.
Job descriptions pass through to your dataset but are never written to the Actor's own history. The persistent store holds numbers and identifiers only: job ids, applicant counts, dates.
Company logos are not redistributed.
Storage
History lives in a named key-value store, linkedin-jobs-history, so it survives between runs. Deleting it resets the baselines — every job reports as a first observation again, and velocity goes quiet until it has two samples.
Running locally
npm installnpm test # 34 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 job postings with applicant counts, without logging in
- find LinkedIn jobs with few applicants (low competition)
- track how fast applicants arrive on a LinkedIn job posting
Use it from Claude Code (or another coding agent)
Save this as ~/.claude/skills/linkedin-job-applicants/SKILL.md (other agents that read SKILL.md files: their skills folder), put your Apify API token in the APIFY_TOKEN environment variable, and ask the agent in plain words — "Find software engineer jobs in Berlin posted this week with fewer than 25 applicants".
---name: linkedin-job-applicantsdescription: LinkedIn job postings with their applicant counts, from a search (keywords and location) or a list of job URLs, without logging in. Runs the Apify Actor northbell/linkedin-jobs-applicants-scraper with the user's APIFY_TOKEN.---Run: curl -s -X POST "https://api.apify.com/v2/acts/northbell~linkedin-jobs-applicants-scraper/run-sync-get-dataset-items?timeout=300" -H "Authorization: Bearer $APIFY_TOKEN" -H "Content-Type: application/json" -d '{"keywords": "software engineer", "location": "Berlin", "datePosted": "past-week", "maxJobs": 50}'For long lists start `/runs` instead and read the dataset when the run finishes.Rows carry the job title, company, location, postedOn and applicants (above 200 LinkedIn only says Over 200). Filter by applicants yourself after the run.Never print the token. Tell the user the price on the Actor's Store page before a large run.
Prefer MCP? Add https://mcp.apify.com?tools=northbell/linkedin-jobs-applicants-scraper as a remote MCP server — in Claude Code: claude mcp add --transport http apify "https://mcp.apify.com?tools=northbell/linkedin-jobs-applicants-scraper" — and sign in to Apify when asked; the Actor then shows up as a tool.
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 Jobs Scraper with Applicant Counts — jobs plus how fast applicants are arriving
- LinkedIn Jobs Scraper — Filters That Actually Work — the experience/workplace filters LinkedIn silently ignores, applied for real
- LinkedIn Jobs Salary Data — Filter by Pay — salary parsed into numbers so you can filter by yearly pay
- Fast LinkedIn Jobs Scraper — bulk job listings, cheap and quick
- LinkedIn Company Jobs Scraper — every open role at a company you name
LinkedIn companies
- LinkedIn Company Scraper with Headcount Growth — the real headcount and how fast it's growing
- LinkedIn Company Posts + Engagement — a company's posts with exact reaction and comment counts
- Official Company Headcount — the headcount from business registers and tax boards (8 European countries, Japan and the US), with source and date
App stores
- App Store Rank & Rating Scraper — iOS keyword rank and rating changes over time
- Shopify App Reviews Scraper — Filter & Sort by Rating — exact per-star review counts, filter and sort
- Google Play Rating & Review Tracker — an Android app's rating tracked day by day