LinkedIn Search Jobs Scraper With Market Analytics
Pricing
from $3.99 / 1,000 results
LinkedIn Search Jobs Scraper With Market Analytics
Scrapes LinkedIn job search results by keyword, location, or filters, capturing job titles, companies, descriptions, seniority, locations, post dates, and job URLs. Ideal for market research, talent analysis, recruitment automation, and large-scale job data extraction
Pricing
from $3.99 / 1,000 results
Rating
0.0
(0)
Developer
Scrapio
Maintained by CommunityActor stats
0
Bookmarked
6
Total users
0
Monthly active users
3 days ago
Last modified
Categories
Share
LinkedIn Jobs Scraper โ Extract Listings, Salaries and Market Data
LinkedIn Search Jobs Scraper With Market Analytics reads LinkedIn's public job search and job-posting pages and returns typed JSON โ ready for a spreadsheet, a database, or an LLM context window without any HTML parsing. Every job comes back with title, company, location, and description, plus derived fields LinkedIn never publishes directly: posting age, days until expiry, applicant velocity, and an annualized salary midpoint. Every search query also gets its own market-snapshot row โ total results, remote/hybrid/on-site demand, seniority mix, and the salary range actually observed in that run. The guide below covers every field and how teams deploy it.
๐งญ What Does LinkedIn Search Jobs Scraper With Market Analytics Do?
LinkedIn Search Jobs Scraper With Market Analytics is a LinkedIn jobs scraper that turns a keyword, a location, a company page, or a job URL into two kinds of rows: individual job listings and a per-query market-snapshot summary. It reads LinkedIn's public, no-login job search and job-posting pages directly โ no LinkedIn account, cookie, or API key is used anywhere in the Actor. Every job listing is enriched with derived hiring-market fields computed from LinkedIn's own structured posting data, and every query gets one aggregate row describing the hiring demand behind it.
- ๐ Searches by keyword,
keyword, locationpairs, a company profile URL, a full job-search URL, or a single job URL/ID - ๐ผ Returns core job identity: title, company, location, description, and structured
criteria(seniority, employment type, job function, industries) - ๐ Computes per-job analytics: posting age, days until expiry, applicant velocity, annualized salary midpoint
- ๐งฎ Computes a per-query market snapshot: total results, remote/hybrid/on-site demand, seniority distribution, observed salary range
- ๐ข Resolves company profile URLs to LinkedIn's numeric organization ID before searching, so company-scoped searches actually filter to that company
- ๐พ Writes every row to the dataset the moment it's collected (live saving)
- ๐ Works with or without Apify Proxy โ off by default
โก Features & Capabilities
The Actor's own features split into two layers: the base LinkedIn job-search extraction, and the market-analytics engine that's this variant's headline addition.
Core features
- ๐ Flexible search input โ keywords,
keyword, locationpairs, company profile URLs, full job-search URLs, or a single job URL/ID, all accepted through onestartUrlsfield - ๐ผ Full job identity per listing โ
title,company,location,description,postedTimeAgo,numberOfApplicants,link, and a nestedjobobject with its ownid/link/title/company/location - โ
Structured criteria โ a
criteriaarray with LinkedIn's own seniority level, employment type, job function, and industries labels for each listing - ๐ Per-job derived analytics โ
postingAgeDays,daysUntilExpiry,applicantsCount(normalized from LinkedIn's free-text applicant count),applicantVelocity, and salary fields (salaryCurrency,salaryMinAnnualized,salaryMaxAnnualized,salaryUnitText,salaryMidpointAnnualized) computed from the listing's own structured JobPosting data - ๐งฎ Per-query market snapshot โ one
marketSnapshotrow per query withtotalResultsForQuery,workplaceDemandRemote/Hybrid/Onsite,seniorityDistribution,jobsAnalyzed,jobsWithSalaryCount, andobservedSalaryMinAnnualized/MaxAnnualized - โ๏ธ Sort by relevance or recency via
sortOrder - ๐งฐ Optional toggles โ turn
includeJobAnalyticsorincludeMarketSnapshotoff for faster, cheaper runs when you only need raw listings
When another tool might suit you better
If all you need is a fast list of job titles and links โ no salary math, no expiry countdown, no market-snapshot row โ turning includeJobAnalytics and includeMarketSnapshot off already gets you a faster, cheaper run; but at that point a simpler raw-listings scraper without an analytics engine may genuinely be the better fit. Likewise, this Actor won't help if you need logged-in LinkedIn Recruiter data โ candidate search, InMail credits, applicant profiles โ because it only reads LinkedIn's public, no-login job pages, by design.
LinkedIn Search Jobs Scraper With Market Analytics within the Scrapio data stack
This Actor covers LinkedIn job listings and hiring-market analytics. For LinkedIn activity from people rather than jobs โ posts, engagement rate, posting cadence โ use Scrapio's LinkedIn Profile Posts Scraper With Engagement Analytics, which scrapes a profile's own posts and computes engagementRate, postAgeDays, likesPerDay, and engagementRank per post. Both Actors return typed JSON to the same kind of Apify dataset, so a workforce-intelligence pipeline can combine job-market signals from this Actor with posting activity from the profile-posts scraper.
Why do developers and data teams scrape LinkedIn jobs?
๐ข Recruiters & Talent Acquisition Teams
Recruiters and TA teams feed startUrls with the role and location they're hiring for or already competing against, then read applicantVelocity and daysUntilExpiry straight off each job row to see which competing postings are filling fast and which are about to expire. salaryMidpointAnnualized gives a same-basis number to benchmark an offer against, even when the original posting listed an hourly or monthly rate โ the Actor annualizes it automatically. The market-snapshot row's seniorityDistribution shows whether a market is stacked with entry-level or senior openings before a requisition is even opened, and workplaceDemandRemote/Hybrid/Onsite tells a TA team whether a location expects remote flexibility before the job ad is written. Results land in the dataset the moment each job is scraped, so a sourcing team can start working a list before the run finishes.
๐ AI training data and RAG indexing
description and the criteria array (seniority level, employment type, job function, industries) are the highest-information text and structured fields on a job row โ description is unstructured free text that indexes well for RAG retrieval over "what does this role actually involve", while criteria gives a small, consistently-shaped taxonomy to filter or facet on without any NLP. Two use cases follow: RAG enrichment, where a hiring assistant retrieves description chunks alongside the numeric salaryMidpointAnnualized and postingAgeDays fields to ground an answer in a specific, current listing rather than a stale training-time snapshot; and training-data construction, where salaryCurrency, salaryMinAnnualized, salaryMaxAnnualized, and applicantVelocity provide numeric labels for a compensation or demand-prediction model. Every field returns as a typed primitive โ no markup to strip before it reaches a model's context window.
๐ฑ Competitive and market intelligence
Scope startUrls to a competitor's company profile URL and the Actor resolves it to LinkedIn's numeric organization ID before searching, so the results are that company's own openings โ not an unfiltered firehose of unrelated jobs. Track totalResultsForQueryNumeric and jobsAnalyzed run over run to see whether a competitor is scaling a team up or down, and seniorityDistribution to see whether they're hiring senior or junior. Run the same query on a schedule and diff the market-snapshot row's observedSalaryMinAnnualized/observedSalaryMaxAnnualized to catch a pay-range shift before it's public news.
๐ฌ Research and academic use
Labor-market researchers can build a query set across roles, industries, and locations and let each query's market-snapshot row stand in as a lightweight demand and pay-range observation for that segment โ totalResultsForQuery, workplaceDemandRemote/Hybrid/Onsite, and observedSalaryMinAnnualized/MaxAnnualized are exactly the aggregate figures an academic hiring-trends dataset needs. This Actor only reads LinkedIn's own public, no-login job search pages โ no private or authenticated data is collected, which keeps a resulting dataset within public-data research scope.
๐ฅ Product and SaaS development
Teams building a compensation-benchmarking tool, a job-board aggregator, or an HR-tech enrichment API can run this Actor behind their own product and re-serve salaryMidpointAnnualized, applicantVelocity, and the market-snapshot fields as their own computed metric, instead of building a LinkedIn parser and an annualization function from scratch. Because job and market-snapshot rows share one dataset schema distinguished by type, a product can ingest both with one parser and route them to different tables downstream.
๐ Input Parameters
All input is read directly from .actor/actor.json's input schema. startUrls is the only required field; everything else has a default.
| Parameter | Required | Type | Description | Example Value |
|---|---|---|---|---|
startUrls | Yes | array of strings | Add LinkedIn job search queries: keywords, keywords, location pairs, company profile URLs, full job-search URLs, or single job URLs/IDs. One item per line โ each query gets its own market-snapshot row. | ["data analyst, United States"] |
sortOrder | No | string, enum: relevant, recent | Choose how to sort results: most relevant or most recent. Default: relevant. | "relevant" |
maxJobs | No | integer, min 1, max 10000 | Maximum number of jobs to analyze per query. This is also the sample size used to compute that query's market-snapshot statistics. Default: 10. | 50 |
includeJobAnalytics | No | boolean | When enabled, resolves each job's real posting/expiry dates and structured salary to compute postingAgeDays, daysUntilExpiry, applicantVelocity, and salaryMidpointAnnualized. Adds 2 extra page fetches per job. Default: true. | true |
includeMarketSnapshot | No | boolean | When enabled, adds one aggregate marketSnapshot row per query with total results, remote/hybrid/onsite demand, seniority distribution, and the observed salary range across that query's scraped jobs. Default: true. | true |
proxyConfiguration | No | object (Apify proxy config) | Optional: enable Apify Proxy if requests get blocked. Off by default (direct requests). | { "useApifyProxy": false } |
Example JSON input:
{"startUrls": ["data analyst, United States","https://www.linkedin.com/company/openai/","4333046374"],"sortOrder": "relevant","maxJobs": 50,"includeJobAnalytics": true,"includeMarketSnapshot": true,"proxyConfiguration": { "useApifyProxy": false }}
Supported URL types and input formats
Each line in startUrls is normalized independently and can be any of:
- Keyword or
keywords, locationpair โ"data analyst, United States"or"data analyst | data scientist, Remote"(pipe-separated keywords before the comma). Runs a normal job search and gets its own market-snapshot row. - Company profile URL โ
"https://www.linkedin.com/company/openai/". The Actor resolves the slug to LinkedIn's numeric organization ID first, then searches with that ID as thef_Cfilter โ a slug-based filter alone is silently ignored by LinkedIn and returns the entire unfiltered job firehose instead. - Full job-search URL โ any
https://www.linkedin.com/jobs/search/?...URL is used as-is, with the requestedsortOrderappended to its query string. - Single job URL or numeric job ID โ
"https://www.linkedin.com/jobs/view/4333046374"or bare"4333046374". This skips search entirely and returns just that one job โ there is no market-snapshot row for a single-job input, since no search page was ever run.
๐ฆ Output Format
Every row โ job or market snapshot โ is written to the same Apify dataset the moment it's collected, and both row types share one schema distinguished by the type field ("job" or "marketSnapshot"). Fields LinkedIn doesn't publish for a given listing (e.g. validThroughISO, the salary fields) return as null rather than being guessed or omitted.
Output for a job listing
{"id": "4333046374","type": "job","query": "data analyst, United States","title": "Data Analyst","company": "Example Corp","location": "Austin, TX","postedTimeAgo": "3 days ago","numberOfApplicants": "Over 100 applicants","description": "We are looking for a Data Analyst to join our growing analytics team...","criteria": [{ "title": "Seniority level", "value": "Mid-Senior level" },{ "title": "Employment type", "value": "Full-time" },{ "title": "Job function", "value": "Information Technology" },{ "title": "Industries", "value": "IT Services and IT Consulting" }],"link": "https://www.linkedin.com/company/example-corp","job": {"id": "4333046374","link": "https://www.linkedin.com/jobs/view/data-analyst-at-example-corp-4333046374","title": "Data Analyst","company": "Example Corp","location": "Austin, TX"},"scrapedAt": "2026-08-04T09:12:00Z","applicantsCount": 100,"applicantsCountApprox": true,"datePostedISO": "2026-08-01T00:00:00Z","validThroughISO": "2026-08-31T00:00:00Z","postingAgeDays": 3,"daysUntilExpiry": 27,"salaryCurrency": "USD","salaryMinAnnualized": 85000,"salaryMaxAnnualized": 110000,"salaryUnitText": "YEAR","salaryMidpointAnnualized": 97500,"applicantVelocity": 33.333}
Output for a market-snapshot row
{"id": "snapshot-abcd1234-1","type": "marketSnapshot","query": "data analyst, United States","totalResultsForQuery": "63,000+","totalResultsForQueryNumeric": 63000,"workplaceDemandRemote": 8500,"workplaceDemandHybrid": 12400,"workplaceDemandOnsite": 41000,"seniorityDistribution": [{ "level": "Mid-Senior level", "count": 6 },{ "level": "Entry level", "count": 4 }],"jobsAnalyzed": 10,"jobsWithSalaryCount": 3,"observedSalaryCurrency": "USD","observedSalaryMinAnnualized": 62000,"observedSalaryMaxAnnualized": 145000,"scrapedAt": "2026-08-04T09:12:05Z"}
Schema stability and export options
Field names stay stable across runs; job rows and market-snapshot rows share one dataset and are told apart by type. Because the salary and date fields are read from LinkedIn's own structured JobPosting markup rather than scraped from visual layout, they're resilient to LinkedIn's front-end redesigns in a way that CSS-selector-based fields (like postedTimeAgo's HTML source) are not. Export options are Apify's own: JSON, CSV, or Excel from the Console's Export button, or streamed via the Apify API / apify-client โ the Actor itself adds no separate export mechanism.
One billing detail worth knowing: job rows are pushed through a charged pay-per-result event; the per-query marketSnapshot row is pushed as a plain dataset write with no charged event attached to it, so the aggregate hiring-demand row comes along with the run rather than as a separate line item.
๐ก LinkedIn Search Jobs Scraper With Market Analytics Strategy Guide
๐ฏ Strategy 1: Real-time enrichment pipeline
Trigger a run per inbound record โ a new lead, a company added to your CRM, a role opening in your ATS โ using that company's LinkedIn profile URL or a keyword, location pair as startUrls. The Actor resolves the company to its numeric organization ID automatically, so the search only returns that company's own openings. Read jobsAnalyzed, workplaceDemandRemote/Hybrid/Onsite, and observedSalaryMinAnnualized/MaxAnnualized from the market-snapshot row and append them to the CRM record as a live hiring-activity and pay-range signal, without anyone touching the Apify Console.
๐ฏ Strategy 2: Scheduled monitoring and alerting
Save the same startUrls query as an Apify Schedule (daily or weekly). Each run pushes a new marketSnapshot row for that query โ diff totalResultsForQueryNumeric, workplaceDemandRemote, and observedSalaryMinAnnualized/MaxAnnualized against the previous run's row for the same query value. A jump in totalResultsForQueryNumeric signals a hiring push; a widening salary band signals a pay-range shift. Apify Schedules trigger the run itself; there's no webhook built into the Actor's own code, so pull the diff logic into whatever reads the dataset after each scheduled run, or attach an Apify webhook on run completion.
๐ฏ Strategy 3: Bulk dataset build
Put one keyword, location pair per line in startUrls โ each gets its own job rows and its own market-snapshot row, processed sequentially within the run. Job-detail pages are fetched with an internal concurrency of 3 (not configurable via input), and search pagination stops after 40 pages of 25 results, so a single query tops out around 1,000 discovered job IDs regardless of how high maxJobs is set. For a large keyword ร location matrix, split the list across several parallel Actor runs instead of one long startUrls list, then aggregate the resulting datasets to CSV or a database.
Strategy comparison at a glance
| Strategy | Best for | Run pattern | Output format |
|---|---|---|---|
| Real-time enrichment | CRM/ATS records needing a live hiring signal | On-demand run per record | Job + market-snapshot rows via Apify API |
| Scheduled monitoring | Tracking demand/salary drift over time | Apify Schedule, recurring | Market-snapshot rows, diffed run over run |
| Bulk dataset build | Research or training datasets across many queries | Multiple parallel runs | Dataset export to CSV/JSON |
๐ด Related LinkedIn Scrapers & Tools
| Scraper | What it extracts |
|---|---|
| LinkedIn Profile Posts Scraper With Engagement Analytics (Scrapio) | LinkedIn profile posts with engagement rate, post age, like velocity, and posting cadence โ the people side of LinkedIn, rather than job listings |
Both Actors are part of Scrapio's LinkedIn coverage: this one handles job listings and hiring-market analytics; pair it with the profile-posts scraper for recruiter or employee posting activity on the same accounts.
How to integrate LinkedIn Search Jobs Scraper With Market Analytics with your stack
LinkedIn Search Jobs Scraper With Market Analytics works with any language or tool that can call the Apify API โ most commonly through the official apify-client SDKs.
Python
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_API_TOKEN>")run_input = {"startUrls": ["data analyst, United States", "data analyst, Canada"],"sortOrder": "relevant","maxJobs": 50,"includeJobAnalytics": True,"includeMarketSnapshot": True,}run = client.actor("YOUR_USERNAME/linkedin-search-jobs-scraper-with-market-analytics").call(run_input=run_input)jobs, snapshots = [], []for item in client.dataset(run["defaultDatasetId"]).iterate_items():(jobs if item["type"] == "job" else snapshots).append(item)print(f"Collected {len(jobs)} jobs and {len(snapshots)} market snapshots")import csvwith open("jobs.csv", "w", newline="", encoding="utf-8") as f:writer = csv.DictWriter(f, fieldnames=jobs[0].keys())writer.writeheader()writer.writerows(jobs)
Node.js
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_API_TOKEN>' });const run = await client.actor('YOUR_USERNAME/linkedin-search-jobs-scraper-with-market-analytics').call({startUrls: ['data analyst, United States'],sortOrder: 'relevant',maxJobs: 50,includeJobAnalytics: true,includeMarketSnapshot: true,});const { items } = await client.dataset(run.defaultDatasetId).listItems();const jobs = items.filter((item) => item.type === 'job');const snapshots = items.filter((item) => item.type === 'marketSnapshot');console.log(`Collected ${jobs.length} jobs and ${snapshots.length} market snapshots`);
Async and scheduled pipelines
This Actor doesn't implement its own webhook or polling endpoint โ async delivery goes through Apify's own run surface. For fire-and-forget large jobs, start the run with .start(run_input=...) instead of .call(), then either poll the run's status via client.run(run_id).get() or attach an Apify webhook (Console or API) that fires when the run reaches SUCCEEDED, FAILED, or ABORTED. For recurring queries, use an Apify Schedule rather than polling from your own infrastructure.
๐ฏ Who Needs LinkedIn Search Jobs Scraper With Market Analytics? (Use Cases & Industries)
๐ข Recruiters & Talent Acquisition Teams
A TA lead searches a target role and location, then sorts the resulting jobs by daysUntilExpiry to see which competing postings are about to lapse โ a signal that those roles may reopen or that a competitor is struggling to fill them.
๐ People Analytics & Compensation Teams
A compensation analyst runs the same keyword, location query for several regions and compares each market-snapshot row's observedSalaryMinAnnualized/MaxAnnualized to build a same-basis pay-range comparison, since every salary is annualized regardless of the original pay period.
๐ฑ Competitive Intelligence Analysts
An analyst scopes a query to a competitor's company profile URL and tracks jobsAnalyzed and seniorityDistribution run over run to see whether that company is scaling its engineering or sales org, without manually browsing its careers page.
๐ฌ Researchers
Labor-market and workforce researchers assemble a query set across roles and geographies and use each market-snapshot row's totalResultsForQuery and workplace-demand fields as public-data inputs to a hiring-trends dataset.
๐ฅ HR-Tech & SaaS Builders
Teams building a compensation-benchmarking or job-board product run this Actor behind their own service and re-serve salaryMidpointAnnualized and the market-snapshot fields as their own computed metric, instead of building a LinkedIn parser from scratch.
Is it legal to scrape LinkedIn jobs?
Yes โ scraping LinkedIn's publicly accessible job listings is legal in the United States; the Ninth Circuit held in hiQ Labs, Inc. v. LinkedIn Corp., 938 F.3d 985 (9th Cir. 2019), that scraping data LinkedIn makes publicly viewable does not violate the Computer Fraud and Abuse Act. That ruling addresses unauthorized-access law, not LinkedIn's own Terms of Service โ scraping LinkedIn still breaches LinkedIn's User Agreement, which is a civil contract matter between the scraper and LinkedIn, not a criminal one. This Actor returns job listings and business/hiring data โ job title, company, location, description, and derived pay and demand analytics โ not personal data about identifiable individuals, so data-protection regimes like GDPR generally do not attach to its output the way they would for a scraper of personal profiles. LinkedIn Search Jobs Scraper With Market Analytics returns only publicly accessible data. What you do with that data is your responsibility โ consult legal counsel for commercial applications.
โ Frequently asked questions
Does LinkedIn Search Jobs Scraper With Market Analytics work without a LinkedIn account?
Yes. The Actor only calls LinkedIn's public, no-login job search and job-posting pages (including the jobs-guest API endpoint) โ no LinkedIn account, session cookie, or API key is used anywhere in its code.
How does it handle LinkedIn's anti-scraping measures?
It retries failed requests up to 3 times with exponential backoff and jitter, specifically watching for HTTP 429 (rate limit) and 999 (LinkedIn's own block status) responses. Requests carry realistic browser headers, the run opens with a warm-up request to LinkedIn's homepage, and random delays are inserted between pages, between job-detail fetches, and between separate startUrls inputs. Apify Proxy is supported but off by default.
Can I run it at scale without getting blocked?
The Actor scrapes job-detail pages with an internal concurrency of 3 and a connection pool capped at 10 total / 3 per host โ both hard-coded, not exposed as input parameters. No uptime or block-rate figure is published for this Actor; running many parallel Actor runs increases the chance of hitting LinkedIn's rate limiting, and enabling Apify Proxy (proxyConfiguration) is the documented mitigation if that happens.
How fresh is the data?
Fully live. Every run fetches LinkedIn's search and job pages fresh over HTTP โ nothing is cached or reused from a previous run.
Which fields work best for AI training and RAG indexing?
For RAG, index description โ it's the highest-information free-text field on a job row. For training data, use the fields with the most consistent structure across records: criteria (seniority level, employment type, job function, industries), salaryMidpointAnnualized, postingAgeDays, and applicantVelocity. All of these return as typed primitives or small typed arrays, so no additional normalization is needed before use.
Does LinkedIn Search Jobs Scraper With Market Analytics work with Claude, ChatGPT, and other AI agent tools?
It's callable as an HTTP endpoint by any agent framework through the Apify API โ start a run, then read the dataset via apify-client or a direct API call. Every response is typed JSON, so an agent can consume it directly without parsing HTML first.
What's the difference between a job row and a market-snapshot row?
Both live in the same dataset and are distinguished by the type field. type: "job" rows carry one listing each (title, company, salary, analytics). type: "marketSnapshot" rows carry one aggregate per query โ total results, workplace-type demand, seniority distribution, and the observed salary range across that query's own scraped jobs. Filter on type to separate them.
What happens if I input a company profile URL instead of keywords?
The Actor extracts the company slug from the URL and resolves it to LinkedIn's numeric organization ID before searching, because LinkedIn's own company-scoped job filter (f_C) silently ignores a slug and returns the entire unfiltered job firehose if given one directly. If the ID can't be resolved, the Actor falls back to a plain keyword search on the company name instead of failing the query.
How does it compare to a plain LinkedIn job-listing scraper?
A basic listings scraper hands back title, company, location, and a link โ and stops there. This Actor returns the same core listing fields plus the analytics layer computed from each job's own structured posting data (posting age, expiry countdown, applicant velocity, annualized salary) and a per-query market-snapshot aggregate. If you don't need that analytics layer, includeJobAnalytics and includeMarketSnapshot can both be turned off for a faster, cheaper run that behaves like a plain listings scraper.
โน๏ธ Disclaimer
LinkedIn Search Jobs Scraper With Market Analytics extracts only publicly available data from LinkedIn's job search and job-posting pages. This tool is intended for lawful use cases only. Users are responsible for complying with LinkedIn's Terms of Service and applicable data protection laws in their jurisdiction.