LinkedIn Search Jobs Scraper By Multiple Keywords & Locations avatar

LinkedIn Search Jobs Scraper By Multiple Keywords & Locations

Pricing

Pay per usage

Go to Apify Store
LinkedIn Search Jobs Scraper By Multiple Keywords & Locations

LinkedIn Search Jobs Scraper By Multiple Keywords & Locations

The LinkedIn Search Jobs Scraper actor collects job listings from LinkedIn based on keywords, locations, and filters. It extracts titles, companies, locations, posting dates, and job URLs. Ideal for recruiters, analysts, and job platforms needing automated LinkedIn job market data collection.

Pricing

Pay per usage

Rating

0.0

(0)

Developer

Scraper Engine

Scraper Engine

Maintained by Community

Actor stats

1

Bookmarked

37

Total users

2

Monthly active users

3 days ago

Last modified

Share

LinkedIn Jobs Scraper — Titles, Companies and Applicants

Run a whole keyword × location matrix of LinkedIn job searches in a single job and get every listing back as structured JSON — job title, company, location, posting age, applicant counts, full description, and parsed criteria (seniority, employment type, job function, industries). No LinkedIn login is required. Every job is tagged with the exact keyword and location that surfaced it, duplicates found across different searches are collapsed automatically, and a per-run coverage summary tells you honestly how many jobs were requested versus delivered. Try a run on the Apify Console below.

What is LinkedIn Jobs Scraper By Multiple Keywords & Locations?

It's an Apify Actor that expands a keyword @ location1; location2 matrix into a full cross-product of individual LinkedIn job searches, runs them in one job, and returns deduplicated job listings as clean JSON. It reads LinkedIn's public guest job-search and job-detail pages only — no LinkedIn account or login is used anywhere in the code. It's built for recruiters and sales-intelligence teams tracking hiring activity across markets, market researchers, and developers or AI agents that need structured job data for many roles and regions at once.

What LinkedIn job data is publicly available to scrape?

LinkedIn's guest job-search and job-detail pages expose a job posting's core details — title, company, location, description, and hiring criteria — to any visitor, no sign-in required. What's gated sits behind LinkedIn's login and application flow.

Data CategoryPublicly Available (no login)Restricted (login / apply required)
Job title, company, location
Full job description
Posting criteria (seniority, employment type, job function, industries)
Applicant count, as LinkedIn displays it (e.g. "Over 200 applicants")
Time since posting
Identities of individual applicantsLogin + poster/recruiter access
Apply flow, hiring-manager contact detailsLogin + Easy Apply
Salary/compensation range❌ (not parsed by this Actor)

LinkedIn Jobs Scraper By Multiple Keywords & Locations only returns publicly visible data — what any visitor sees. Nothing behind a login wall.

What data can I extract with LinkedIn Jobs Scraper?

Every row is a single LinkedIn job listing, returned with these fields:

FieldDescription
idNumeric LinkedIn job posting ID
titleJob title
companyHiring company name
locationJob location text as shown on LinkedIn
descriptionFull job description text
criteriaArray of {title, value} pairs — seniority level, employment type, job function, industries, as listed on the posting
companyUrlURL to the hiring company's LinkedIn page
jobUrlCanonical LinkedIn URL for the job posting
postedTimeAgoFree-text posting age as shown on LinkedIn (e.g. "3 days ago")
numberOfApplicantsFree-text applicant count as shown on LinkedIn (e.g. "Over 200 applicants")
applicantsCountThe same applicant count parsed into a nullable number, or null if LinkedIn didn't display one
scrapedAtUTC timestamp of when this job was scraped
sourceKeywordThe keyword (or term1|term2 OR-group) that surfaced this job
sourceLocationThe location string that surfaced this job
sourceGeoIdLinkedIn's resolved numeric geoId for that location, if resolution succeeded
matchedTargetsArray of every {keyword, location} target that returned this job — how cross-target duplicates are traced

Two legacy aliases are kept for backward compatibility with the base actor's output shape and always mirror the values above: link (= companyUrl) and job.link (= jobUrl, nested).

Job identity and description fields

id, title, company, location, description, criteria, companyUrl, jobUrl (plus deprecated link / job.link).

Metric and timing fields

postedTimeAgo, numberOfApplicants, applicantsCount, scrapedAt.

Search-provenance fields

sourceKeyword, sourceLocation, sourceGeoId, matchedTargets — these exist because this Actor runs many searches in one job and needs to trace which job came from which target.

