LinkedIn Jobs Scraper avatar

LinkedIn Jobs Scraper

Pricing

Pay per usage

Go to Apify Store
LinkedIn Jobs Scraper

LinkedIn Jobs Scraper

Scrape LinkedIn jobs by keyword, location, company & filters — no login or cookies needed. Returns title, company, logo, location, salary, seniority, full description, applicants count and more. Fast, reliable, built for scale.

Pricing

Pay per usage

Rating

0.0

(0)

Developer

Osamih

Osamih

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Share

What does LinkedIn Jobs Scraper do?

Scrape LinkedIn job postings at scale — by keyword, location, company, industry, salary, seniority and more — without a LinkedIn account, login, or cookies. Every run returns clean, structured JSON: job title, company, logo, location, salary, seniority, employment type, applicant count, the full job description, and the direct job URL.

This Actor reads LinkedIn's public guest job pages, the same pages any logged-out visitor sees on linkedin.com/jobs. Nothing is scraped from behind a login, so there is no account to get restricted and no cookies to keep alive. Run it from the Apify Console, call it from your own backend through the Apify API, schedule it, hook it into Make, Zapier, Google Sheets, or Slack, and export results as JSON, CSV, Excel, HTML, or XML.

Why use LinkedIn Jobs Scraper?

  • Job boards and aggregators — keep a fresh index of openings in your niche, refreshed hourly or daily on a schedule.
  • Recruiting and talent intelligence — track which companies are hiring, for which roles, in which markets, and how fast postings accumulate applicants.
  • Market and salary research — collect posted salary ranges, seniority mixes, and remote-vs-onsite splits across an industry.
  • Lead generation — a company that is hiring five sales engineers is a company with budget; surface those signals early.
  • Job-seeker tools — power alerts for new postings that match a candidate's filters, including LinkedIn's "be an early applicant" (under 10 applicants) filter.

