LinkedIn Company Jobs Scraper — Every Open Role, No Cookies
Pricing
from $0.64 / 1,000 jobs
LinkedIn Company Jobs Scraper — Every Open Role, No Cookies
Give it a company - LinkedIn URL, website, name or numeric ID - and get every role it has open on LinkedIn right now: title, location, the exact posting date and a link. Turn on trackChanges to see which roles opened and closed since the last run. No login, no cookies.
Pricing
from $0.64 / 1,000 jobs
Rating
0.0
(0)
Developer
Northbell
Maintained by CommunityActor stats
0
Bookmarked
13
Total users
6
Monthly active users
10 hours ago
Last modified
Categories
Share
LinkedIn Company Jobs Scraper
Give it a company. Get every role that company has open right now.
$0.01 per company plus $0.80 per 1,000 jobs — 10 companies with 50 open roles each cost $0.51 including the run start.
Most LinkedIn job scrapers take a search query. This one takes a company — in whatever form your list has it:
linkedin.com/company/stripe, stripe.com or just Stripe. A website is matched to the LinkedIn page whose own
website is the same, and companies LinkedIn splits over several IDs (Johnson & Johnson has five) are searched as one.
No login. No cookies. No account. Public pages only.
What you get
One row per open role:
| Field | Meaning |
|---|---|
title, location | The role and where it is |
postedOn | The real posting date, not "3 days ago" |
activelyHiring | Whether LinkedIn flags the company as actively hiring |
jobUrl | Direct link |
And one summary row per company: openJobs, byLocation, the resolved companyId, and
collectionComplete (see below).
When a company comes back with 0 jobs, its row says why in note. LinkedIn dates a job by when it was
first posted, so a company that is hiring but posted nothing new this week returns 0 with Posted within: Past week
(on 2026-10-09 HubSpot had 0 jobs from the past week, 12 from the past month and 30 or more at any date). The Actor then
checks once without the date filter and writes, for example,
No job posted in the past week. ... this company has 10 or more open jobs posted earlier: set "Posted within" to "Any time" to get them.Run it again and again
- On a schedule — save your input as a task and add a Schedule: the same companies every morning. Send each run to Google Sheets, Slack or email with an integration on the task.
- Watch for changes — Turn on Track what changed since last run: each run reports the roles opened and closed since the previous one (when both runs read the same filters in full).
- In bulk — Put every company in Companies in one run.
How complete is the list?
LinkedIn's public job listing does not paginate the way you would expect. Asking for
start=20 does not return "results 21–30" — it returns a different random sample. Fetch
the same company twice with naive paging and 40–60% of the job IDs differ. Offsets of 1000
or more are rejected outright.
So a naive scrape of a large company returns a sample, not the roster. Measured on a company with 880 open roles: naive paging returned 660 — a quarter of the roles silently missing, with no indication anything was wrong.
This Actor keeps sampling and measures its own coverage. Splitting the requests into two independent halves and comparing the overlap gives an estimate of the true total — the same mark-and-recapture method used to count fish in a lake. It keeps going until the estimated coverage reaches your target.
Every company row carries the result:
| Field | Meaning |
|---|---|
openJobs | Roles actually collected |
estimatedTotal | Estimated true number of open roles |
coverage | Fraction collected (e.g. 1.0) |
estimatedMissing | Roles believed still unseen |
requestsUsed | What it cost to get there |
Verified against that 880-role company: 880 collected, 880 estimated, coverage 1.0, in 452 requests.
Optional: what changed since last run
Closed roles vanish from LinkedIn and leave nothing behind, so "what disappeared" cannot be
reconstructed after the fact — only recorded. Turn on trackChanges and each run compares
against the last.
This is only reported when the comparison is sound. Two runs, both at full coverage, sixty seconds apart, on that same 880-role company:
opened: 0 closed: 0 changeMarginOfError: 0
Which is the correct answer — nothing changed in a minute. An earlier version of this Actor
reported "170 opened, 159 closed" for exactly that case, because it was comparing two
different samples. It now refuses to answer unless coverage is high, and reports
changeMarginOfError so you can see how much of any number could be measurement noise.
Opened/closed come back as null, with a reason, when: it is the first run, coverage did not
reach the target, or you changed the filters between runs (which changes which roles were in
scope). Hiring pace additionally needs 7 days of observation.
Cost of certainty: full coverage of a 880-role company took ~450 requests — about 15
minutes at the default 30 requests per minute (about 8 at 60). You are charged per job returned, not per request, so this costs you time rather
than money. Set targetCoverage lower (default 0.99) if you would rather have speed.
Input
{"companies": ["https://www.linkedin.com/company/stripe/","anthropicresearch","1035"],"maxJobsPerCompany": 1000,"location": "United States","datePosted": "past-week"}
companies— company URL, slug, or numeric ID, or just the company website (stripe.com) or name (Scale AI). A website is matched to the LinkedIn page whose own website is the same, and the company row says so indomainMatch(true,same-namefor stripe.com vs stripe.dev,falsewhen only a same-named page was found); names that had to be turned into a guess carryguessedFrom. A name with a legal suffix (Stripe, Inc.,Deutsche Bank AG) is also tried without it, and that page is used only when its company name matches what you typed (matchedBy: "name-without-legal-suffix"). A name whose LinkedIn address does not follow it (McDonald's,Nestlé), and a one-word name whose handle belongs to a smaller company (anthropic), is also looked up on Wikidata, which records many companies' LinkedIn page and official website; that page is used only when its own website matches the one Wikidata lists (matchedBy: "wikidata", with thewikidataId). This adds about 4 seconds per name the first time (answers are remembered for 30 days);lookUpNames: falseturns it off. Two entries that end on the same company are collected and charged once; the second gets a free row that says so. You can also paste a LinkedIn job posting URL: the company that posted it is tracked (matchedBy: "job-posting"). An entry that cannot be read becomes a free error row and the rest of the run goes on. Country subdomains (uk.linkedin.com/...), non-Latin page addresses andurn:li:organization:<id>work too. Showcase pages are refused with a reason: their jobs are posted under the parent company.location,keywords,datePosted— optional filters, applied on top of the company filter. These three actually work — we verifydatePostedagainst the returned postings' real dates. There is deliberately no workplace or experience filter here: LinkedIn's public search acceptsf_WT/f_Eand silently ignores them (we measured it — "remote" and "on-site" queries return 88% the same postings). IfworkTypeorexperienceis sent anyway, the run carries on and says so — in the status, in OUTPUT (ignoredInput) and in a freenoticerow — so you never mistake the results for filtered ones. If you need a real experience filter, use LinkedIn Jobs Scraper — Filters That Actually Work, which applies it from each job page's own value.maxJobsPerCompany— default 1000, max 5000. Set it above the company's real number of open roles. You are charged per job returned, not per request, so a generous ceiling costs nothing extra for a smaller company.trackChanges— off by default. See above for when it is worth turning on.
Pricing
Pay per event. You are charged for what you receive.
| Event | Price |
|---|---|
| Run started | $0.01 |
| Per company | $0.01 |
| Per job | $0.0008 ($0.80 / 1,000) |
Job rows are priced the same as a plain listing scrape, because that is what they are.
Failed company lookups are not charged. A company with zero open roles still returns a company row — "they are not hiring right now" is an answer, not a failure.
How it works
Reads only LinkedIn's public, logged-out job listing pages — 10 roles per request. Because each request returns a fresh sample rather than a fixed page, the Actor keeps requesting until several rounds in a row bring nothing new, then stops. A 670-role company took about 93 requests this way.
The company ID is resolved from the public company page using two independent markers that must agree. If they disagree, the Actor returns an error instead of a guess — returning another company's jobs under your company's name would be worse than returning nothing.
The line this Actor does not cross
Logging in is a design decision, not a promise:
- Request headers are a frozen object.
CookieandAuthorizationcannot be attached at runtime. - Input fields that look like credentials (
cookie,session,token,li_at,password) are rejected. - Both are covered by unit tests.
Related Actors
| Actor | Use it when |
|---|---|
| LinkedIn Jobs Scraper with Applicant Counts | You want applicant counts and how fast they grow |
| Fast LinkedIn Jobs Scraper | You want volume and speed, by search query |
| LinkedIn Company Scraper with Headcount Growth | You want employee counts and growth |
| LinkedIn Company Posts Scraper | You want what a company publishes and how it lands |
Pair this with the headcount scraper: headcount tells you how big a company is, open roles tell you where it is going next.
**Something broke or looks wrong?** Open an issue on this Actor's Issues tab and I'll look at it. **Found it useful?** A short review on the Store page helps other people find it. Our own running cost is about **$0.035 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.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 all open jobs at a specific company on LinkedIn
- track which roles a company opened or closed since the last check
- list every job a company is hiring for, with measured coverage
How the coverage number is measured
The write-up of the method, and the four bugs behind it, is published under the Apify organisation on DEV: My scraper returned 660 jobs. There were 880. Nothing in the output said so.
The estimator, the guards and the tests that pin down each bug are open source, with no dependencies: northbell-dev/honest-scraping
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
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