🤖 Add-on: Need additional LinkedIn data?

If you also need data from LinkedIn profile pages rather than job postings, LinkedIn Profile Post Scraper (also in this catalog) extracts posts from LinkedIn profile pages. Pair the two when you need both hiring activity and the people behind it.

Why not build this yourself?

Scraping LinkedIn's guest job surface directly means maintaining your own HTML selectors against markup that changes without notice, handling LinkedIn's 429 and 999 rate-limit responses with backoff, and reverse-engineering two non-obvious LinkedIn quirks this Actor already handles: resolving a free-text location to LinkedIn's numeric geoId (the text-only filter is far less precise), and resolving a linkedin.com/company/<slug> URL to its real numeric organization ID before filtering by f_C — LinkedIn silently ignores the slug form and returns the entire unfiltered job firehose instead of erroring. On top of that you'd need to build cross-target dedup and an honest requested-vs-delivered report yourself. This Actor does all of it out of the box, with retries, jitter, and optional residential proxy support already wired in.

How to use LinkedIn Jobs Scraper

No field is marked required in the input schema, but the run needs at least one usable entry in targets or startUrls — with neither, it exits immediately with "No usable targets found."

  1. Open the Actor on its Apify Store page and click Try for free (or run it from your own Console).
  2. Fill in targets with one or more keywords @ location1; location2 lines — this is the bulk keyword × location matrix.
  3. Optionally set sortOrder, maxJobs, maxTargets, and proxyConfiguration.
  4. Start the run.
  5. Download results as JSON, CSV, Excel, or any format the Apify dataset export supports, or stream them via the API as they're written.

How to scale to bulk job extraction

targets is the primary bulk mechanism: each line's keyword part is cross-product-expanded against every semicolon-separated location on that line, so a handful of lines can expand into dozens of searches in one run. startUrls adds legacy single-line inputs (search URLs, bare keywords, company URLs, keyword, location strings, or direct job URLs/IDs) as additional implicit targets alongside the matrix, for input lists carried over from the base actor. maxTargets caps the expanded total so a large matrix can't run away; 0 means no cap.

What can you do with LinkedIn job data?

  • 📊 Market researchers tracking hiring demand use sourceKeyword, sourceLocation, and postedTimeAgo to compare how many roles for a title are posted across regions and how fresh they are.
  • 🎯 Sales and demand-gen teams use company, companyUrl, and criteria (employment type, seniority) to flag companies actively hiring as a buying-intent signal.
  • 🤖 AI agents and job-alert bots use title, description, and applicantsCount to filter and rank newly posted jobs before notifying a candidate — the JSON is typed and ready to pass straight into an agent's context.
  • 🔬 People-ops and workforce analysts use criteria and numberOfApplicants across matchedTargets to gauge how competitive postings are for a given role and region.
  • 👩‍💻 Developers building an internal job board pull the structured JSON directly, deduped via matchedTargets, instead of re-scraping the same listing twice when it appears under two different searches.

How does LinkedIn Jobs Scraper handle rate limits and blocking?

Every request goes through a retry loop (3 attempts) with randomized delays and exponential backoff, and specifically recognizes LinkedIn's 429 (rate limit) and 999 (temporary block) responses to back off rather than fail outright. Requests use realistic browser headers, a warm-up request before the run starts, randomized delays between requests, and capped concurrency (a global connector limit and a 3-way semaphore for detail-page scraping) to avoid hammering LinkedIn. By default the Actor makes direct requests with no proxy — enabling proxyConfiguration (Apify Proxy, including residential) is optional and only recommended if you see requests getting blocked. If a job's detail page still fails to load after all retries, that single job is skipped and logged — it doesn't stop the run. If a location or company URL can't be resolved to LinkedIn's internal ID, that target is skipped rather than run unfiltered.

⬇️ Input

