LinkedIn Jobs Scraper – Jobs, Companies & Recruiters
Pricing
from $1.00 / 1,000 job results
LinkedIn Jobs Scraper – Jobs, Companies & Recruiters
Scrape LinkedIn jobs by keyword, location, or search URL. Get full job descriptions, company details, recruiter/job-poster data, salaries, applicant counts, and structured output. No LinkedIn login or cookies required.
Pricing
from $1.00 / 1,000 job results
Rating
0.0
(0)
Developer
Delowar Munna
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
4 days ago
Last modified
Categories
Share

Scrape LinkedIn jobs by keyword, location, or search URL. Get full job descriptions, company details, recruiter/job-poster data, salaries, applicant counts, and structured output. No LinkedIn login or cookies required.
What does this actor do?
It searches LinkedIn Jobs the way you would — by keyword and location, or from a search URL you already built — and returns every result as a clean, structured record. For each job you get the full description, the employer, where it is, when it was posted, how many people have applied, the salary when LinkedIn publishes one, and the recruiter or job poster when one is shown publicly.
You never supply a LinkedIn username, password, cookie, li_at token, or session ID. The actor only reads pages that are public to anyone on the internet.
Why use this actor?
- Search the way you already do — keywords plus locations, or paste LinkedIn search URLs.
- Full job descriptions, not just the card preview.
- Company enrichment — website, domain, industry, size, employee count, headquarters, founding year.
- Recruiter / job-poster details when LinkedIn shows them publicly, at no extra charge.
- Automatic deduplication across every keyword and location in the run.
- Incremental scraping — pass the job IDs you already have and they are skipped before they cost anything.
- Every keyword x every location in a single run, with full traceability back to the search that found each job.
- No login, no cookies, no proxy setup required.
What data do you get?
| Group | Fields |
|---|---|
| Job | jobId, jobUrl, title, descriptionText, descriptionHtml, applyUrl, postedAt, listedAt |
| Employer | companyName, companyLinkedInUrl, companyId |
| Classification | location, workplaceType, employmentType, seniorityLevel, jobFunctions, industries |
| Demand | applicantCount, easyApply |
| Pay | salary.min, salary.max, salary.currency, salary.period, salary.text |
| Recruiter | recruiter.name, recruiter.title, recruiter.linkedinUrl, recruiter.profileImageUrl |
| Company (opt-in) | company.website, company.domain, company.industry, company.companySize, company.employeeCount, company.headquarters, company.companyType, company.foundedYear, company.description, company.logoUrl |
| Contact (opt-in) | emails |
| Traceability | searchMetadata.source, .keyword, .location, .searchUrl, .parentSearchId, .segment, .provider |
| Run | scrapedAt |
Fields LinkedIn does not publish are returned as null. Values are never invented.
How to search
Provide Search Keywords and optionally Locations. Every keyword is combined with every location, so 3 keywords x 2 locations runs 6 searches and merges the results with deduplication.
{"searchKeywords": ["Software Engineer"],"locations": ["Sydney, Australia"],"maxResults": 100}
Using LinkedIn search URLs
Already built a search on LinkedIn? Paste the URL. Filters in the URL are preserved, tracking parameters are stripped, and duplicates are removed. You can also paste individual job URLs (linkedin.com/jobs/view/...) to scrape those postings directly.
{"searchUrls": ["https://www.linkedin.com/jobs/search/?keywords=data%20analyst&location=Melbourne","https://www.linkedin.com/jobs/view/4443029595/"],"maxResults": 200}
Filters
Standard: Posted Within, Workplace (remote / hybrid / on-site), Employment Type, Experience Level, Easy Apply, Under 10 Applicants, Sort By.
Advanced: include/exclude companies, exclude job-title keywords, exclude locations, job functions, industries, salary range and currency, applicant range, company employee-count range, and exclude recruitment agencies.
LinkedIn does not always honour its own filter parameters, so every filter is re-checked against the scraped data before a job is saved. See Limitations for which filters are best-effort.
Company enrichment
Enable Include Company Details to add employer data. Enrichment is charged once per unique company, not once per job — a company appearing in 50 jobs is charged once. Failed lookups are not charged and never remove the job.
Recruiter enrichment
Enable Include Recruiter Details (on by default) to capture the job poster or hiring-team member LinkedIn shows publicly: name, headline, profile URL, and photo. This costs nothing extra because it is read from the job page already being fetched.
Expect roughly 1 in 5 jobs to name a recruiter — measured at 16–20% across live runs. LinkedIn simply does not attach a public poster to most postings, so an empty recruiter field is that job's reality rather than a scraping failure. The actor reports what is public and never guesses.
Deduplication and incremental runs
Duplicates are removed by job ID, then job URL, then title + company + location. For scheduled runs, pass the IDs you already collected in Skip Job IDs — matching jobs are skipped before their page is fetched, so they cost you nothing.
{"searchKeywords": ["Product Manager"],"locations": ["Remote"],"skipJobIds": ["4219847745", "4219848811"],"maxResults": 500}
Large search mode
Enable Expand Large Searches when a broad search hits LinkedIn's result ceiling. The actor splits it into smaller sub-searches, merges the results, and deduplicates globally. Your Maximum Jobs limit still applies to the whole run.
Example inputs
1. Simplest possible run
{"searchKeywords": ["Software Engineer"],"locations": ["Sydney, Australia"],"maxResults": 100}
2. Lead generation — direct employers with company data
Skip staffing agencies, keep mid-sized companies, and pull employer details plus the job poster.
{"searchKeywords": ["Software Engineer", "Backend Engineer"],"locations": ["Sydney, Australia", "Melbourne, Australia"],"maxResults": 1000,"postedWithin": "pastWeek","workplaceTypes": ["remote", "hybrid"],"employmentTypes": ["fullTime", "contract"],"experienceLevels": ["midSenior"],"excludeJobTitleKeywords": ["Manager", "Director"],"excludeRecruitmentAgencies": true,"minimumCompanyEmployees": 20,"maximumCompanyEmployees": 5000,"removeDuplicates": true,"includeCompanyDetails": true,"includeRecruiterDetails": true,"expandLargeSearches": true}
3. Daily monitoring — only jobs you haven't seen
Pass back the jobId values from yesterday's run. Matching jobs are skipped before their page is fetched, so repeats cost you nothing.
{"searchKeywords": ["Product Manager"],"locations": ["Remote"],"postedWithin": "past24Hours","maxResults": 500,"skipJobIds": ["4219847745", "4219848811", "4438759861"],"removeDuplicates": true}
4. Scrape a search URL you already built
{"searchUrls": ["https://www.linkedin.com/jobs/search/?keywords=data%20analyst&location=Melbourne&f_TPR=r604800","https://www.linkedin.com/jobs/view/4438759861/"],"maxResults": 200,"extractPublicEmails": true}
Example output
One dataset item per job. Every example below is a real record from a live run — nothing is illustrative. Fields LinkedIn does not publish come back null; values are never invented.
{"jobId": "4438759861","jobUrl": "https://www.linkedin.com/jobs/view/4438759861/","title": "Software Engineer","companyName": "Verse","companyLinkedInUrl": "https://www.linkedin.com/company/verse-group---recruitment-specialists","companyId": "verse-group---recruitment-specialists","location": "Greater Perth Area","workplaceType": null,"employmentType": "Full-time","seniorityLevel": "Mid-Senior level","postedAt": "2026-07-17T00:00:00.000Z","listedAt": null,"applicantCount": 200,"easyApply": true,"salary": {"min": 110000,"max": 130000,"currency": "AUD","period": "YEAR","text": "A$110,000.00/yr - A$130,000.00/yr"},"jobFunctions": ["Information Technology"],"industries": ["Software Development"],"descriptionText": "About the Role. An exciting opportunity is available for a Software Engineer to join a growing software development team based in Perth. You'll work across both backend and frontend development using technologies including Java, Spring Boot, Python …","descriptionHtml": "<p><strong>About the Role</strong></p><p>An exciting opportunity is available for a Software Engineer …</p>","applyUrl": "https://www.linkedin.com/jobs/view/4438759861/","recruiter": {"name": "Matt Burke","title": "2024 Sourcr Best Recruiter WA - IT/ Telecommunications | Division Manager - Applications at Verse Group","linkedinUrl": "https://www.linkedin.com/in/matt-burke-perth","profileImageUrl": "https://media.licdn.com/dms/image/v2/D5603AQECuO74T8q5TA/profile-displayphoto-scale_400_400/…"},"company": {"website": "http://www.versegroup.com.au","domain": "versegroup.com.au","industry": "Staffing and Recruiting","companySize": "51-200 employees","employeeCount": 28,"headquarters": "Perth, Western Australia","companyType": "Privately Held","foundedYear": 2013,"description": "Verse is an innovative, agile boutique recruitment business specialising in niche tech skills …","logoUrl": "https://media.licdn.com/dms/image/v2/D560BAQEBvgF08-iP7A/company-logo_200_200/…"},"emails": ["matthew.burke@versegroup.com.au"],"searchMetadata": {"source": "structured_search","keyword": "Software Engineer","location": "Australia","searchUrl": null,"parentSearchId": null,"segment": null,"provider": "linkedin_public"},"scrapedAt": "2026-08-12T06:12:25.335Z"}
companySize is the employer's self-reported band; employeeCount is how many members LinkedIn actually lists. They legitimately differ — both are reported as published rather than reconciled.
Output views
The dataset ships five ready-made views. Switch between them with the tabs above the results table, or append ?view=<name> to the dataset API URL. Each sample below is the same real job seen through a different view.
Jobs overview
The fields most runs need — role, employer, location, recency, and how to apply.
{"title": "Software Engineer","companyName": "Verse","location": "Greater Perth Area","postedAt": "2026-07-17T00:00:00.000Z","workplaceType": null,"employmentType": "Full-time","applicantCount": 200,"salary.text": "A$110,000.00/yr - A$130,000.00/yr","recruiter.name": "Matt Burke","recruiter.linkedinUrl": "https://www.linkedin.com/in/matt-burke-perth","jobUrl": "https://www.linkedin.com/jobs/view/4438759861/"}
Full job data
Every field, including the complete job description and the search trace — the same shape as the Example output above.
Company details
Employer data, populated when Include Company Details is enabled.
{"companyName": "Verse","companyLinkedInUrl": "https://www.linkedin.com/company/verse-group---recruitment-specialists","companyId": "verse-group---recruitment-specialists","company.website": "http://www.versegroup.com.au","company.domain": "versegroup.com.au","company.industry": "Staffing and Recruiting","company.employeeCount": 28,"company.companySize": "51-200 employees","company.headquarters": "Perth, Western Australia","company.foundedYear": 2013,"jobUrl": "https://www.linkedin.com/jobs/view/4438759861/"}
Recruiters
Job posters and hiring-team members shown publicly on the job page.
{"jobId": "4438759861","title": "Software Engineer","companyName": "Verse","recruiter.name": "Matt Burke","recruiter.title": "2024 Sourcr Best Recruiter WA - IT/ Telecommunications | Division Manager - Applications at Verse Group","recruiter.linkedinUrl": "https://www.linkedin.com/in/matt-burke-perth","jobUrl": "https://www.linkedin.com/jobs/view/4438759861/"}
Search trace
Which search produced each job — essential when one run covers many keywords, locations, or URLs.
{"jobId": "4438759861","jobUrl": "https://www.linkedin.com/jobs/view/4438759861/","searchMetadata.source": "structured_search","searchMetadata.keyword": "Software Engineer","searchMetadata.location": "Australia","searchMetadata.searchUrl": null,"searchMetadata.parentSearchId": null,"searchMetadata.segment": null,"searchMetadata.provider": "linkedin_public"}
How complete is the data?
Measured on a real 50-job run with company enrichment enabled:
| Field | Populated | Why |
|---|---|---|
title, companyName, location, postedAt, jobUrl, applyUrl | 100% | Always available |
descriptionText, employmentType, seniorityLevel, jobFunctions, industries, applicantCount | 100% | Read from the job page |
company | 100% | When Include Company Details is on |
workplaceType | ~44% | Only when the posting states remote / hybrid / on-site |
salary | ~20% | Most LinkedIn jobs publish no salary |
recruiter | ~16% | LinkedIn names a public job poster on a minority of postings |
emails | ~14% | Only when an address appears in the description |
The last four reflect what LinkedIn publishes, not extraction failures — where the value exists on the page, it is captured.
Pricing
This actor uses Apify's pay-per-event pricing:
| Event | When it is charged |
|---|---|
job-result | Once per unique job saved to the dataset. |
company-enrichment | Once per unique company successfully enriched — not once per job. |
Never charged: duplicates, jobs removed by filters, jobs matched by Skip Job IDs / Skip URLs, failed extractions, failed company lookups, and recruiter data (which is bundled into the job result at no extra cost).
There is no Actor-start fee — a run that returns nothing costs nothing.
This README deliberately quotes no figures: current rates are shown on the actor's Pricing tab, which is always the live source.
Free plan limitations
Nothing is switched off on the Apify Free plan. Every job field — including full descriptions, company details and recruiter data — is returned exactly as it is on a paid plan.
The single difference: when LinkedIn blocks part of a run, the actor can fall back to a paid backup data provider to recover those jobs, and that backup has a smaller budget on the Free plan. If it is ever reached the run continues normally with a status message, is never failed, and you are never charged for it. Paid plans get the larger budget because the backup provider bills us per request.
🚦 Proxy policy
Use Apify Datacenter proxy or no proxy for normal runs — both work reliably for LinkedIn's public job pages at this actor's conservative request rate.
Apify Residential proxy is not supported. The actor fails at startup if apifyProxyGroups includes RESIDENTIAL. Reason: in pay-per-event actors, residential bandwidth is billed to the developer rather than to you, so a single bandwidth-heavy run could exceed the per-result revenue.
If you genuinely need residential routing, supply your own provider via the proxy editor's Custom proxy URLs field — that traffic goes through your provider, not Apify, and is unaffected:
http://user:pass@proxy.iproyal.com:12321http://user:pass@proxy.brightdata.com:22225http://user:pass@proxy.oxylabs.io:7777
API example
curl -X POST "https://api.apify.com/v2/acts/coregent~linkedin-jobs-scraper/runs?token=$APIFY_TOKEN" \-H 'Content-Type: application/json' \-d '{"searchKeywords": ["Software Engineer"],"locations": ["Sydney, Australia"],"maxResults": 100}'
Results are available from the run's dataset endpoint. Every run also writes a RUN_SUMMARY record to the default key-value store:
{"searchesPlanned": 1,"searchesCompleted": 1,"rawJobsFetched": 120,"duplicatesRemoved": 3,"jobsFilteredOut": 0,"jobsSaved": 100,"companiesEnriched": 0,"recruitersEnriched": 20,"errors": 0,"jobsSkippedByIncremental": 0,"detailPagesVisited": 115,"requestsAttempted": 130,"retriedRequests": 2,"throttled429s": 2,"blockedRequests": 0,"runtimeSeconds": 64}
blockedRequests counts requests that failed for good. throttled429s counts requests LinkedIn rate-limited — these are normally retried successfully, so a small number here with blockedRequests: 0 means your results are complete. If throttling climbs above a few percent of requestsAttempted, the run logs a warning telling you to slow it down.
recruitersEnriched and companiesEnriched count what was delivered in your dataset, so they reconcile against the rows you receive. detailPagesVisited counts work done and is normally slightly higher than jobsSaved.
Performance
A 100-job run completes in about a minute. The figures above are from a real run: 100 jobs saved in 64 seconds, with 0 errors and 0 blocked requests.
The actor collects only a small margin beyond your Maximum Jobs limit, and widens that margin automatically when you set filters that can only be judged after the job page is read (workplace, employment type, experience level, salary, applicants, company size). So an unfiltered run does close to the minimum work needed, and a heavily filtered run fetches enough to still fill your limit.
Limitations
- Public data only. Anything LinkedIn hides behind a login is not available, by design.
- Recruiter data is sparse — about 1 in 5 jobs. Measured at 16–20% across live runs. LinkedIn only attaches a public job poster to some postings.
- Public emails are rarer still. Most employers route applications through LinkedIn instead of publishing an address, so
emailsis usually empty. Measured on recruitment-agency listings — the densest case — only about 1 in 3 contained one; on general roles it is far lower. Every address that does appear in a description is captured. - Applicant counts are best-effort. LinkedIn often renders a bucket such as "Over 200 applicants" instead of an exact figure. Jobs with no exact count are kept, so Under 10 Applicants, Min Applicants and Max Applicants are approximate.
- Salary is only as good as the posting. Most LinkedIn jobs publish no salary. Jobs without one are kept unless you enable Exclude Jobs Without Salary. Salary is never estimated.
postedAtcan be approximate. LinkedIn often shows a relative date ("2 weeks ago"), which resolves to a day, not an exact time.- Filter parameters are hints. LinkedIn does not reliably honour its own URL filter parameters, so every filter is re-checked against the scraped data. Jobs missing the relevant field are kept rather than silently dropped.
- Recruitment-agency exclusion is a heuristic based on company naming patterns, plus the company's LinkedIn industry when company enrichment is enabled.
- Result ceiling. LinkedIn caps how deep any single search can page. Use Expand Large Searches for very broad queries.
FAQ
Do I need a LinkedIn account, cookies, or an li_at token?
No. The actor never asks for credentials and only reads publicly accessible pages.
Do I need to configure a proxy? No. Defaults work. See the Proxy policy for the one unsupported option.
Am I charged for duplicates or filtered-out jobs? No. You are charged only for unique jobs actually saved, plus company enrichment if you enable it.
How do I run this daily without paying for the same jobs twice?
Collect jobId values from each run and pass them back in Skip Job IDs. Matching jobs are skipped before their page is fetched.
Why is recruiter empty on so many jobs?
Because LinkedIn only shows a job poster on some postings — about 1 in 5 in practice. The actor reports what is public and does not fabricate the field.
Do I need a proxy, and which one? Defaults work. Apify Datacenter or no proxy both run reliably; Apify Residential is not supported (see Proxy policy).
What happens if LinkedIn blocks part of my run?
The actor retries, rotates sessions, and falls back to a backup data provider for any job page it still cannot read, so blocked pages are recovered rather than returned empty. blockedRequests in RUN_SUMMARY tells you whether this happened.
Can I get more than a few hundred results for one broad search? LinkedIn limits how deep a single search can page. Enable Expand Large Searches, or split your query into more specific keywords and locations.
Legal
You are responsible for complying with applicable laws, with LinkedIn's terms and policies, and with privacy and data-protection rules that apply to the data you collect, including how you store and use personal data such as recruiter names and public email addresses. This actor collects only publicly accessible information and does not access private, logged-in, or paywalled content.