LinkedIn Jobs Scraper — Job Postings, LinkedIn Salary
Pricing
$30.00 / 1,000 jobs
LinkedIn Jobs Scraper — Job Postings, LinkedIn Salary
Scrape LinkedIn jobs by keyword, location or company — no login, no cookies. Every posting returns the full description, salary, required skills, seniority and hiring-team contacts. Built for recruiters and sourcers. Export to CSV, JSON or Excel, run via API, or schedule runs.
Pricing
$30.00 / 1,000 jobs
Rating
0.0
(0)
Developer
Andrey Afanasenko
Maintained by CommunityActor stats
0
Bookmarked
72
Total users
15
Monthly active users
5 days ago
Last modified
Categories
Share
Search LinkedIn jobs by keyword, location, or company and enrich any job posting URL — full description, salary, skills, and hiring-team contacts. Built for recruiters, sourcers, and lead-gen teams. No login or cookies.
LinkedIn's own job APIs are partner-only — access is granted to hiring customers, for their own postings. This LinkedIn job scraper reads any public posting without a login or cookies and returns 46 columns per row: the full description, salary_display and structured salary_details, the required skills, the hiring_team contacts, seniority and job type, plus company size, industry and HQ.
🎯 What it does
You feed the Actor LinkedIn job criteria — keywords + location, a list of company URLs, or specific job-posting URLs — it returns clean, structured rows ready for a CRM, sheet, or downstream agent.
| Mode | Input | What it returns |
|---|---|---|
1: Search Jobs (searchJobs) | Keywords + LinkedIn geo code + optional date / type / level / workplace filters | 10-field slim job rows (25 per upstream call, paginated). Cost cap: maxItemsMode1. |
2: Fetch Job Details (fetchJobDetails) | LinkedIn job posting URLs (one per line) | 1 row per URL, 33 detail columns incl. full description, skills, salary, hiring team. Cost cap: maxItemsMode2. |
3: Search Company Jobs (searchCompanyJobs) | LinkedIn company URLs + optional keyword | Slim job rows for every open posting at the listed companies. Cost cap: maxItemsMode3. |
⚠️ Pick your mode first. All three modes have their own dedicated input section. Filling the wrong mode's fields is the #1 reason runs return zero rows. Always set "Operation Mode" before touching anything else.
What each column is actually for:
| You get | Why it matters |
|---|---|
hiring_team — the recruiter or manager on the posting, with their LinkedIn profile | Who to write to. This is the column that turns a job board into a lead list, and it is the one LinkedIn's own API will not give you for postings you don't own |
salary_display plus structured salary_details (min, max, period, currency) | Benchmark pay by role and city without parsing prose. The structured object is what you aggregate; the display string is what you show |
skills and job_functions | What the market is actually asking for, per role — countable and groupable, not free text you have to read |
applies and views | How contested the posting is. 3 applicants at 400 views is a different job from 300 at 400 |
experience_level, job_type, remote_allow | Closed values, so you can sort and filter instead of guessing from the title |
employee_count, employee_range, industries, company_description, the seven hq_* fields | Account scoring in the same row as the vacancy — no second lookup to decide whether this company is worth the outreach |
posted, closed, expired | Whether the role is still live. A stale posting wastes an outreach slot |
job_url, job_urn, company_linkedin_url | The keys you join on. A posting's title changes; its URN does not |
46 columns in all: 10 on every search row, 33 more on every enriched (Mode 2) row, plus
row_status / why_withheld and the input echo. Detail columns are null on search rows.
7 advanced filters — title contains / NOT contains, company name allow-list, location contains, minimum salary, posted-within-N-days, remote-only — applied across all modes.
📦 What you get — output sample
One row per LinkedIn job. Slim columns (top of the row) always populated; detail columns only filled by Mode 2 (Fetch Job Details) and emit null in slim rows.
{"input": "https://www.linkedin.com/jobs/view/4344628911","company": "Qualcomm","company_linkedin_url": "https://www.linkedin.com/company/qualcomm","company_logo": "https://media.licdn.com/dms/image/.../qualcomm_logo","job_title": "New Graduate - AI SW SDK Engineer","job_url": "https://www.linkedin.com/jobs/view/4344628911","job_urn": "4344628911","location": "Santa Clara, CA","posted_time": "2026-05-18 11:33:11","remote": "On-site","salary": "","applies": 0,"benefits": [],"closed": false,"company_description": "Qualcomm is a world leader in 3G, 4G and next-generation wireless technologies …","company_id": "1337","company_name": "Qualcomm","company_public_id": "qualcomm","employee_count": 38000,"employee_range": "10001+","experience_level": "Entry level","expired": false,"follower_count": 4600000,"hiring_team": [{ "name": "Jane Recruiter", "title": "University Talent Acquisition", "profile_url": "https://www.linkedin.com/in/jane-r" }],"hq_address_line1": "5775 Morehouse Drive","hq_address_line2": null,"hq_city": "San Diego","hq_country": "US","hq_full_address": "5775 Morehouse Drive, San Diego, CA, US","hq_postalcode": "92121","hq_region": "California","industries": ["Wireless Services", "Semiconductor Manufacturing"],"job_description": "We are looking for a New Graduate AI SW SDK Engineer to join …","job_functions": ["Engineering", "Information Technology"],"job_id": "4344628911","job_location": "Santa Clara, CA, United States","job_type": "Full-time","posted": "2026-05-18 11:33:11","remote_allow": "On-site","salary_details": null,"salary_display": "$110,000 - $140,000","skills": ["Python", "C++", "CUDA", "ONNX", "PyTorch"],"specialities": ["wireless", "semiconductor", "5G"],"views": 1240,"row_status": "delivered","why_withheld": "N/A"}
Detail columns (applies through views) are null in rows produced by Mode 1 (Search Jobs) and Mode 3 (Search Company Jobs); they only populate in Mode 2 (Fetch Job Details).
On the free plan, the results also tell you what was left behind. When the run hits the free cap with matches still remaining, the jobs that were already fetched but not delivered are published at the end of the same dataset with row_status = "withheld_free_plan" and a why_withheld sentence naming exactly what is locked and how many jobs the search matched in total. Title, company and location are real on those rows; the link, salary and details are not. Those rows are never charged, and a paid run never produces them.
⚡ How to use
- Choose Operation Mode at the top of the input panel.
- The matching
🎯 Mode Nsection appears below — fill its primary field (keywords / job URLs / company URLs). - Optionally tune the
🔬 Advanced filteringsection. - Set the per-mode cap (
maxItemsModeN) if you want a hard ceiling. Leave at 0 for unlimited (paid plan only). - Click Save & Start.
If you start a run with no input filled in, the Actor uses a built-in demo seed and caps the output at 10 rows — see the USER_MESSAGE record in the run's Storage tab.
Recruiting a daily delta? Save your filled-in search as an Apify Task and schedule it to run daily — diff against yesterday's dataset to catch new postings the moment they appear.
🧭 When to use it
- Recruiting and sourcing — a LinkedIn job search by keyword, narrowed by the geo cascade (198 countries → 1,147 regions and states → 932 major cities → any LinkedIn-indexed place typed by hand) and by
searchJobsDatePosted,searchJobsExperienceLevels,searchJobsJobTypesandsearchJobsUnder10Applicants— so you see the roles worth applying pressure to, not the whole board. - Hiring-team contacts for outbound — Mode 2 returns
hiring_teamon each posting alongsidecompany_linkedin_url,employee_rangeandindustries, which is the account context you need in the same row as the person. - LinkedIn salary benchmarking —
salary_displayfor reading andsalary_details(min, max, period, currency) for aggregating, grouped byjob_title,locationandexperience_level. - Competitor hiring intelligence — Mode 3 takes company LinkedIn URLs and returns every open role at each of them, so new job postings at named accounts show up as ordinary rows you can diff day to day.
- Skills and market research —
skills,job_functionsandindustriesper posting, withappliesandviewsto tell a contested role from a quiet one. - Bulk enrichment of LinkedIn jobs by URL — paste a list of posting links and get all 33 detail columns per link; postings that returned nothing are named in the run report and are never charged.
🎯 Example tasks
Ready-to-run examples — each opens a pre-configured task you can run in one click (no setup):
- Scrape LinkedIn jobs by keyword — Search LinkedIn job postings by keyword and location — export title, company, location, salary, posted date and apply link.
- Find Data Analyst jobs on LinkedIn — Export current Data Analyst job openings from LinkedIn — company, location, salary, posted date and direct apply link.
- Search Product Manager jobs on LinkedIn — Find Product Manager roles on LinkedIn by location — export company, title, salary, posted date and apply link as structured data.
- Scrape remote jobs on LinkedIn — Export current remote job openings from LinkedIn by keyword — company, title, salary, posted date and direct apply link as structured data.
- Find Software Engineer jobs on LinkedIn — Export current Software Engineer job openings from LinkedIn — company, location, salary, posted date and direct apply link as structured data.
- Scrape jobs by company on LinkedIn — Get every open job at any company from its LinkedIn page — title, location, salary, posted date and apply link as structured data.
🛠 Operation modes
🎯 Mode 1: Search Jobs
Search LinkedIn jobs by keyword + location with optional filters (date posted, job type, experience level, workplace type, Easy Apply, low-applicant). 25 jobs per upstream call; the Actor paginates until your cap is reached.
Required: searchJobsKeywords (free text). Geo defaults to United States.
Geo targeting — four fields, most specific non-empty wins (top to bottom on the input panel):
- Country (
searchJobsGeoCode) — 198 countries, required default (US). - State / province / region (
searchJobsState, optional) — ~1500 entries covering top 70 markets. Overrides Country. - Major city (
searchJobsCity, optional) — top 1000 cities globally by population. Overrides State + Country. - Custom Location (
searchJobsLocationCustom, optional) — type ANY LinkedIn-indexed place ("Cluj-Napoca", "Tartu", "Pune", "Brooklyn") or paste a numeric LinkedIn geoId. Resolved live at run start. Overrides everything above.
⚠️ Make sure Operation Mode is "Mode 1: Search Jobs". Mode-2 / Mode-3 fields are ignored here.
🎯 Mode 2: Fetch Job Details
Enrich an array of LinkedIn job-posting URLs with all 33 detail columns (description, skills, salary, hiring team, …). Use this after Mode 1 to drill into the postings you care about.
Required: fetchJobDetailsUrls (array of LinkedIn job URLs). Optional toggles: include skills / include hiring team (both default on).
If some links return nothing (the posting was closed, filled, or removed), the run report says exactly how many — empty links are never charged.
⚠️ Make sure Operation Mode is "Mode 2: Fetch Job Details". Mode-1 / Mode-3 fields are ignored here.
🎯 Mode 3: Search Company Jobs
Find every open job at one or more LinkedIn companies. The Actor resolves each company URL to its LinkedIn numeric id, then runs a single search across all of them — all locations, every role. Leave the keyword blank to get the complete listing; add one core keyword (e.g. "engineer") only when you need to narrow.
Some companies hire on their own careers site and list no jobs on their LinkedIn page. When a search comes back empty, the run report names each company with its live open-roles count — so you can tell "no roles listed" apart from "keywords too narrow".
Required: searchCompanyJobsCompanyUrls (array of company LinkedIn URLs). Optional: searchCompanyJobsKeywords. To limit by location, set the Mode 1 location fields — otherwise Mode 3 searches worldwide. Large companies can list 1,000+ open roles — set maxItemsMode3 to bound cost.
⚠️ Make sure Operation Mode is "Mode 3: Search Company Jobs". Mode-1 / Mode-2 primary fields are ignored here (only the Mode-1 location fields carry over, and only when you change them).
💸 Pricing
PAY_PER_EVENT — one event, one price, across all modes.
| Event | Price | When it fires |
|---|---|---|
JOB_ANALYZED | $0.030 per job | Once per row pushed to the dataset (any mode). |
Apify platform compute (memory + storage + bandwidth) is on top of that and small for this workload — a 20-job Mode 1 run typically costs well under $0.05 in platform overhead.
🔬 Advanced filtering
Filters apply after the upstream fetch, before the row lands in the dataset. The paid event still fires on the raw fetch — filters narrow your output, not your bill.
| Filter | Effect |
|---|---|
filterTitleContains | Drop rows whose job title doesn't contain this substring. |
filterTitleNotContains | Drop rows whose job title contains this substring (great for excluding "senior" or "intern"). |
filterCompanyNames | Allow-list — drop rows whose company name doesn't match one of the entries. |
filterLocationContains | Substring match against location. |
filterSalaryMin | Drop rows whose listed salary is below this. Note: LinkedIn rarely surfaces salary on search rows, so any non-zero floor drops most of them. |
filterPostedWithinDays | Drop rows posted more than N days ago. |
filterRemoteOnly | Drop rows not flagged remote. |
🎁 Free vs paid plan
| Free plan | Any paid Apify plan | |
|---|---|---|
| Jobs per run | up to 20, in full (links, salary, details) | your own maxItemsModeN — no cap |
| All 3 modes | ✓ | ✓ |
| Advanced filters | ✓ | ✓ |
| Matches beyond the cap | 🔒 preview rows: title, company & location visible; links, salary & details locked | ✓ delivered in full |
| Scheduling via Tasks | ✓ (capped at 20 jobs/run) | ✓ full runs |
The first 20 jobs of a free run are complete rows — nothing inside them is masked. When a search matches more than 20, the next few matches are previewed at the end of your dataset (row_status = 'withheld_free_plan'; preview rows are never charged) and the run's USER_MESSAGE (Storage tab) says exactly how many jobs matched in total. Any paid plan lifts the cap — re-run the same search and the full result set, links and salary included, is yours.
📊 Output destinations
The Actor writes to the standard Apify dataset, which is reachable as JSON / CSV / Excel / RSS / HTML / XML via the Dataset API. Ready-to-pipe into:
- Google Sheets — via the Apify ↔ Sheets integration.
- Airtable — via Apify ↔ Airtable.
- Webhooks — every successful run can POST to your endpoint.
📡 Live status & storage records
The Actor exposes a live progress page while it works (Apify Console → Live View) and writes named records to the run's key-value store. These are stable, machine-readable surfaces — read them with Actor.getValue(<KEY>) from another Actor, or over the REST API, instead of parsing the log.
| Record | What is in it |
|---|---|
status.html | The live status page — progress, mode, what has been fetched so far. Rendered during the run, not after it. |
RUN_SUMMARY | Complete run statistics: status, jobs found and charged, filter breakdown, cost, and any free-plan caps applied. |
USER_MESSAGE | A plain-English explanation of what happened, written whenever a run needs one — including every zero-result run, so a run that returned nothing still tells you why. |
FREE_LIMITS_APPLIED | Stable id codes for the free-plan ceilings this run hit; empty or absent on paid runs. Switch on this instead of grepping the log. |
🤖 Programmatic / API use
Most of this Actor's traffic is machines, not people. Everything the input panel does is available over the API.
Start a run and get the rows back in one call:
curl -X POST "https://api.apify.com/v2/acts/afanasenko~linkedin-jobs-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H 'Content-Type: application/json' \-d '{"operationMode": "searchJobs","searchJobsKeywords": "data engineer","searchJobsLocationCustom": "Berlin","searchJobsDatePosted": "Past week","maxItemsMode1": 100}'
Or fire it and poll, which is what you want for large company sweeps:
# startcurl -X POST "https://api.apify.com/v2/acts/afanasenko~linkedin-jobs-scraper/runs?token=$APIFY_TOKEN" \-H 'Content-Type: application/json' \-d '{"operationMode":"fetchJobDetails","fetchJobDetailsUrls":["https://www.linkedin.com/jobs/view/4344628911"]}'# then, per run idcurl "https://api.apify.com/v2/actor-runs/<RUN_ID>?token=$APIFY_TOKEN"curl "https://api.apify.com/v2/actor-runs/<RUN_ID>/dataset/items?format=csv&token=$APIFY_TOKEN"curl "https://api.apify.com/v2/actor-runs/<RUN_ID>/key-value-store/records/RUN_SUMMARY?token=$APIFY_TOKEN"
- Any format, no re-run — the same dataset is readable as JSON, CSV, Excel, XML, RSS or HTML via
?format=. - Scheduled — save an input as a Task and give it a schedule; each run writes its own dataset, so a daily delta is a diff of two dataset ids.
- Webhooks — point
ACTOR.RUN.SUCCEEDEDat your endpoint and skip polling entirely. - Agents — the Actor is callable through Apify's MCP server, so an assistant can run it as a tool and read the rows back without you writing glue code.
- For unattended pipelines, branch on
RUN_SUMMARYandFREE_LIMITS_APPLIEDrather than on row count: a short result set and a capped result set are different situations, and those two records tell them apart.
❓ FAQ
Q: Do I need a LinkedIn account, cookies, or login? No. You don't connect your LinkedIn account and there's nothing to log in to — just enter keywords, company URLs, or job URLs and run. Only public LinkedIn job data is returned.
Q: My run returned zero rows.
99% of the time this is "wrong mode selected." Open the input panel, check Operation Mode at the top, then make sure the matching 🎯 Mode N section is the one you filled in.
Q: How do I get more results from a search? Keep your keywords to the core job title — extra words ("senior", "MBA", "remote") sharply narrow results and can return nothing. Broaden the location, widen "Date posted" to "Past month" or "Any time", and turn off narrow filters like "Easy Apply only". A single clear title in a big market (e.g. "Data Analyst" in the United States) returns the most.
Q: Salary is empty on most rows.
LinkedIn rarely shows salary on search-result rows. Enrich the postings you care about via Mode 2 — salary_display is more often populated there.
Q: My target city / town isn't in the City dropdown. Use the Custom Location field — type any LinkedIn-indexed place (e.g. "Cluj-Napoca", "Tartu", "Brooklyn"). The Actor resolves it live against LinkedIn's typeahead at run start and logs the resolved name + ID so you can verify the pick.
Q: I already have a LinkedIn geoId from the URL — can I paste it directly?
Yes. Paste the numeric ID (e.g. 102095887 for California) into Custom Location. The Actor detects 6–10 digit numeric inputs and skips the typeahead resolve.
Q: Are filters charged?
The JOB_ANALYZED event fires on the raw upstream fetch. Filters in the 🔬 Advanced filtering section run after the fetch, so they narrow your output but not your bill. If "all rows filtered" is a problem, loosen the filters and post-filter downstream.
Q: Can I search several job titles at once?
Yes — separate them with commas in the keyword field (e.g. Risk Analyst, Data Analyst, Financial Analyst). The Actor searches for jobs matching any of them. Custom Location works the same way: a comma list resolves to the first place LinkedIn recognises.
Q: Can I sort by posting date?
Yes — set searchJobsSortBy to "Most recent" in Mode 1.
Q: How many jobs can I get in one run?
The free plan saves up to 20 jobs per run, across all modes. On a paid plan there's no cap — you're limited only by the per-mode cost ceiling you set (maxItemsModeN). If a free run hits the limit, the run's USER_MESSAGE tells you how many more matched, and the jobs that were already fetched but not delivered are appended to the same dataset as preview rows (row_status = "withheld_free_plan") with their titles, companies and locations visible — so you can see what you didn't get without paying for it.
Q: How do I get every open role at a company? Use Mode 3 with the company's LinkedIn URL and leave the keyword field blank — you get the complete listing, all locations. If the run comes back empty, the run report shows the company's live open-roles count: some companies hire on their own careers site and list nothing on LinkedIn.
Q: Can I bulk-enrich a big list of job posting URLs?
Yes. Mode 2 takes many job URLs at once and returns all 33 detail columns per posting. For very large lists, split into batches and set maxItemsMode2 to cap spend per run.
Q: Can I schedule this to run daily? Yes — save your filled-in input as an Apify Task and add a schedule to it. Each scheduled run uses your saved input; free-plan scheduled runs are capped at 20 jobs per run like any other run.
⏱ Timing & runtime
- Mode 1: ~2-4 seconds per 25-job page — a full free-plan run (20 jobs) finishes in seconds.
- Mode 2: ~2-3 seconds per job URL (sequential — the detail endpoint is single-job).
- Mode 3: 1 extra company-id resolve call per company URL, then same as Mode 1.
Default Apify timeout is fine for runs ≤ a few hundred rows; bump the timeout in Run Options for larger sweeps.
🤝 Related Actors
| You start with… | You want to find… | Use this actor |
|---|---|---|
| Job criteria / job-posting URLs / company URLs | Job listings + full description, salary, skills, hiring team | This actor |
| LinkedIn profile URLs or search criteria | Profiles, companies, decision-makers (70+ fields) | LinkedIn Profile Scraper |
| Instagram handles | Profile data + 5 discovery modes (followers, similar, keyword, location) | Instagram Profile Scraper |
| TikTok profile / hashtag | Profile data + posts | TikTok Profile Scraper |
| YouTube channel URL / handle | Channel + video metadata | YouTube Channel Scraper |
| US ZIP / address | Property listings + market data | Zillow Scraper |
🆘 Support
Found a bug, a missing field, or a posting that didn't enrich? Use the Issues tab on this Actor's Apify Store page — it's the fastest path to a fix.
💡 Tips & Best Practices
Getting max results
- Mode 1: keep keywords to the core job title; comma-separate several titles for an OR search (
Risk Analyst, Data Analyst). - Geo targeting: the most specific non-empty field wins — Custom Location beats City beats State beats Country.
- Mode 3: leave the keyword blank to get every open role at the listed companies; add one core keyword only when you need to narrow.
Cost optimization
- Use
maxItemsModeNaggressively when testing — 5-10 rows to validate the output shape, then scale up. - Shortlist with Mode 1's slim rows first, then enrich only the postings you care about in Mode 2 — both cost $0.030/row, so enriching everything you searched doubles the spend.
- Filters don't reduce billing (they run after the fetch) — narrow with keywords and location first, filter second.
Performance
- Mode 1: ~2-4 seconds per 25-job page. Mode 2: ~2-3 seconds per URL (sequential). Mode 3: one extra company-resolve call per company URL.
- The live status page shows progress in real time; the default Apify timeout is fine for runs up to a few hundred rows.
Data quality
salaryon search rows is often empty — Mode 2'ssalary_displayis populated far more often.- Job postings expire quickly; enrich URLs via Mode 2 soon after collecting them.
hiring_teamcontacts appear when LinkedIn shows them publicly on the posting — not every posting lists its recruiter.
⚖️ Disclaimer
This Actor scrapes only publicly available LinkedIn data. You are responsible for using the data in accordance with LinkedIn's Terms of Service, applicable privacy laws (GDPR, CCPA), and ethical-use guidelines. The Actor does not scrape private data, personal feeds, or content behind login walls.
📋 Changelog
See ./CHANGELOG.md for the version history.