ParameterRequiredTypeDescriptionExample Value
targetsNoarrayOne line per target: 'keywords @ location1; location2'. Everything before @ is the keyword search ('term1|term2' for an OR search); everything after @ is one or more locations separated by ; — each location becomes its own search (cross-product). Each location is auto-resolved to LinkedIn's numeric geoId.["software engineer|backend developer @ United States; United Kingdom"]
startUrlsNoarrayBackward-compatible with the base actor's input: LinkedIn job search URLs, keywords, company URLs, structured 'keyword, location' strings, or job URLs/IDs — one item per line. Each item is treated as one additional implicit target alongside targets.[]
sortOrderNostring (enum: relevant, recent)Choose how to sort results within each target: most relevant or most recent. Default "relevant"."relevant"
maxJobsNointeger (min 1, max 10000)Maximum number of jobs to scrape for each target/search. ⚠️ LinkedIn's guest search surface has a real hard ceiling of about 1,000 jobs per target regardless of this value — the coverage summary reports maxJobsRequested vs maxJobsDelivered honestly if the ceiling is hit. Default 25.50
maxTargetsNointeger (min 0)Safety cap on how many expanded keyword × location targets (after cross-product) will actually run in one job. 0 = no cap. Default 0.0
proxyConfigurationNoobject (proxy editor)Optional: enable Apify Proxy if requests get blocked. By default the Actor runs with direct requests (no proxy). Proxies may consume extra credits. Default {"useApifyProxy": false}.{"useApifyProxy": false}

Example input

{
"targets": [
"software engineer|backend developer @ United States; United Kingdom",
"product manager @ United States"
],
"startUrls": [],
"sortOrder": "relevant",
"maxJobs": 50,
"maxTargets": 0,
"proxyConfiguration": { "useApifyProxy": false }
}

⬆️ Output

Each job is pushed to the default dataset the moment its detail page finishes scraping — typed, normalized JSON with a consistent schema across runs. Export it as JSON, CSV, Excel, or any other format the Apify platform supports, or stream it via the API.

Example output

{
"id": "3891234567",
"title": "Senior Backend Engineer",
"company": "Acme Robotics",
"location": "London, England, United Kingdom",
"postedTimeAgo": "3 days ago",
"numberOfApplicants": "Over 200 applicants",
"applicantsCount": 200,
"description": "We are looking for a Senior Backend Engineer to join our platform team...",
"criteria": [
{ "title": "Seniority level", "value": "Mid-Senior level" },
{ "title": "Employment type", "value": "Full-time" },
{ "title": "Job function", "value": "Engineering and Information Technology" },
{ "title": "Industries", "value": "Software Development" }
],
"companyUrl": "https://www.linkedin.com/company/acme-robotics",
"jobUrl": "https://www.linkedin.com/jobs/view/senior-backend-engineer-at-acme-robotics-3891234567",
"link": "https://www.linkedin.com/company/acme-robotics",
"job": { "link": "https://www.linkedin.com/jobs/view/senior-backend-engineer-at-acme-robotics-3891234567" },
"sourceKeyword": "software engineer|backend developer",
"sourceLocation": "United Kingdom",
"sourceGeoId": "101165590",
"matchedTargets": [
{ "keyword": "software engineer|backend developer", "location": "United Kingdom" }
],
"scrapedAt": "2026-08-04T10:15:32Z"
}

Alongside the main dataset, each run also writes one coverage summary row to a separate, run-specific dataset (coverage-summary-<runId>) with: type, targetsRequested, targetsResolved, totalUniqueJobs, duplicatesCollapsed, maxJobsRequested, maxJobsDelivered, perTargetCounts (an array of {target, sourceKeyword, sourceLocation, sourceGeoId, resolved, jobsFound} per target), and scrapedAt. This is what gives you an honest requested-vs-delivered figure instead of a run that silently under-delivers.

How does it work?

Requests go to LinkedIn's public guest job-search pages over plain HTTP, with realistic browser headers, randomized delays, and optional Apify Proxy — there's no headless browser or JavaScript rendering involved. The run has two phases: first, a discovery pass across every expanded target that collects job IDs and resolves each location's geoId and each company slug's numeric organization ID; second, a detail-scrape pass that fetches each unique job exactly once, even if it matched several different keyword/location targets, and tags it with all of them via matchedTargets. Only what LinkedIn already shows a signed-out visitor is returned. The output schema is defined by this Actor, not by LinkedIn's raw HTML, so it stays stable even if LinkedIn changes its page markup.

Integrations

LinkedIn Jobs Scraper runs on the Apify platform, so it works with everything already built around Apify's API.

Calling LinkedIn Jobs Scraper programmatically

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_API_TOKEN>")
run_input = {
"targets": ["product manager @ United States"],
"sortOrder": "relevant",
"maxJobs": 50,
}
run = client.actor("linkedin-search-jobs-scraper-by-multiple-keywords-and-locations").call(run_input=run_input)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item["title"], item["company"], item["location"])

