LinkedIn Jobs Scraper Pro
Pricing
from $25.00 / 1,000 results
LinkedIn Jobs Scraper Pro
Paste any LinkedIn job-search URL (with filters) and get every listing with full job details, deep company data, hiring-team profiles, salary, skills, applicant insights, and official-source contact enrichment. Deep pagination captures 100% of results.
Pricing
from $25.00 / 1,000 results
Rating
0.0
(0)
Developer
YESHWANT SINGH
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
LinkedIn Jobs Scraper Pro π
Paste any LinkedIn job-search URL β get every listing, with full job + company + hiring-team data and official-source contact enrichment. Nothing missed, built to be reliable.
This Actor was built from scratch to out-feature every LinkedIn job scraper in the Apify Store. It is HTTP-first against LinkedIn's public guest endpoints (fast, cheap, hard to block), adds an optional logged-in layer for data only visible when authenticated, and finishes with official-source contact enrichment for recruiter email and company phone.
β¨ Why this one is different
| Capability | Most scrapers | This Actor |
|---|---|---|
| Paste a search URL with all your filters | Partial | β
Full β search/, search-results/, collections/ |
| Capture 100% of results past LinkedIn's ~1,000 cap | β silently truncates | β Deep pagination auto-splits by facet |
| Company profile (website, industry, size, HQ, followers) | Sometimes | β Full, cached per company |
| Hiring team / job poster + their LinkedIn profile | Rare | β With logged-in session |
| Applicant insights & skills | Rare | β With logged-in session |
| Recruiter email + company phone | β or fabricated | β Official sources only, never guessed |
| Salary parsed into min/max/currency/period | Rare | β + raw text |
| Reliability (residential proxy, session pool, retries) | Varies | β Tuned for LinkedIn |
π§ What it scrapes
Job: id Β· title (role) Β· canonical job URL Β· company apply URL (offsite/ATS) Β· careers-portal URL Β· ATS name (Greenhouse, Lever, Workdayβ¦) Β· Easy-Apply flag Β· posted date + "time ago" Β· expiry Β· location Β· workplace type (on-site/remote/hybrid) Β· seniority / job level (Entry, Associate, Mid-Senior, Director, Executiveβ¦) Β· employment type Β· job function Β· industries Β· full description (text, HTML, optional Markdown) Β· skills Β· applicant insights Β· salary (raw + min/max/location-aware currency/period).
Company: name Β· LinkedIn URL Β· official website Β· tagline Β· description Β· industry Β· size Β· employee count Β· headquarters Β· followers Β· type Β· specialties Β· logo Β· phone Β· public inbox.
Contacts & hiring team: recruiter email + phone for the role (from the description, or provider enrichment) Β· descriptionEmails / descriptionPhones Β· and, when logged in, the hiring team's name Β· headline Β· LinkedIn profile URL plus the derived recruiterName / recruiterProfileUrl.
See the full field list in Output.
π Quick start
- Filter jobs on LinkedIn exactly how you want them.
- Copy the browser URL (it contains all your filters).
- Paste it into
searchUrlsand run.
{"searchUrls": ["https://www.linkedin.com/jobs/search-results/?keywords=Data%20Analyst&geoId=101452733&f_TPR=r86400"],"maxItems": 0,"deepPagination": true,"scrapeCompanyDetails": true,"proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }}
Prefer not to build a URL? Use the structured fields instead:
{"keywords": "Data Analyst","country": "au","datePosted": "past24h","experienceLevels": ["entry", "associate"],"workplaceTypes": ["remote", "hybrid"]}
country: "au" targets all of Australia by its LinkedIn geoId β more reliable than free text. Ten countries are built in; use country: "custom" + geoId for any other region.
ποΈ Input reference (highlights)
| Field | Type | Notes |
|---|---|---|
searchUrls | string[] | One or more LinkedIn search URLs. Recommended. |
keywords, location, country, geoId | string | Build a search without a URL. |
datePosted | enum | any / past24h / pastWeek / pastMonth. |
experienceLevels, jobTypes, workplaceTypes | string[] | Facet filters (used when building from keywords). |
maxItems | int | 0 = scrape everything. |
deepPagination | bool | Auto-split to break the ~1,000 cap. Default on. |
scrapeCompanyDetails | bool | Fetch each company page (cached). Default on. |
useLoginSession | bool | Unlock hiring team + applicant insights. See below. |
liAtCookie, jsessionidCookie | secret | Your LinkedIn session cookies. |
enrichContacts | bool | Fill recruiter email + company phone. |
contactProvider | enum | none / hunter / apollo. |
hunterApiKey, apolloApiKey | secret | Provider keys (bring your own). |
proxyConfiguration | proxy | RESIDENTIAL strongly recommended. |
maxConcurrency, minDelayMs, maxRequestRetries | int | Reliability tuning. |
π§© Deep pagination β capturing everything
LinkedIn's guest search returns at most ~1,000 results per query (40 pages Γ 25) and gives no total count. NaΓ―ve scrapers just stop there and silently miss the rest.
When deepPagination is on and a search hits the cap, this Actor automatically partitions the query by the next unused discrete facet β first by the six experience levels, then by job type, then by workplace type. Every job belongs to exactly one bucket, results are deduplicated by job id, and the union is complete. If a fully-faceted slice still exceeds 1,000, the Actor logs a clear warning telling you to add one more filter (e.g. a tighter date window or a sub-region).
π Logged-in session (optional) β hiring team, applicants, skills
Some fields are only visible to a signed-in LinkedIn user: the "Meet the hiring team" poster and their profile, applicant insights, and skill data. Provide session cookies to unlock them:
- Log in to LinkedIn in Chrome (use a dedicated account β see the warning below).
- Open DevTools β Application β Cookies β
https://www.linkedin.com. - Copy the value of
li_atintoliAtCookie. - Copy the value of
JSESSIONID(looks like"ajax:1234567890") intojsessionidCookieβ keep the quotes. - Set
useLoginSession: true.
Both are stored as encrypted secrets. On startup the Actor launches a real headless Chrome, applies your cookies, and logs π LinkedIn login OK β signed in as <you> or a clear π login FAILED β so you instantly know whether the session works (stale cookies are the #1 cause of empty hiring-team fields). For each job it then opens the logged-in job page and reads the "Meet the hiring team" card (name + LinkedIn profile URL) and the true external apply URL that the public page hides behind a login wall.
Because logged-in mode drives a browser it's slower and needs more memory β run it with 2 GB+ RAM and a Residential proxy for best results. (linkedinHiringTeamQueryId is an optional advanced fallback for the API path and can be left blank.)
π‘ You often don't even need login for the recruiter's contact. Many postings β especially recruitment-agency listings β put the recruiter's email and phone in the description text. The Actor extracts those automatically into
recruiterEmail/recruiterPhone(sourcejob-description). See Contact enrichment below.
β οΈ Account risk. Scraping while authenticated can violate LinkedIn's User Agreement and may get the account restricted. Use a throwaway account you can afford to lose, and keep concurrency/
minDelayMsgentle. LeaveuseLoginSessionoff for the safe, public-only crawl.
π§ Contact enrichment β email & phone, official sources only
Recruiter email and company phone are not published in LinkedIn's structured data, so this Actor sources them the legitimate way, in priority order, and leaves the field empty if nothing genuine is found (no guessed patterns, no grey-market lists):
- The job description itself (first-party, always legitimate, no key) β recruiters frequently write their email/phone into the posting ("send your resume to β¦", "call me on β¦"). The Actor pulls these into
recruiterEmail/recruiterPhone(sourcejob-description) plus fulldescriptionEmails/descriptionPhonesarrays. This is the most reliable source of a role-specific contact. - The company's own website (first-party, always legitimate) β reads
tel:/mailto:links andschema.orgOrganizationdata (telephone,email,contactPoint) that the company itself published. No key required; enabled byscrapeCompanyWebsiteForContacts. - A reputable provider you opt into (bring your own key):
Set enrichContacts: true, pick contactProvider, and paste the matching API key. Enriched fields carry a recruiterEmailSource / confidence so you always know where a value came from.
π€ Output
One dataset item per job (undefined fields are omitted):
{"jobId": "4436678938","title": "Senior Data Analyst","jobUrl": "https://www.linkedin.com/jobs/view/4436678938/","applyUrl": "https://boards.greenhouse.io/acme/jobs/12345","externalApplyUrl": "https://boards.greenhouse.io/acme/jobs/12345","companyApplyUrl": "https://boards.greenhouse.io/acme/jobs/12345","companyCareersUrl": "https://boards.greenhouse.io/","atsName": "Greenhouse","applyIsOffsite": true,"easyApply": false,"postedDate": "2026-07-12","postedTimeAgo": "1 day ago","expiresAt": "2026-08-12","location": "Sydney, New South Wales, Australia","workplaceType": "Hybrid","seniorityLevel": "Mid-Senior level","employmentType": "Full-time","jobFunction": "Analyst","industries": "IT Services and IT Consulting","descriptionText": "β¦","descriptionHtml": "β¦","skills": ["SQL", "Python", "Power BI"],"applicantsCount": 200,"applicantInsights": "Over 200 applicants","salaryText": "A$90,000 β A$110,000/yr","salaryMin": 90000, "salaryMax": 110000, "salaryCurrency": "AUD", "salaryPeriod": "year","companyName": "Acme Corp","companyLinkedinUrl": "https://www.linkedin.com/company/acme-corp/","companyWebsite": "https://www.acme-corp.com","companyIndustry": "Information Technology","companySize": "1,001-5,000 employees","companyHeadquarters": "Sydney, NSW","companyFollowers": 48213,"companyPhone": "+61255501234","hiringTeam": [{ "name": "Jane Recruiter", "headline": "Talent Acquisition @ Acme", "profileUrl": "https://www.linkedin.com/in/jane-recruiter/" }],"recruiterName": "Jane Recruiter","recruiterProfileUrl": "https://www.linkedin.com/in/jane-recruiter/","recruiterEmail": "jane@acme-corp.com","recruiterEmailSource": "hunter.io","recruiterEmailConfidence": 94,"recruiterPhone": "61283195529","recruiterPhoneSource": "job-description","descriptionEmails": ["jane@acme-corp.com"],"descriptionPhones": ["61283195529"],"searchUrl": "https://www.linkedin.com/jobs/search-results/?keywords=Data%20Analyst&f_TPR=r86400","dataSource": "guest+loggedIn","scrapedAt": "2026-07-13T02:20:00.000Z"}
A SUMMARY record (searches started, jobs enqueued/pushed, companies resolved) is written to the default key-value store at the end of every run.
π οΈ Deploy it (you push it yourself)
You need the Apify CLI and a free Apify account.
npm install -g apify-cli # 1. install the CLIapify login # 2. authenticate (paste your API token)cd linkedin-jobs-scraper # 3. from this project folderapify push # 4. build & upload to your Apify account
apify push builds the Docker image on the platform and creates the Actor under your account. Open it in the Apify Console, run it from the auto-generated input form, and check the Dataset tab for results.
Run it locally first if you like:
npm installnpm run buildapify run -p # runs with your input.json against Apify proxy
Publish to the Store (to make it famous π): in the Console open your Actor β Publication β set a title, description, categories, and pricing β Publish. Full guide: https://docs.apify.com/platform/actors/publishing.
Memory for logged-in mode. The Actor ships on the Playwright + Chromium base image, so the logged-in browser layer works out of the box β no Dockerfile changes needed. When you enable the logged-in session, run it with 2 GB+ memory (choose it in the Console before Start). The public-only crawl runs fine on 512 MBβ1 GB.
π Project layout
.actor/actor.json # Actor metadatainput_schema.json # 30 typed inputs (4 secrets)dataset_schema.json # dataset views (overview + contacts)Dockerfile # HTTP-first build (Playwright note inside)src/main.ts # entrypoint: input β config β crawlerrouter.ts # SEARCH + JOB handlers, pagination, deep-splitconstants.ts # endpoints, filter maps, geoIdstypes.ts # typed input / recordsparsers/ # urlParser, guestList, guestDetail, companyParserlinkedin/voyager.ts # logged-in enrichment (best-effort)enrichment/ # companySite, hunter, apollo, orchestratorutils/text.ts # cleaning, salary parsing, helperssmoke-test.mjs # offline test of the parsing logic (npm run build first)
Run the offline tests any time: npm run build && node smoke-test.mjs.
π§― Reliability & troubleshooting
- Blocking / few results β use RESIDENTIAL proxy, lower
maxConcurrency, raiseminDelayMs. Datacenter IPs get blocked fast. - Missing hiring team / applicants β those need
useLoginSessionwith valid, fresh cookies. - Empty email/phone β nothing legitimate was published or matched; that is by design.
- Stopped at ~1,000 β make sure
deepPaginationis on, or add a tighter filter. - The crawler auto-retires blocked sessions and retries with a new proxy up to
maxRequestRetries.
βοΈ Compliance
This Actor is intended for lawful use on publicly available data. Scraping LinkedIn β especially while logged in β may conflict with LinkedIn's Terms of Service. You are responsible for how you use it, for respecting rate limits and robots directives, and for handling any personal data (recruiter names, profiles, enriched emails/phones) in line with applicable laws such as GDPR/CCPA. Enrichment uses official, opt-in provider APIs and first-party company data only.