LinkedIn Jobs Scraper
Pricing
from $1.50 / 1,000 job scrapeds
LinkedIn Jobs Scraper
Anonymous LinkedIn job search (no cookies, no login): typed salary, applicants, seniority, ISO posting dates, provenance block on every item — plus a job-alert mode with cross-run dedupe: scheduled runs push only jobs never seen before. Errors are never charged.
Pricing
from $1.50 / 1,000 job scrapeds
Rating
0.0
(0)
Developer
Torchtechnology LTD
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
12 days ago
Last modified
Categories
Share
What does LinkedIn Jobs Scraper do?
LinkedIn Jobs Scraper extracts typed, provenance-stamped job postings from LinkedIn Jobs — anonymously, with no cookies and no LinkedIn account — using LinkedIn's public guest search. Search by keywords × locations with a date-posted filter, and get clean JSON: title, company, location, ISO posting date, applicants count, typed salary (min/max/currency/period, where the company publishes one), seniority, employment type, and — opt-in — the full description text. Every item carries an audit-proof provenance block (source URL, HTTP status, SHA-256 content hash) and a per-field coverage status that never invents a value.
Its standout feature is alert mode: give your search a watchlistId, schedule the actor daily, and each run pushes only jobs never seen before — a job-alert delta feed as a data product, with true cross-run deduplication that persists between runs.
The actor runs on the Apify platform, so you get API access, scheduling, integrations, proxy rotation, and run monitoring out of the box — no infrastructure to manage. Pricing is pay per job with no platform-usage passthrough, and errors are never charged. Try it on the Apify free tier in a couple of clicks.
What makes this LinkedIn Jobs Scraper different?
Most LinkedIn job scrapers are commodity keyword scrapers that re-deliver the same postings on every run and leave you to dedupe them yourself. This one was built for pipelines you can defend — typed values, honest nulls, and a persistent alert history.
| Capability | LinkedIn Jobs Scraper (this actor) | curious_coder/linkedin-jobs-scraper (12.7K users/30d, $2/1K, ★4.36) | cheap_scraper/linkedin-job-scraper (9.5K users/30d, $0.70/1K, ★3.90) | valig/linkedin-jobs-scraper (4.9K users/30d, $0.40/1K) | fantastic-jobs/advanced-linkedin-job-search-api (3.1K users/30d, $5/1K, ★3.45) |
|---|---|---|---|---|---|
| Alert mode: only new jobs per run, history persists across runs | ✅ built in (job-new event) | ❌ | ❌ | ❌ | ❌ |
| Cross-run dedupe (watchlist store, job reported exactly once, ever) | ✅ | ❌ | ⚠️ within-run only ("remove duplicates") | ❌ | ❌ |
| Typed salary (min/max/currency/period as numbers, not strings) | ✅ {"min":103000,"max":131000,"currency":"USD","period":"year"} | ❌ string array | ❌ | ❌ | ⚠️ partial |
| Applicants count as integer | ✅ | ❌ string | ⚠️ | ❌ | ⚠️ |
| Provenance block (URL, HTTP status, SHA-256 hash per item) | ✅ on every item | ❌ | ❌ | ❌ | ❌ |
Per-field coverage status (obtained/absent/unknown) | ✅ | ❌ silent nulls | ❌ | ❌ | ❌ |
| Honest about LinkedIn's ignored filters (workplaceType/jobType/experienceLevel) | ✅ documented + parsed fields for client-side filtering | ⚠️ keyword-conversion hack | ⚠️ | ❌ | ❌ |
| Errors never charged, pushed as transparency items | ✅ | ⚠️ | ⚠️ | ⚠️ | ⚠️ |
| Pricing | $1.50/1K jobs, $2/1K new-job alerts, no usage passthrough | $2/1K | $0.70/1K | $0.40/1K | $5/1K |
User counts, ratings, and prices from the Apify Store API, August 2026.
Job alerts as a data product (alert mode)
This is the feature none of the established competitors have. With alertMode: true and a watchlistId of your choice, the actor keeps a persistent history (job-watchlist-<watchlistId> key-value store) of every job it has ever reported for that watchlist. Each run then:
- collects the current search results,
- diffs them against the watchlist history,
- pushes only the jobs never seen before (charged as
job-new), - records everything it saw, so a posting is never delivered twice — not tomorrow, not next month.
Combine it with an Apify Schedule and you have a job-alert pipeline with zero glue code:
- Run the actor once with
alertMode: trueand awatchlistId(e.g."backend-berlin"). The first run reports everything currently matching — that is your baseline. - In Apify Console, open Schedules → Create new, pick this actor, keep the same input (same
watchlistId), and set a cron like0 7 * * *(daily at 07:00). - From the second run on, your dataset contains only new postings — pipe them to Slack, email, a webhook, or your ATS via Apify integrations.
A good pairing is datePosted: "pastWeek" with a daily schedule: LinkedIn's date filter plus the watchlist diff keeps alerts fresh and duplicate-free.
How much does it cost to scrape LinkedIn jobs?
The actor uses pay-per-event pricing — you pay only for delivered results, and there is no platform-usage passthrough: unlike some competing LinkedIn scrapers, we do not bill you for compute units or proxy traffic on top. The event prices below are the whole cost (exact prices are always shown on the actor's Pricing tab in Apify Console):
job— $0.0015 per job ($1.50 per 1,000), charged once per successfully delivered job item. Detail hydration (includeDescription) is included — no add-on charge.job-new— $0.002 per new job ($2.00 per 1,000), charged in alert mode only, and only for jobs the watchlist has never reported.apify-actor-start— a one-time $0.01 per run.
Errors are never charged. Invalid inputs, blocked requests, and transport failures produce transparency items in the dataset at zero cost. Jobs that disappear (404) mid-run are reported, not billed.
Cost by volume (job event only, + $0.01 per run)
| Jobs per run | Standard mode | Alert mode (all new) |
|---|---|---|
| 100 jobs | $0.16 | $0.21 |
| 1,000 jobs | $1.51 | $2.01 |
| 10,000 jobs | $15.01 | $20.01 |
The Apify free tier is enough to try the actor on a handful of searches before you scale up. Alert mode with a daily schedule typically costs a few cents per month per watchlist — you only ever pay for genuinely new postings.
Who is this LinkedIn Jobs Scraper for?
| Audience | What they use it for |
|---|---|
| Job seekers & career platforms | Scheduled job alerts via alert mode — new matching postings as a clean delta feed, no duplicates, ever. |
| Recruiters & staffing agencies | Market mapping: who is hiring what, where, at which seniority — with typed salary benchmarks where published. |
| HR tech & ATS builders | A stable, typed ingestion feed (ISO dates, integer counts, structured salary) with provenance per item. |
| Labor-market researchers | Time-series of postings per keyword/location; the watchlist store doubles as a seen-set for longitudinal studies. |
| Sales & lead-gen teams | Hiring signals — companies posting relevant roles right now (datePosted: past24h, daily schedule). |
How to scrape LinkedIn jobs with LinkedIn Jobs Scraper
- Open the actor in Apify Store and click Try for free.
- On the Input tab, set Keywords (e.g.
["software engineer", "product manager"]) and/or Locations (e.g.["Berlin", "United States"]). - Optionally set Date posted (
past24h/pastWeek/pastMonth) and enable Include description & details for full text, salary, applicants, and seniority per job. - For a recurring alert, enable Alert mode, set a Watchlist ID, and attach an Apify Schedule (see above).
- Click Start, then download your results from the Output or Storage tab — or pull them via the Apify API.
Using the Apify API (JavaScript)
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('CyprusAPI/linkedin-jobs').call({keywords: ['software engineer'],locations: ['Berlin'],datePosted: 'pastWeek',includeDescription: true,maxJobs: 50,});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items);
Using the Apify API (Python)
from apify_client import ApifyClientclient = ApifyClient('<YOUR_APIFY_TOKEN>')run = client.actor('CyprusAPI/linkedin-jobs').call(run_input={'keywords': ['software engineer'],'locations': ['Berlin'],'alertMode': True,'watchlistId': 'backend-berlin',})for item in client.dataset(run['defaultDatasetId']).iterate_items():print(item)
Input
The actor accepts the following parameters (see the Input tab for the full form). Provide keywords and/or locations — every keyword is searched in every location.
| Field | Type | Description |
|---|---|---|
keywords | array | Search keywords (job titles, skills, companies). Default ["software engineer"]. |
locations | array | Free-text locations as on linkedin.com/jobs (e.g. "Berlin", "United States"). Defaults to the country name when empty. |
country | string | ISO alpha-2 code (US, DE, …). Default location + geo-pinned proxy exit. Default "US". |
datePosted | string | any (default), past24h, pastWeek, pastMonth. The one filter LinkedIn's guest search verifiably honors. |
workplaceType | string | on-site / remote / hybrid. Currently ignored by LinkedIn — see Known limitations. |
jobType | string | full-time / part-time / contract / … Currently ignored by LinkedIn — use includeDescription and filter on the parsed employmentType. |
experienceLevel | string | entry / associate / mid-senior / … Currently ignored by LinkedIn — filter on the parsed seniority. |
maxJobs | integer | Global cap per run, deduplicated by job ID. Default 10, max 1000. |
includeDescription | boolean | Fetch the detail page per job: descriptionText, applicantsCount, salary, seniority, employmentType, jobFunction, industries. Default false. |
alertMode | boolean | Push only jobs never reported before for watchlistId (charged as job-new). Default false. |
watchlistId | string | Persistent history identifier for alert mode. Default "default". |
proxyConfiguration | object | Optional — bundled geo proxies are included; enable only to force your own or Apify proxies. |
proxiesByCountry | object | Optional geo-pinned proxies per ISO country code, tried first. |
Output
One dataset item per job. List fields (title, company, location, posting date) come from the search cards; includeDescription adds detail-page fields. Every item carries coverage (per-field-group status) and capture (provenance). You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.
{"jobId": "4450182400","title": "Global Product Manager, Hardware","company": "Cricut","companyUrl": "https://www.linkedin.com/company/cricut","location": "South Jordan, UT","workplaceType": null,"postedAt": "2026-08-12","postedAtText": "1 week ago","applicantsCount": null,"applicantsText": "Be among the first 25 applicants","salary": { "min": 103000, "max": 131000, "currency": "USD", "period": "year", "raw": "$103,000.00/yr - $131,000.00/yr" },"seniority": "Mid-Senior level","employmentType": "Full-time","jobFunction": "Product Management","industries": "Manufacturing","benefits": ["Be an early applicant"],"descriptionText": "This range is provided by Cricut…","jobUrl": "https://www.linkedin.com/jobs/view/4450182400","promoted": null,"searchKeywords": "product manager","searchLocation": "United States","coverage": { "core": "obtained", "salary": "obtained", "applicants": "absent", "description": "obtained", "criteria": "obtained", "workplaceType": "unknown", "promoted": "unknown" },"capture": {"captured_at_utc": "2026-08-20T15:40:00+00:00","url": "https://www.linkedin.com/jobs-guest/jobs/api/seeMoreJobPostings/search?keywords=product+manager&location=United+States&start=0","http_status": 200,"content_hash": "sha256:9f2c…","detail": { "url": "https://www.linkedin.com/jobs-guest/jobs/api/jobPosting/4450182400", "http_status": 200, "content_hash": "sha256:71ab…" }},"scrapedAt": "2026-08-20T15:40:01+00:00"}
Fields follow a three-state discipline: a value means observed, null means verifiably absent, and "unknown" in the coverage block means not observable anonymously. "Be among the first 25 applicants"-style labels are kept in applicantsText but do not become a fake applicantsCount. Failed searches appear as transparency items with error and errorDescription and are never charged.
Data table
| Field | Description |
|---|---|
jobId, jobUrl | LinkedIn job ID and canonical, non-localized job URL (stable for dedupe) |
title, company, companyUrl, companyLogo | Posting and company identity |
location | Location as displayed on the posting |
postedAt, postedAtText | ISO date from LinkedIn's datetime attribute, plus the raw relative text ("2 weeks ago") |
applicantsCount, applicantsText | Integer applicant count where LinkedIn shows one ("Over 200 applicants" → 200); raw label always kept |
salary | Typed {min, max, currency, period, raw} from the company-provided pay range — null when the posting has none (most don't) |
seniority, employmentType, jobFunction, industries | LinkedIn's job-criteria block (with includeDescription) |
benefits | Card badges such as "Actively Hiring" / "Be an early applicant" |
descriptionText | Full plain-text job description (with includeDescription) |
workplaceType, promoted | Not observable anonymously — always null, marked unknown in coverage (see Known limitations) |
isNew, watchlistId | Present in alert mode; every pushed item is new by construction |
searchKeywords, searchLocation | The search combo that surfaced this job |
coverage | Per-field-group status: obtained / absent / unknown |
capture | Provenance: timestamp, source URL(s), HTTP status, SHA-256 content hash |
Known limitations
We'd rather you know these up front:
- LinkedIn currently ignores the
workplaceType,jobType, andexperienceLevelsearch filters. Verified live on 2026-08-20: the guest search returns byte-identical result sets with and withoutf_WT/f_JT/f_E. We still forward them (LinkedIn may re-enable them) and the actor warns in the run log when you set them — but they do not filter results. The reliable path: enableincludeDescriptionand filter client-side on the parsedemploymentTypeandseniorityfields. OnlydatePostedverifiably filters results today. - No structured workplace type or promoted/sponsored flag exists in LinkedIn's anonymous markup. Rather than guessing from description text, both fields are
nullwith coverageunknown. Any competitor showing these fields anonymously is inferring them. - Salary is rare and company-provided. Only postings with a company-published pay range carry
salary; in our live verification roughly a minority of US postings and almost no EU postings had one.nullmeans the posting shows no salary, not "unknown salary". postedAtis date-granularity. LinkedIn's guest cards expose an ISO date, not a timestamp;postedAtTextkeeps the raw relative label.- Result cap ~1,000 per search. LinkedIn's guest search stops paginating after about 100 pages (10 cards each); very broad queries ("manager", "United States") hit that wall. Narrow with keywords, locations, and
datePosted— or use alert mode, where the daily delta is what matters. - No login-only data. Job-poster profiles, "Easy Apply" internals, and similar login-gated datapoints are out of scope by design — this actor never uses cookies or accounts.
Tips and advanced options
- Start narrow, then fan out. Prefer several specific keyword×location combos over one broad query — you get better relevance and stay far from LinkedIn's ~1,000-result ceiling.
- Use
datePosted: "past24h"with a daily schedule for the tightest alert feed; usepastWeekif you want overlap safety (the watchlist dedupes it away anyway). - Alert mode baseline: the first run of a new
watchlistIdreports everything currently matching. KeepmaxJobsmodest for that first run, then let the schedule deliver deltas. - Multiple independent alerts = multiple schedules with different
watchlistIds — each keeps its own history in its own store (job-watchlist-<id>). - Proxy fallback chain. Searches are attempted in tiers: your own
proxiesByCountrypool first, then the bundled geo pool (we cover it), then Apify datacenter, then Apify residential. Retries only happen on bot-detection/transport failures; blocked combos surface as uncharged transparency items. - Automate it. Schedules, webhooks, and integrations (Slack, Google Sheets, Make/Zapier) are all available on the Apify platform — alert mode plus a schedule is the intended sweet spot.
FAQ
Do I need a LinkedIn account or cookies?
No. The actor uses only LinkedIn's public guest endpoints — no login, no cookies, no account, ever. That also means there is no account-ban risk on your side, unlike cookie-based scrapers.
Will LinkedIn block the scraper?
LinkedIn rate-limits anonymous traffic, which is why each request goes through a tiered proxy fallback chain with a browser TLS fingerprint, and retries happen only on genuine bot-detection. Blocked searches surface as transparency items with error / errorDescription and are never charged.
How does alert mode differ from just scheduling any job scraper?
A plain scheduled scraper re-delivers the same postings every day and leaves deduplication to you. Alert mode keeps a persistent cross-run history per watchlistId: a job is delivered exactly once, on the first run that sees it. Your dataset becomes a true delta feed — and you only pay the job-new event for genuinely new postings.
Why are workplaceType and promoted always null?
Because LinkedIn's anonymous pages verifiably do not contain them (checked 2026-08-20). We report honest null + coverage unknown instead of inventing values — that is the design, not a bug.
Can I bring my own proxies?
Yes. Add endpoints via proxyConfiguration.proxyUrls, or pin geo-located proxies per country with proxiesByCountry (e.g. German exits for locations: ["Berlin"]) — your pool is always tried first.
Is scraping LinkedIn jobs legal?
Web scraping is legal when you extract publicly available data that is not behind a login. This actor scrapes only anonymous, public job-search pages. Job postings are company-published data, but postings can reference individuals (e.g. in description text) — personal data is protected by regulations such as GDPR in the EU, and you should have a legitimate purpose and legal basis for processing it. If you are unsure, consult a lawyer, and review LinkedIn's Terms of Service before scraping. Apify is not liable for how you use the extracted data.
Something isn't working — where do I report it?
Open the Issues tab on the actor's page in Apify Console and describe the problem, ideally with your input JSON and the run ID. Error items in the dataset (error / errorDescription) already tell you what went wrong for individual searches.
Can you build a custom LinkedIn data solution?
Yes — if you need different fields, company or profile data, or a fully managed pipeline, reach out via the Issues tab and ask about a custom solution.