Works in Go, Ruby, Node.js, cURL — any language that can make an HTTP request to the Apify API.

No-code tools (n8n, Make)

In n8n, use the Apify node (or an HTTP Request node against the Actor's run endpoint) to trigger a run and pull dataset items into your workflow. In Make, the Apify app module can start this Actor and pass its output straight into a downstream scenario, such as a CRM update or a Slack alert on new postings.

Scraping publicly available job-posting data is generally permitted — LinkedIn Jobs Scraper only returns what LinkedIn's guest job-search and job-detail pages already show to a visitor without logging in. A job listing is business/product data (a company's own posting about an open role), not personal data about an individual, so the framework that applies is LinkedIn's Terms of Service and database-rights law, not GDPR/CCPA-style personal-data rules. Note that some free-text fields, like description, may incidentally contain a hiring contact's name — treat any such text with the same care you'd apply to any scraped web content. Consult legal counsel if your use case involves bulk storage of personal data.

Frequently asked questions

What LinkedIn job fields does LinkedIn Jobs Scraper return?

The top fields are title, company, location, description, and criteria (seniority, employment type, job function, industries) — see What data can I extract above for the full field list.

Does LinkedIn Jobs Scraper require a LinkedIn account or login?

No. It reads only LinkedIn's public guest job-search and job-detail pages — no login, cookies, or session is used anywhere in the Actor.

How many job listings can I extract in one run?

Up to maxJobs (max 10,000) per target, across as many targets as your targets/startUrls matrix expands to (optionally capped by maxTargets). In practice, LinkedIn's guest search surface caps discovery at roughly 1,000 jobs per individual target regardless of a higher maxJobs value — the coverage-summary row reports maxJobsRequested vs maxJobsDelivered so you can see if that ceiling was hit.

What happens if a search target returns zero results?

That target's jobsFound is recorded as 0 in the coverage summary's perTargetCounts, and the run simply moves on to the next target — no error is raised. If every target returns zero jobs, the run finishes with an empty main dataset and a coverage-summary row showing totalUniqueJobs: 0.

Can I scrape multiple LinkedIn keyword/location combinations at once?

Yes — that's the Actor's core feature. targets accepts multiple keyword @ location1; location2 lines, cross-product-expanded into individual searches, and startUrls adds further legacy inputs on top.

Does LinkedIn Jobs Scraper work with Claude, ChatGPT, and other AI agent tools?

It's callable as a standard HTTP endpoint through the Apify API by any agent framework that can make a web request — there is no dedicated MCP server for this specific Actor.

How is this different from a plain LinkedIn Jobs Scraper?

This variant is built specifically for running many keyword/location combinations in one job: it expands a bulk matrix input into a cross-product of searches, auto-resolves each location's geoId, dedupes jobs found across different targets and tags each with matchedTargets, and reports maxJobsRequested vs maxJobsDelivered honestly instead of silently under-delivering when LinkedIn's guest-surface ceiling is hit. A single-keyword, single-location LinkedIn job search doesn't need this — this Actor is for running dozens of searches together and getting one clean, deduplicated dataset back.

Does LinkedIn Jobs Scraper return data in a format LLMs can use directly?

Yes. Every job comes back as typed, normalized JSON with consistent field names across runs — no HTML parsing or CSS selectors needed. Pass it straight to an LLM, index it into a vector store, or hand it to an agent tool.

What happens when LinkedIn changes its layout or anti-bot system?

The Actor is maintained and its output schema is designed to stay stable even when LinkedIn's page markup changes. No specific update turnaround time is promised.

Can I use LinkedIn Jobs Scraper without managing proxies or browser infrastructure?

Yes — there's no browser to manage since the Actor makes plain HTTP requests, and by default it runs without any proxy at all. If you do hit blocking, turning on proxyConfiguration routes requests through Apify Proxy without any infrastructure of your own.

Which fields work best for AI training data and RAG indexing?

For RAG, index description alongside title, company, and criteria as metadata for filtering. For structured training data, title, company, location, postedTimeAgo, and applicantsCount return as consistent typed primitives (strings and a nullable number) across every record.

ScraperWhat it extracts
LinkedIn Profile Post ScraperPosts published on LinkedIn profile pages

💬 Your feedback

Found a bug or missing a field? Use the Issues tab or the Contact developer button on this Actor's Apify Store page to let us know — we read every report.