It is built on pure HTTP requests (Crawlee's CheerioCrawler, no headless browser), which makes it fast and inexpensive — a 60-job run finishes in about 35 seconds.

How to use LinkedIn Jobs Scraper

  1. Click Try for free / Start to open the Actor.
  2. Type a job title or keywords (e.g. Software Engineer) and a location (e.g. United States, London, Remote).
  3. Optionally narrow the search — date posted, remote/hybrid/on-site, employment type, experience level, company, industry, or minimum salary.
  4. Set Number of results to how many jobs you want.
  5. Click Save & Start, then watch live progress in the run's status message (Scraped 40/100 jobs).
  6. When the run finishes, open the Output tab and export as JSON, CSV, Excel, HTML, or XML.

Keep the default Apify residential proxy — LinkedIn blocks datacenter IPs aggressively, so residential is what makes runs reliable.

Input

The only thing you really need is a job title or a location. Everything else is an optional filter.

FieldTypeDefaultNotes
titlestring""Job title / keywords. Quoted phrases supported.
locationstring"United States"Free-text city, state, or country.
companyNamestring[][]Resolved to LinkedIn company IDs automatically; unresolvable names fall back to quoted keyword search.
companyIdstring[][]Numeric LinkedIn company IDs (f_C, e.g. 1441 for Google) for exact targeting when a name is ambiguous. OR-combined and merged with any resolved companyName.
publishedAtenum"""" any time, r2592000 month, r604800 week, r86400 24 h.
workTypeenum""1 on-site, 2 remote, 3 hybrid.
contractTypeenum""F/P/C/T/I/V/O = full-time/part-time/contract/temporary/internship/volunteer/other.
experienceLevelenum""1 internship … 5 director, 6 executive.
jobFunctionsstring[][]LinkedIn job-function codes (e.g. eng engineering, sale sales, it IT, mrkt marketing). OR-combined.
industryIdsstring[][]Numeric LinkedIn industry codes (e.g. 4 software development, 43 financial services). OR-combined.
titleIdsstring[][]Numeric LinkedIn title IDs for exact-title filtering (f_T). OR-combined.
minSalaryenum""19 = $40k+ … $200k+ (LinkedIn salary filter; mostly US).
easyApplybooleanfalseOnly Easy Apply jobs (apply directly on LinkedIn).
under10ApplicantsbooleanfalseOnly jobs with fewer than 10 applicants so far ("Be an early applicant").
distanceinteger0Search radius in miles around the location; 0 = LinkedIn default.
sortByenum""R relevance (default), DD most recent first. Changes which jobs LinkedIn returns; not a guarantee about row order.
postedWithinSecondsinteger0Custom freshness window (e.g. 3600 = past hour). Overrides publishedAt.
proxyobjectApify RESIDENTIALKeep residential — LinkedIn blocks datacenter IPs.
rowsinteger50Target number of jobs (max 2000). LinkedIn caps one search at ~1,000; above that the Actor splits the search across facets automatically.

At least one of title, location, companyName, or companyId must be non-empty, otherwise the run stops immediately with Input error: provide at least a job title, location, or company.

Example input

{
"title": "Software Engineer",
"location": "United States",
"rows": 50,
"publishedAt": "r604800",
"workType": "2",
"proxy": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}

Output

One dataset item per job. You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

{
"id": "4430579227",
"title": "Data Engineer",
"jobUrl": "https://www.linkedin.com/jobs/view/data-engineer-at-arlo-4430579227",
"companyName": "Arlo",
"companyUrl": "https://www.linkedin.com/company/arlo-health",
"companyId": "65615916",
"companyLogo": "https://media.licdn.com/dms/image/v2/.../company-logo_100_100/...",
"location": "New York, NY",
"postedTime": "8 hours ago",
"publishedAt": "2026-07-01",
"applicationsCount": "Over 200 applicants",
"description": "…full plain-text job description…",
"descriptionHtml": "<strong>About Arlo</strong><br>…raw HTML of the description…",
"contractType": "Full-time",
"experienceLevel": "Mid-Senior level",
"workType": "Information Technology",
"sector": "Insurance",
"salary": "$150,000.00/yr - $220,000.00/yr",
"applyUrl": null,
"applyType": "EXTERNAL",
"benefits": ["Medical insurance", "401(k)"],
"posterFullName": "Grace Boyle",
"posterProfileUrl": "https://www.linkedin.com/in/gracekboyle"
}

Data fields

FieldDescription
idLinkedIn job posting ID
titleJob title
jobUrlCanonical link to the posting
companyName, companyUrl, companyId, companyLogoHiring company details
locationJob location as shown by LinkedIn
postedTime, publishedAtRelative age (8 hours ago) and absolute date (YYYY-MM-DD)
applicationsCountApplicants so far, e.g. Over 200 applicants
description, descriptionHtmlFull job description as plain text and as HTML
contractType, experienceLevel, workType, sectorLinkedIn's job criteria
salaryPosted compensation range, where LinkedIn shows one
applyTypeEASY_APPLY (apply on LinkedIn) or EXTERNAL (apply on the company site)
benefitsListed benefits, where shown
posterFullName, posterProfileUrlThe person who posted the job, where shown

Field availability

LinkedIn does not publish every field for every posting, so some are null depending on the job. Measured on live runs: id, title, jobUrl, companyName, companyId, location, publishedAt, description and applyType come back ~100% of the time; salary roughly half of US searches; posterFullName / posterProfileUrl roughly a third; benefits mostly on US postings.

applyUrl is always null — the external application link is not present on LinkedIn's public pages, and reaching it would require a logged-in session, which this Actor deliberately avoids so that no account is ever put at risk. applyType still tells you reliably whether a job is Easy Apply or external, and jobUrl always takes you to the posting.

Row order is not guaranteed. Job detail pages are fetched concurrently and written as they complete, so sort on publishedAt client-side if you need ordering.

How much does it cost to scrape LinkedIn jobs?

Measured on a real 60-job run: $0.021 total, which works out to roughly $0.35 per 1,000 jobs (about 3.5 cents per 100). Residential proxy bandwidth is the dominant cost at ~68% of the total; the Actor itself uses about 0.01 compute units per 60 jobs and peaks at ~120 MB RAM.

Apify's free plan includes $5 of monthly usage, which is enough for roughly 10,000+ jobs per month at no cost. Your exact rate depends on your plan's residential proxy pricing and on how long the job descriptions are.

Using the Actor via API

Small jobs (rows ≤ ~100) — synchronous, returns items directly:

POST https://api.apify.com/v2/acts/osamih~linkedin-jobs-scraper/run-sync-get-dataset-items?token=<API_TOKEN>
Content-Type: application/json
{ "title": "Software Engineer", "location": "United States", "rows": 25 }

Large or bursty jobs — asynchronous run plus a webhook:

POST https://api.apify.com/v2/acts/osamih~linkedin-jobs-scraper/runs?token=<API_TOKEN>
{
"title": "Software Engineer", "location": "United States", "rows": 500,
"webhooks": [{ "eventTypes": ["ACTOR.RUN.SUCCEEDED", "ACTOR.RUN.FAILED"], "requestUrl": "https://your-app.example.com/apify-callback" }]
}

Then fetch results from GET https://api.apify.com/v2/datasets/{defaultDatasetId}/items. Keep your API token server-side only.

Run semantics for integrations:

  • Success with 0 items — the search legitimately matched nothing (Finished: no jobs found for this search).
  • Success with fewer than rows items — LinkedIn had fewer results. Not an error.
  • Non-zero exit — input or system error.

Tips and advanced options

  • Go faster and cheaper by setting rows to what you actually need; the run stops the moment it has them.
  • Monitor new postings with postedWithinSeconds (e.g. 3600 for the past hour) on a schedule, rather than re-scraping everything.
  • Target a specific employer with companyName. If the name is ambiguous, use companyId — open the company's LinkedIn page and copy the number from the URL.
  • Scrape more than 1,000 jobs by simply asking for them: LinkedIn caps a single search at ~1,000 results, so the Actor automatically splits the query across experience levels, work types, or employment types to get past that ceiling.
  • 1 GB of memory is plenty — peak usage is ~120 MB. Compute cost scales linearly with allocated memory, so raising it just multiplies your bill.

FAQ

Do I need a LinkedIn account or cookies? No. The Actor only reads publicly accessible job pages. There is no login, so there is no account to get restricted.

Is it legal to scrape LinkedIn jobs? This Actor collects only publicly available job-posting information — the same content any logged-out visitor can see. Scraping public data is generally lawful in many jurisdictions, but how you use the data is your responsibility, particularly under GDPR/CCPA where personal data (such as a job poster's name) is involved. Review LinkedIn's Terms of Service and consult your own legal counsel before commercial use. Do not use this Actor to collect personal data without a lawful basis.

Why did I get fewer jobs than I asked for? LinkedIn simply had fewer matching results. The run still succeeds and its status message says so. Broadening the location or removing filters usually helps.

Why is applyUrl empty? See Field availability above — that link is not exposed on LinkedIn's public pages.

Can it scrape LinkedIn profiles, companies, or posts? No. This Actor is scoped to job postings.

Support

Found a bug or need a field that isn't here? Open an issue on the Actor's Issues tab and it will be looked at. Feature requests and custom-scraper enquiries are welcome.

Development

npm install
npm test # unit tests (parsers, query planning)
apify run --purge # local run, reads storage/key_value_stores/default/INPUT.json
apify push # build & deploy

Architecture (see src/): main.ts wires input → proxy → crawler; routes.ts holds the two handlers (SEARCH pages enqueue DETAIL pages with a rolling 5-page lookahead; DETAIL pushes exactly rows items and stops the crawl); parsers.ts extracts data (schema.org ld+json first, CSS selectors as fallback); company.ts resolves company names via the guest typeahead endpoint; queryPlan.ts splits >1,000-row requests across search facets.

apify push does not update defaultRunOptions on an Actor that already exists — it only applies them at creation. If the Console shows a different memory than .actor/actor.json declares, every run silently costs the wrong amount. Check with apify api acts/<actorId> and correct it with:

$apify api PUT acts/<actorId> -d '{"defaultRunOptions":{"build":"latest","timeoutSecs":3600,"memoryMbytes":1024}}'