LinkedIn Jobs Scraper avatar

LinkedIn Jobs Scraper

Pricing

from $1.50 / 1,000 job scrapeds

Go to Apify Store
LinkedIn Jobs Scraper

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

Torchtechnology LTD

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

12 days ago

Last modified

Share

What does LinkedIn Jobs Scraper do?

LinkedIn Jobs Scraper extracts typed, provenance-stamped job postings from LinkedIn Jobsanonymously, 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.

CapabilityLinkedIn 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:

  1. collects the current search results,
  2. diffs them against the watchlist history,
  3. pushes only the jobs never seen before (charged as job-new),
  4. 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:

  1. Run the actor once with alertMode: true and a watchlistId (e.g. "backend-berlin"). The first run reports everything currently matching — that is your baseline.
  2. In Apify Console, open Schedules → Create new, pick this actor, keep the same input (same watchlistId), and set a cron like 0 7 * * * (daily at 07:00).
  3. 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 runStandard modeAlert 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?

AudienceWhat they use it for
Job seekers & career platformsScheduled job alerts via alert mode — new matching postings as a clean delta feed, no duplicates, ever.
Recruiters & staffing agenciesMarket mapping: who is hiring what, where, at which seniority — with typed salary benchmarks where published.
HR tech & ATS buildersA stable, typed ingestion feed (ISO dates, integer counts, structured salary) with provenance per item.
Labor-market researchersTime-series of postings per keyword/location; the watchlist store doubles as a seen-set for longitudinal studies.
Sales & lead-gen teamsHiring signals — companies posting relevant roles right now (datePosted: past24h, daily schedule).

How to scrape LinkedIn jobs with LinkedIn Jobs Scraper

  1. Open the actor in Apify Store and click Try for free.
  2. On the Input tab, set Keywords (e.g. ["software engineer", "product manager"]) and/or Locations (e.g. ["Berlin", "United States"]).
  3. Optionally set Date posted (past24h / pastWeek / pastMonth) and enable Include description & details for full text, salary, applicants, and seniority per job.
  4. For a recurring alert, enable Alert mode, set a Watchlist ID, and attach an Apify Schedule (see above).
  5. 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 ApifyClient
client = 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.

FieldTypeDescription
keywordsarraySearch keywords (job titles, skills, companies). Default ["software engineer"].
locationsarrayFree-text locations as on linkedin.com/jobs (e.g. "Berlin", "United States"). Defaults to the country name when empty.
countrystringISO alpha-2 code (US, DE, …). Default location + geo-pinned proxy exit. Default "US".
datePostedstringany (default), past24h, pastWeek, pastMonth. The one filter LinkedIn's guest search verifiably honors.
workplaceTypestringon-site / remote / hybrid. Currently ignored by LinkedIn — see Known limitations.
jobTypestringfull-time / part-time / contract / … Currently ignored by LinkedIn — use includeDescription and filter on the parsed employmentType.
experienceLevelstringentry / associate / mid-senior / … Currently ignored by LinkedIn — filter on the parsed seniority.
maxJobsintegerGlobal cap per run, deduplicated by job ID. Default 10, max 1000.
includeDescriptionbooleanFetch the detail page per job: descriptionText, applicantsCount, salary, seniority, employmentType, jobFunction, industries. Default false.
alertModebooleanPush only jobs never reported before for watchlistId (charged as job-new). Default false.
watchlistIdstringPersistent history identifier for alert mode. Default "default".
proxyConfigurationobjectOptional — bundled geo proxies are included; enable only to force your own or Apify proxies.
proxiesByCountryobjectOptional 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

FieldDescription
jobId, jobUrlLinkedIn job ID and canonical, non-localized job URL (stable for dedupe)
title, company, companyUrl, companyLogoPosting and company identity
locationLocation as displayed on the posting
postedAt, postedAtTextISO date from LinkedIn's datetime attribute, plus the raw relative text ("2 weeks ago")
applicantsCount, applicantsTextInteger applicant count where LinkedIn shows one ("Over 200 applicants" → 200); raw label always kept
salaryTyped {min, max, currency, period, raw} from the company-provided pay range — null when the posting has none (most don't)
seniority, employmentType, jobFunction, industriesLinkedIn's job-criteria block (with includeDescription)
benefitsCard badges such as "Actively Hiring" / "Be an early applicant"
descriptionTextFull plain-text job description (with includeDescription)
workplaceType, promotedNot observable anonymously — always null, marked unknown in coverage (see Known limitations)
isNew, watchlistIdPresent in alert mode; every pushed item is new by construction
searchKeywords, searchLocationThe search combo that surfaced this job
coveragePer-field-group status: obtained / absent / unknown
captureProvenance: 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, and experienceLevel search filters. Verified live on 2026-08-20: the guest search returns byte-identical result sets with and without f_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: enable includeDescription and filter client-side on the parsed employmentType and seniority fields. Only datePosted verifiably 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 null with coverage unknown. 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. null means the posting shows no salary, not "unknown salary".
  • postedAt is date-granularity. LinkedIn's guest cards expose an ISO date, not a timestamp; postedAtText keeps 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; use pastWeek if you want overlap safety (the watchlist dedupes it away anyway).
  • Alert mode baseline: the first run of a new watchlistId reports everything currently matching. Keep maxJobs modest 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 proxiesByCountry pool 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.

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.