LinkedIn Jobs Scraper | Remove Duplicate Jobs | Pay Per Result
Pricing
from $0.35 / 1,000 results
LinkedIn Jobs Scraper | Remove Duplicate Jobs | Pay Per Result
LinkedIn Jobs Scraper | Remove Duplicate Jobs. The LinkedIn jobs scraper allows you to collect jobs in 2 ways: By providing one or more start URLs, or By entering multiple keywords, search queries. You can use either method individually or combine both.
Pricing
from $0.35 / 1,000 results
Rating
4.4
(42)
Developer
cheap_scraper
Maintained by CommunityActor stats
231
Bookmarked
52K
Total users
3.7K
Monthly active users
4.7 hours
Issues response
8 days ago
Last modified
Categories
Share
LinkedIn Jobs Scraper
Pay-per-result LinkedIn job scraper that allows you to collect job postings from LinkedIn using either keyword-based search or URLs-based search. With the option of removing duplicates!
Important: the 1,000-job limit applies per individual LinkedIn search, NOT per Actor run. You can provide multiple keywords, locations, or LinkedIn search URLs in a single run. Each search can return up to 1,000 jobs, so you can collect thousands of jobs in one run.
Concrete example: you start ONE run with these 3 searches:
| Search | Jobs returned (max) |
|---|---|
| "Software Engineer" in Toronto | up to 1,000 |
| "Software Engineer" in Montreal | up to 1,000 |
| "Data Scientist" in Vancouver | up to 1,000 |
| Total in one run | up to 3,000 |
You do not need 3 separate runs. The total can be lower if the same job appears in several searches and saveOnlyUniqueItems is enabled (it is saved only once), or if you set a lower maxItems.
Pricing
| Event Type | 🆓 Free Tier | 🥉 Bronze Tier | 🥈 Silver Tier | 🥇 Gold Tier |
|---|---|---|---|---|
| Result Event (per 1,000 jobs) | $0.7 | $0.60 | $0.50 | $0.35 |
Example costs:
- Scraping 500 jobs on Free Tier: $0.35
- Scraping 1,000 jobs on Bronze Tier: $0.60
- Scraping 5,000 jobs on Silver Tier: $2.50
💡 Tip: Enable saveOnlyUniqueItems to avoid paying for duplicate job postings and maximize your value!
💡 Company data enrichment is free: enabling enrichCompanyData does not increase the price per result. The only trade-off is that the run may take longer.
💡 Actor start fee: Each run also incurs a small fixed Apify actor start fee based on the memory size you choose: 0.005 dollars per GB. The default 4GB machine costs up to 0.02 dollars per run. This fee is charged once per run, regardless of how many jobs are scraped.
💡 Actor timeout: The actor automatically terminates after 25 minutes.
Tutorial
Watch the official tutorials to get started quickly:
📺 How to use the LinkedIn Jobs Scraper |
📺 Build a Free Automated LinkedIn Job Newsletter with Make.com & Apify |
📺 Build Any Apify Actor Input with AI: No Docs, No Code |
Community Tutorials
Made by the community using this actor:
- 📺 Build a LinkedIn Job Scraping & Lead Generation System with N8N, Apollo & Notion
- 📺 Scrape 1,000+ LinkedIn Jobs for 35 Cents Using Apify (Step-by-Step Guide)
- 📺 Scrape LinkedIn Jobs, Auto-Customize Your Resume with AI & Find Hiring Manager Emails (N8N)
- 📺 Turn 1,000 LinkedIn Jobs Into Sales Leads with an AI Outreach Assistant (N8N)
How It Works
The LinkedIn job scraper allows you to collect jobs in 2 ways:
- By providing one or more start URLs, or
- By entering keyword-based search queries.
You can use either method individually or combine both.
What happens when you combine several searches in one run?
- Every
startUrlsentry is its own search (up to 1,000 jobs each). keyword+locationsgenerate additional searches (one per keyword/location combination). They never modify yourstartUrls.- All results from all searches are collected in the same dataset.
- With
saveOnlyUniqueItems: true, a job found by several searches is saved only once.
Example: 3 URLs in startUrls + 2 keywords × 2 locations = 7 searches in one run, all merged into one dataset.
Three kinds of filters, three different behaviors:
| Type | Effect | Billed? |
|---|---|---|
| Keyword Search Filters (LinkedIn search filters) | Change which jobs LinkedIn returns | Only returned jobs are scraped |
Universal Filters (subLocationExclude, companyExclude, ...) | Skip jobs before their page is fetched | Skipped jobs are not billed |
| Dynamic Filters | Flag jobs after they are fetched (dynamicFilterMatch) | Every scraped job is saved and billed |
Important! AI-Powered Search Mode
⚠️ Important update: https://youtu.be/oD5FAQSPOCc
LinkedIn randomly serves either its classic search engine or a newer AI-powered search, regardless of whether you use URL-based or keyword-based search.
- Classic search: all filters (
workType/jobType/experienceLevel, plus the ones listed in Keyword Search Filters below) are enforced directly. - AI-powered search: LinkedIn ignores
workType/jobType/experienceLevelas filters. The scraper compensates by rewriting your search query in plain language instead (e.g."Remote Software Engineer"), since LinkedIn's AI search understands intent phrased this way.
How AI Search Query Rewriting Works
Only the keywords part of the search changes; location, time posted, and sort order are untouched. Where the rewrite gets its values depends on your search mode:
- Keyword-based search: uses the
workType/jobType/experienceLevelfield values you provided. - URL-based search: independent of any input fields. It's based on whatever
f_WT/f_JT/f_Eparameters are already present in the URL (e.g. because you applied those filters on linkedin.com before copying it).
The rule (applied independently, per filter):
- 1 value selected for that filter → bare label (e.g.
Remote) - 2+ values selected for that filter →
(Value1 OR Value2 OR ...)
Assembly order: [workType fragment] [jobType fragment] [experienceLevel fragment] [original keywords]
workType | jobType | experienceLevel | Rewritten query |
|---|---|---|---|
| Remote | — | — | Remote software engineer |
| Remote, Hybrid | — | — | (Remote OR Hybrid) software engineer |
| — | Full-time, Contract | — | (Full-time OR Contract) software engineer |
| — | — | Entry level, Associate | (Entry level OR Associate) software engineer |
| Remote | — | Entry level, Internship, Associate | Remote (Entry level OR Internship OR Associate) software engineer |
If you don't want the actor to rewrite your query:
- Keyword-based search: leave
workType/jobType/experienceLevelempty and phrase the intent directly inkeyword(e.g."Remote Entry Level Software Engineer"). - URL-based search: don't apply those filters on linkedin.com before copying the URL, phrase the intent in the LinkedIn search box instead, so the copied URL has no
f_WT/f_JT/f_Eparams for the scraper to react to.
Input
The LinkedIn scraper's input is divided into 7 sections:
🔍 Search
Use this section to tell the scraper where to start. You can provide LinkedIn job search URLs directly, use keywords to let the scraper build the URLs for you, or combine both.
📺 Watch this section explained in the tutorial video
How to get a LinkedIn job search URL:
- Open a private/incognito browser window (to avoid personalised results)
- Go to https://www.linkedin.com/jobs
- Search for a job title and apply any filters you want
- Copy the URL from the address bar and paste it in the Start URLs field
| Input name | Type | Description / Behavior | Example |
|---|---|---|---|
startUrls | { url: string }[] | One or more LinkedIn Job Search URLs to start crawling from. Use this when you want to set filters directly on LinkedIn in your browser. See the URL generation instructions above. | [{ "url": "https://www.linkedin.com/jobs/search?keywords=Developer&location=Canada&f_TPR=r604800" }] |
keyword | string[] | Job titles or search terms for keyword-based search. Combined with locations to generate one search URL per keyword/location combination. | ["Software Engineer", "Data Scientist"] |
📍 Keyword Search Filters
These filters are sent directly to LinkedIn's search engine as part of the search URL. They only apply to keyword-based search and have no effect on URL-based search.
📺 Watch this section explained in the tutorial video
| Input name | Type | Description / Behavior | Example |
|---|---|---|---|
locations | string[] | Locations to search in. Each location is paired with every keyword to generate one search URL per combination. Preferred over the legacy location field. | ["Montreal, Canada", "Toronto, Canada"] |
location | string | (Legacy) Single location string. Merged with locations when building searches. Use locations instead. | "Montreal, Canada" |
distance | string | Search radius in miles. Accepted values: "" (default), "5", "10", "15", "25", "50". | "25" |
publishedAt | string | Filter by posting date. Accepted values: "" (any time), "r86400" (last 24 h), "r604800" (last 7 days), "r2592000" (last 30 days). | "r604800" |
jobType | string[] | Filter by employment type. Multiple values allowed. Accepted values: "full-time", "part-time", "contract", "temporary", "internship", "volunteer". | ["full-time", "contract"] |
experienceLevel | string[] | Filter by experience level. Multiple values allowed. Accepted values: "internship", "entry-level", "associate", "mid-senior", "director", "executive". | ["entry-level", "associate"] |
workType | string[] | Filter by work location type. Multiple values allowed. Accepted values: "on-site", "remote", "hybrid". Search filter only, sent to LinkedIn. Not related to the output field workType. | ["remote", "hybrid"] |
companyInclude | string[] | Restrict the search to specific companies by name. Each name is resolved to a LinkedIn company ID — unresolved names are silently skipped. | ["Google", "Shopify"] |
filterUnder10Applicants | boolean | When true, only returns jobs with fewer than 10 applicants. | true |
filterEasyApply | boolean | When true, only returns Easy Apply jobs (apply directly on LinkedIn without leaving the site). | true |
🤖 AI search note: see 🤖 AI-Powered Search Mode above certain filters in this table stop working as real filters when LinkedIn serves its AI-powered search, and the scraper compensates with a query rewrite instead.
🔽 Universal Filters
These filters apply to both keyword-based and URL-based searches. Jobs that match an exclusion rule are skipped entirely and are not fetched or billed.
📺 Watch this section explained in the tutorial video
| Input name | Type | Description / Behavior | Example |
|---|---|---|---|
subLocationExclude | string[] | Skip jobs from specific cities or sub-areas. Any job whose location string contains the provided name (case-insensitive substring) is excluded before being fetched. | ["Laval", "Longueuil"] — skips any job in "Laval, Quebec, Canada" |
companyExclude | string[] | Skip jobs from specific companies. Exact case-insensitive match against the company name on the job card. | ["Randstad", "Adecco"] |
jobTitleExclude | string[] | Skip jobs whose title contains any of these keywords (whole-word, case-insensitive). "senior" excludes "Senior Software Engineer" but not "seniority". | ["senior", "manager", "director"] |
🔎 Dynamic Filters
📺 Watch this section explained in the tutorial video
⚠️ These are NOT LinkedIn search filters — they do not change which jobs LinkedIn returns to you. Jobs are always fetched first and billed, then evaluated against your criteria.
Works equally for both search modes. Because filtering happens after the job page is fetched, it applies regardless of whether you used URL-based search or keyword-based search.
Every job is still saved to the dataset. When a job does not match your dynamic filter criteria, it is still written to the dataset but the field dynamicFilterMatch will be set to false. Jobs that pass (or when no filters are set) will have dynamicFilterMatch set to true.
⚠️ Dynamic filters do NOT prevent jobs from being scraped or billed. To keep only matching jobs in Make.com, n8n, Zapier, Google Sheets or any other tool, filter the dataset on
dynamicFilterMatch = true.
One field for all filters. If you configure several dynamic filters, you only need to check dynamicFilterMatch once: it is the combined result of all of them (a job must pass every configured filter to get true).
Example: with { "excludeRecruitingAgencies": true, "requireSalaryInfo": true }, a job from a "Staffing and Recruiting" company and a job with no salary both get dynamicFilterMatch: false. Only a job from a non-staffing company that lists a salary gets true.
How excludeRecruitingAgencies works: it flags a job with dynamicFilterMatch: false only when the company's LinkedIn industry (companyIndustry) is exactly "Staffing and Recruiting". Any other industry passes, and it does not analyze the company name, description or job title. For example, a company named "ABC Staffing" whose LinkedIn industry is "Human Resources Services" is not excluded. If the company industry is unavailable, the job passes.
How jobIndustryExclude works: it checks the job's industry (the output field sector), not the company's. Several values are allowed, and a match on any of them gives dynamicFilterMatch: false:
{"jobIndustryExclude": ["Retail Apparel and Fashion", "Advertising Services"]}
Industry names must match LinkedIn's classification names exactly.
| Input name | Type | Description / Behavior | Example |
|---|---|---|---|
jobIndustryInclude | string[] | Keep only jobs whose industry matches one of these values. Takes priority over jobIndustryExclude when both are set. Values must match LinkedIn names exactly (e.g. "Software Development"). You can find all the Linkedin industries at full list | ["Software Development", "IT Services and IT Consulting"] — only those industries get dynamicFilterMatch: true |
jobIndustryExclude | string[] | Flag jobs whose industry (the output field sector, i.e. the job's industry, not the company's) matches any of these values as dynamicFilterMatch: false. Ignored when jobIndustryInclude is also set. You can find all the Linkedin industries at full list | ["Staffing and Recruiting"] — agency-posted jobs get dynamicFilterMatch: false |
excludeRecruitingAgencies | boolean | When true, jobs whose company has the LinkedIn industry "Staffing and Recruiting" (companyIndustry) are flagged with dynamicFilterMatch: false. It does not analyze the company name or description. Automatically enables enrichCompanyData. | true — result: { "companyIndustry": "Staffing and Recruiting", "dynamicFilterMatch": false } |
companySizeMin | number | Flag jobs from companies with fewer employees than this value as dynamicFilterMatch: false. Companies with no employee count on LinkedIn always pass. Automatically enables enrichCompanyData. | 50 — companies with fewer than 50 employees get dynamicFilterMatch: false |
companySizeMax | number | Flag jobs from companies with more employees than this value as dynamicFilterMatch: false. Companies with no employee count on LinkedIn always pass. Automatically enables enrichCompanyData. | 500 — to target mid-size companies combine with companySizeMin: { "companySizeMin": 50, "companySizeMax": 500 } |
companyOrganizationTypeInclude | string[] | Keep only jobs from companies whose company page organization type matches one of these values. Takes priority over companyOrganizationTypeExclude when both are set. Automatically enables enrichCompanyData. If unavailable, the job always passes. Accepted values: "Public Company", "Educational", "Self Employed", "Government Agency", "Non Profit", "Self Owned", "Privately Held", "Partnership". See full list | ["Privately Held", "Public Company"] |
companyOrganizationTypeExclude | string[] | Flag jobs from companies whose company page organization type matches any of these values as dynamicFilterMatch: false. Ignored when companyOrganizationTypeInclude is set. Automatically enables enrichCompanyData. If unavailable, the job always passes. See full list | ["Self Employed", "Self Owned"] |
companyFoundedDateMin | number | Keep only jobs from companies founded in or after this year. Automatically enables enrichCompanyData. If the founding year is unavailable, the job always passes. | 2010 |
companyFoundedDateMax | number | Keep only jobs from companies founded in or before this year. Automatically enables enrichCompanyData. If the founding year is unavailable, the job always passes. Combine with companyFoundedDateMin to target a range. | 2020 — to target companies founded between 2010–2020: { "companyFoundedDateMin": 2010, "companyFoundedDateMax": 2020 } |
companyFollowersCountMin | number | Keep only jobs from companies with at least this many LinkedIn followers. Automatically enables enrichCompanyData. If the follower count is unavailable, the job always passes. | 10000 |
companyFollowersCountMax | number | Keep only jobs from companies with at most this many LinkedIn followers. Automatically enables enrichCompanyData. If the follower count is unavailable, the job always passes. | 500000 |
requireRecruiterProfile | boolean | When true, only jobs that have a recruiter/poster profile associated with the posting get dynamicFilterMatch: true. It only checks that a poster is shown; it does not verify the person's hiring authority. Jobs without a poster are still collected and billed, but get dynamicFilterMatch: false. | true |
requireSalaryInfo | boolean | When true, only jobs that include salary information in the posting get dynamicFilterMatch: true. Jobs without salary info are still collected and billed. | true |
📊 Job Match Scoring
📺 Watch this section explained in the tutorial video
⚠️ This is NOT a search filter — it does not change which jobs LinkedIn returns to you.
This is a profile matching tool for smarter job hunting. Paste the skills from your resume/CV here, and the scraper will automatically score every job description against your profile. Instead of reading through dozens of postings, you instantly see which jobs you're actually qualified for — and can skip the rest entirely.
| Input name | Type | Description / Behavior | Example |
|---|---|---|---|
resumeKeywords | { keyword: string, aliases?: string[] }[] | Array of skills from your resume/CV. Each entry requires a keyword and optionally aliases for abbreviations or alternate names. Matching is case-insensitive with smart word boundaries — "JS" won't match "JSON", "C++" is handled correctly. When set, every output job gains three extra fields: matchedKeywords (found skills), unmatchedKeywords (missing skills), and keywordMatchScorePercentage (0–100 score, computed as round(matched ÷ total × 100)). | [{ "keyword": "JavaScript", "aliases": ["JS"] }, { "keyword": "Python" }] |
Full example:
[{ "keyword": "JavaScript", "aliases": ["JS"] },{ "keyword": "Kubernetes", "aliases": ["K8s"] },{ "keyword": "C++" },{ "keyword": "Project Management", "aliases": ["PM"] },{ "keyword": "Python" }]
Matching is case-insensitive and uses smart word boundaries — for example, JS won't accidentally match JSON, and C++ is handled correctly despite containing symbols.
🤖 Generate Skills from Your CV Automatically
Instead of manually listing your skills, let an AI extract them from your CV and output them in the exact format above:
| LLM | Link | Note |
|---|---|---|
| ChatGPT | Open with prompt → | No login needed |
| Claude | Open with prompt → | Login required |
Or copy this prompt and paste it into any LLM of your choice:
Extract skills and keywords from my CV for job matching. Add common abbreviations as aliases.Output ONLY a raw JSON array with no explanation or markdown, using this format:[{"keyword":"JavaScript","aliases":["JS"]},{"keyword":"Python"}].I will paste my CV after this message.
The AI will output a JSON array — copy and paste it directly into the resumeKeywords field.
⚙️ Run Settings
General settings that control how the scraper runs. These apply to both URL-based and keyword-based search.
📺 Watch this section explained in the tutorial video
| Input name | Type | Description / Behavior | Example |
|---|---|---|---|
maxItems | number | Maximum number of job listings to scrape across all searches in the run (not per URL). Example: maxItems: 500 with five search URLs gives 500 results in total, not 500 per URL. Leave empty for no limit. When using pay-per-result billing, a minimum of 150 results is required. | 500 |
saveOnlyUniqueItems | boolean | When true, the same LinkedIn job ID is saved only once within a single run, including duplicates found across different search URLs, keywords and locations. Duplicates are skipped entirely (not flagged, not fetched, not billed). Deduplication does not persist across separate runs: to avoid duplicates across runs, store the jobId values you already have and compare new results against them. Default is false. | true |
enrichCompanyData | boolean | When true, visits each company's LinkedIn page to fetch extra fields: companyDescription, companyAddress, companyEmployeeCount, companyEmployeeCountRange, companyWebsite, companyIndustry, companyOrganizationType, companyFoundedDate, companySpecialties, companyFollowersCount, companyOfficeLocations, companyAffiliatedPages, and companyRecentPosts. Default is false. Included at no additional charge: it does not change the price per result, the only trade-off is a longer run time because company pages are visited. | true |
⚠️ Deprecated Filters
Job Function filters (jobFunctionInclude, jobFunctionExclude) are no longer usable. LinkedIn no longer exposes the job function, so it cannot be scraped and the output field workType is always empty. Do not use these two filters.
The other deprecated filters below are currently unreliable. They may or may not work depending on the search engine LinkedIn serves. When LinkedIn serves its new AI-powered search, these filters are silently ignored. They will be restored once LinkedIn supports them again. Using them will not cause any errors, but results may include jobs that do not match the filter.
| Input name | Type | Description / Behavior | Example |
|---|---|---|---|
salaryBase | string | Deprecated - currently only works when LinkedIn serves its classic search engine; in AI-powered search LinkedIn ignores it and it cannot be verified after scraping. Minimum salary filter. Accepted values: "" (any), "40000", "60000", "80000", "100000", "120000". | "80000" |
jobFunctionInclude | string[] | Deprecated, no longer usable. LinkedIn no longer exposes the job function on public job pages, so it cannot be scraped (the output field workType is always empty). Using it flags every job with dynamicFilterMatch: false. | ["Engineering"] |
jobFunctionExclude | string[] | Deprecated, no longer usable. Same reason as above: the job function cannot be scraped, so this filter has no effect (no job is ever flagged). | ["Sales"] |
Input Sample
{"enrichCompanyData": true,"keyword": ["Full Stack Developer"],"locations": ["Montreal, Canada"],"publishedAt": "r86400","resumeKeywords": [{"aliases": ["TS", "JS", "JavaScript"],"keyword": "TypeScript"},{"aliases": ["Node", "NodeJS"],"keyword": "Node.js"},{"aliases": ["RN", "React Native", "Expo", "ReactJS"],"keyword": "React"},{"aliases": ["PostgreSQL"],"keyword": "SQL"},{"aliases": ["Git","Docker","Jira","Postman","GitHub","GitLab","GitHub Copilot","Webpack"],"keyword": "Tools"}],"saveOnlyUniqueItems": true,"jobTitleExclude": ["senior", "manager", "director", "lead", "principal"]}
Note: You can use either startUrls or keyword (with locations), or combine both methods in a single run.
Output Sample
Output Fields
| Field | Type | Description |
|---|---|---|
jobId | string | null | Unique LinkedIn job identifier. |
jobTitle | string | null | Job position title. |
location | string | null | Job location as text (e.g. "Montreal, Quebec, Canada"). |
salaryInfo | string[] | Salary range as formatted strings (e.g. ["$69100.00", "$129200.00"]). Empty array if not listed. |
postedTime | string | null | Human-readable relative posted time (e.g. "2 days ago"). |
publishedAt | string | null | ISO 8601 date when the job was published (e.g. "2026-06-14T00:00:00.000Z"). |
searchString | string | null | The search query that found this job (e.g. "Software Engineer - Montreal, Canada"). |
jobUrl | string | null | LinkedIn job posting URL. |
companyName | string | null | Company name. |
companyUrl | string | null | LinkedIn company page URL (not the company's own website, see companyWebsite). |
companyLogo | string | null | Company logo image URL. |
companyId | string | null | LinkedIn internal company identifier. |
jobDescription | string | null | Full job description as plain text. |
applicationsCount | string | null | Number of applications received (e.g. "Over 200 applicants"). |
contractType | string | null | Employment type (e.g. "Full-time", "Contract"). |
experienceLevel | string | null | Required experience level as listed on the posting (e.g. "Mid-Senior level"). |
yearsOfExperience | { years: string, context: string, lang: string }[] | Structured experience requirements extracted from the job description. years is a normalized value (e.g. "5+", "2-5"). Empty array if none found. |
workType | string | null | Deprecated, always empty. It used to contain the job function (e.g. "Engineering and Information Technology"), but LinkedIn no longer exposes it. Not related to the input workType filter (remote/hybrid/on-site). The Job Function dynamic filters depend on it and are deprecated too. |
sector | string | null | Industry sector of the job (e.g. "Software Development"). |
applyUrl | string | null | URL to apply for the job. May differ from jobUrl for Easy Apply jobs. |
applyType | string | null | Application method: "EASY_APPLY" (apply on LinkedIn) or "EXTERNAL" (redirects to company site). |
posterFullName | string | null | Name of the person associated with the job posting, when LinkedIn shows one. May be a recruiter, hiring manager or another employee; it is not verified to be the hiring decision-maker. Empty string if not shown. |
posterProfileUrl | string | null | LinkedIn profile URL of the person associated with the posting (see posterFullName). Empty string if not shown. |
dynamicFilterMatch | boolean | true when no dynamic filters are configured or all configured filters passed. false when the job did not meet one or more filter criteria — the job is always saved regardless. |
matchedKeywords | string[] | Skills from resumeKeywords that were found in the job description. Only present when resumeKeywords is set. |
unmatchedKeywords | string[] | Skills from resumeKeywords that were NOT found in the job description. Only present when resumeKeywords is set. |
keywordMatchScorePercentage | number | null | Percentage of resume skills matched (0–100). Computed as round(matched ÷ total × 100). Only present when resumeKeywords is set. |
companyDescription | string | null | Company description from the LinkedIn About section. Only present when enrichCompanyData: true |
companyAddress | object | null | Headquarters address with fields: streetAddress, addressLocality, addressRegion, postalCode, addressCountry. Only present when enrichCompanyData: true. |
companyEmployeeCount | number | null | Exact employee count as shown on LinkedIn (e.g. 1019). Only present when enrichCompanyData: true. |
companyEmployeeCountRange | string | null | Employee count range label (e.g. "201-500 employees"). Only present when enrichCompanyData: true. |
companyWebsite | string | null | External website of the company, as listed on its LinkedIn company profile (unlike companyUrl, which is the LinkedIn page). Empty if the company has not provided one. Only present when enrichCompanyData: true. |
companyIndustry | string | null | Industry category from the LinkedIn About section (e.g. "Software Development"). Only present when enrichCompanyData: true. |
companyOrganizationType | string | null | Organisation type (e.g. "Privately Held", "Public Company"). Only present when enrichCompanyData: true. |
companyFoundedDate | string | null | Year the company was founded (e.g. "2019"). Only present when enrichCompanyData: true. |
companySpecialties | string[] | List of company specialties from the About section. Only present when enrichCompanyData: true. |
companyFollowersCount | number | null | Number of LinkedIn followers the company has. Only present when enrichCompanyData: true. |
companyOfficeLocations | string[] | List of office address strings from the company page. Only present when enrichCompanyData: true. |
companyAffiliatedPages | string[] | Names of affiliated LinkedIn company pages. Only present when enrichCompanyData: true. |
companyRecentPosts | { text: string, datePublished: string, url: string }[] | Recent posts published by the company on LinkedIn. datePublished is ISO 8601. Only present when enrichCompanyData: true. |
[{"jobId": "4354494117","jobTitle": "Full-Stack Software Engineer, Inference","location": "Montreal, Quebec, Canada","salaryInfo": [],"postedTime": "6 hours ago","publishedAt": "2026-06-14T00:00:00.000Z","searchString": "Full Stack Developer - Montreal, Canada","jobUrl": "https://ca.linkedin.com/jobs/view/full-stack-software-engineer-inference-at-cohere-4354494117?trk=public_jobs_topcard-title","companyName": "Cohere","companyUrl": "https://ca.linkedin.com/company/cohere-ai?trk=public_jobs_topcard-org-name","companyLogo": "https://media.licdn.com/dms/image/v2/C560BAQGI_GYoDekJsQ/company-logo_200_200/company-logo_200_200/0/1680024442682/cohere_ai_logo?e=2147483647&v=beta&t=YjvCYqodFatkvEoqaggfjwCzFvzvDUFjIo5zpvlogxk","jobDescription": "Who are we?\n\nCohere is the leading security-first enterprise AI company. We build cutting-edge foundation AI models and end-to-end products that are designed to solve real-world business problems.\n\nWe’re training and deploying frontier models for enterprises who are building AI systems. We believe that our work is instrumental to the widespread adoption of AI and we are looking for folks that want to be part of that.\n\nWe obsess over what we build. Each one of us is responsible for contributing to increasing the capabilities of our models and the value they drive for our customers. Cohere is a team of researchers, engineers, designers, and more, who are all passionate about their craft.\n\nWe are a global technology company co-headquartered in Toronto and San Francisco, with key offices in London, New York City, Montreal, Seoul, Germany and Paris. Join us!\n\nWhy this role?\n\nCohere customers self-serve our API without any intervention. This team unlocks the complex technology we build for customers to understand, trust, and pay for.\n\nAs a Senior Software Engineer, You Will\n\nImprove the platform’s auth, billing, and payment systems\nAdd new features to the interactive Playground where customers can try our models\nImplement new platform features for managing deployments\nWrite and ship minimal code that runs in low-resource environments, and has highly stringent deployment mechanisms\nAs security and privacy are paramount, you will sometimes need to reinvent the wheel, and won’t be able to use the most popular libraries or tooling\n\nYou May Be a Good Fit If\n\nYou have 5+ years of experience writing clean backend code. Our stack includes: Golang and React.\nYou’ve built payment systems and have experience with subscription or usage-based SaaS, and/or products with a freemium model.\nYou have strong coding abilities and are comfortable working across the stack. You’re able to read and understand, and even fix issues outside of the main code base.\nYou’ve worked in both large enterprises and startups.\nYou excel in fast-paced environments and can execute while priorities and objectives are a moving target.\n\nHow And Where We Work\n\nCohere is remote-friendly. We have offices in Toronto, San Francisco, New York City, London, Paris, Montreal, and more coming soon.\nFor those in the office: a daily lunch program, plenty of snacks, and regular community and social events.\nFor those not near an office: a co-working benefit so you can work alongside others in your city.\n\nIf any of the above doesn’t line up exactly with your experience, we still encourage you to apply.\n\nWe strive to create an inclusive work environment for all; we welcome applicants from all backgrounds and are committed to providing equal opportunities. Should you require any accommodations during the recruitment process, please submit an Accommodations Request Form, and we will work together to meet your needs.\n\nWe may use AI-enabled tools to screen and assess applicants against the criteria for this position. This helps our recruiters identify potentially qualified candidates, but it doesn't limit the applications our recruiters may review or consider. \nShow more Show less","applicationsCount": "Over 200 applicants","contractType": "Full-time","experienceLevel": "Not Applicable","yearsOfExperience": [{"years": "5+","context": "writing clean backend code","lang": "en"}],"workType": "","sector": "Software Development","posterFullName": "","posterProfileUrl": "","companyId": "24024765","applyUrl": "https://ca.linkedin.com/jobs/view/full-stack-software-engineer-inference-at-cohere-4354494117?trk=public_jobs_topcard-title","applyType": "EXTERNAL","dynamicFilterMatch": true,"matchedKeywords": ["React","Tools"],"unmatchedKeywords": ["TypeScript","Node.js","SQL"],"keywordMatchScorePercentage": 40},...]
With enrichCompanyData: true
When you enable enrichCompanyData, each result gets the following additional fields fetched from the company's LinkedIn page:
{"companyAddress": {"streetAddress": "171 John St","addressLocality": "Toronto","addressRegion": "Ontario","postalCode": "M5T 1X3","addressCountry": "CA"},"companyDescription": "Cohere is the leading security-first enterprise AI company. We build cutting-edge foundation models and end-to-end AI products designed to solve real-world business problems. We partner closely with companies to deliver seamless integration, full customization, and easy-to-use solutions for their workforce and customers. Our all-in-one platform offers enterprises the highest levels of data security, privacy and optionality to deploy across all major cloud providers, private cloud environments, or on-premises. \n\nHQ: 171 John Street, 2nd Floor, Toronto, ON M5T 1X3","companyEmployeeCount": 1019,"companyWebsite": "https://cohere.com","companyIndustry": "Software Development","companyEmployeeCountRange": "201-500 employees","companyOrganizationType": "Privately Held","companyFoundedDate": "2019","companySpecialties": ["Natural Language Processing","Machine Learning","and Artificial Intelligence"],"companyFollowersCount": 253257,"companyOfficeLocations": ["171 John St 2nd Floor Toronto, Ontario M5T 1X3, CA","San Francisco, California, US","London, GB","New York, NY, US","Paris, FR","Montreal, CA","Seoul , KR"],"companyAffiliatedPages": ["Cohere Labs"],"companyRecentPosts": [{"text": "Four days ago, Cohere co-founder Nick Frosst warned about the dangers of subscriptions to proprietary LLMs on MTS Live:\n\n\"We released [North Mini Code] because we think this technology needs to be sovereign. It needs to be owned, and controlled, by the people who use it.\"","datePublished": "2026-06-14T20:13:09.311Z","url": "https://www.linkedin.com/posts/cohere-ai_nick-frosst-on-mts-live-activity-7472018313511092224-7lKZ"},{"text": "When you rent your artificial intelligence, you have no control, and no choice. This is why sovereignty and ownership matters. \n\nWhether it means using your own hardware, open source, or deep customization. Own your AI, own your future.","datePublished": "2026-06-13T15:49:47.946Z","url": "https://www.linkedin.com/posts/cohere-ai_when-you-rent-your-artificial-intelligence-activity-7471589649916887040-61hN"},...]}
Advanced Workflows & Integrations
Collecting 3,000+ unique jobs in one run
Each search returns at most 1,000 jobs, so use several different searches in one run:
{"keyword": ["Backend Developer", "Software Engineer"],"locations": ["Toronto, Canada", "Montreal, Canada", "Vancouver, Canada"],"publishedAt": "r2592000","saveOnlyUniqueItems": true,"maxItems": 5000}
This creates 6 searches (2 keywords × 3 locations). Set maxItems high enough for the total you want.
Avoid duplicates across runs (Make.com / n8n / Zapier)
saveOnlyUniqueItems only works inside one run. To avoid duplicates between scheduled runs:
- Apify – Get Dataset Items
- Filter – keep only
dynamicFilterMatch = true(if you use dynamic filters) - Google Sheets / database – search for the row where
jobIdequals the job'sjobId - If not found – add a new row
Lead generation & decision-maker enrichment
The actor provides job and company data. It does not identify or verify decision-makers and does not find contact emails. For lead generation, send these fields to your own downstream enrichment service: jobTitle, jobDescription, companyName, companyId, companyWebsite (needs enrichCompanyData: true) and, when available, posterFullName / posterProfileUrl. Finding emails requires a separate service.
Who Is This Actor For?
This LinkedIn Jobs Scraper is perfect for:
-
Recruiters & HR Professionals: Build talent pipelines by collecting thousands of job postings from competitors. Identify hiring trends, salary ranges, and in-demand skills in your industry.
-
Job Seekers: Aggregate job opportunities across multiple searches into one organized dataset. Track new postings, compare salaries, and find direct application links without manually browsing LinkedIn.
-
Market Researchers: Analyze employment trends, skill requirements, and company hiring patterns. Gather data on job market dynamics, remote work adoption, or industry-specific hiring activity.
-
Business Intelligence Teams: Monitor competitors' hiring activities, understand workforce expansion strategies, and identify market opportunities based on job posting data.
-
Data Analysts: Extract structured job market data for analysis, visualization, and reporting. Build dashboards tracking hiring trends over time.
Key Benefits:
- ✅ Pay only for results: Fair pricing model - you're charged based on actual jobs scraped
- ✅ No coding required: Simple web interface with clear input options
- ✅ Duplicate prevention: Optional unique-only mode saves you money
- ✅ Rich company data: Optional enrichment adds company details, addresses, and employee counts at no extra charge
- ✅ Flexible search: Use LinkedIn URLs or keyword-based searches, or combine both
- ✅ Ready-to-use data: JSON/CSV/Excel exports for immediate analysis
- ✅ Automation & pipeline-ready: Dynamic filters (
dynamicFilterMatch) and profile match scoring (keywordMatchScorePercentage) act as cheap pre-qualification steps before passing jobs to any downstream service — use them to cut noise in n8n / Make.com workflows, reduce LLM token costs, enrich only relevant leads in a CRM, trigger data augmentation APIs selectively, or power any pipeline where processing volume directly affects cost
FAQ
Will my LinkedIn account get blocked or banned?
No. The scraper only accesses LinkedIn's public job pages — the same pages anyone can view without logging in. Your LinkedIn account is never used or required.
Do I need a LinkedIn account to use this?
No. The scraper only reads publicly available job listings. No login, no LinkedIn account needed at all.
Is it legal to scrape LinkedIn job postings?
Yes. This scraper only collects data from LinkedIn's public pages that are accessible to anyone without an account. It does not bypass any login or access private data.
How many jobs can I scrape?
Up to 1,000 jobs per search (per keyword/location combination or per LinkedIn search URL). This is a LinkedIn limitation, not the scraper's, and it is not a limit per run. Put several keywords, locations or URLs in one run to collect thousands of jobs, for example 3 different searches can return up to 3,000 jobs in one run (subject to maxItems): for example "Software Engineer" in Toronto + "Software Engineer" in Montreal + "Data Scientist" in Vancouver, each up to 1,000 jobs, all in a single run.
Can I export the results to Excel or Google Sheets?
Yes. Apify lets you download your results in CSV, Excel, or JSON format directly from the dashboard — no coding required.
Do I need coding skills to use this?
No. Everything is configured through a simple form on Apify. Just fill in your keywords, location, and filters and click Run.
How much does it cost to scrape 1,000 jobs?
As low as $0.35 for 1,000 jobs on the Gold tier. There is no flat monthly fee.
Can I run it automatically on a schedule?
Yes. Apify has a built-in scheduler. Set it to run daily, weekly, or at any interval and get fresh job data automatically without any manual work.
Can I connect it to Make.com, Zapier, or n8n?
Yes. Apify integrates natively with Make.com, Zapier, and n8n so you can automatically send job results to Google Sheets, Slack, a CRM, or any other tool in your workflow.
Can I scrape jobs from specific companies only?
Yes. Use the companyInclude input field to restrict results to a list of company names. The scraper will resolve each name to a LinkedIn company ID and return only their job postings.


