LinkedIn Jobs Scraper — Descriptions, Salaries, Seniority avatar

LinkedIn Jobs Scraper — Descriptions, Salaries, Seniority

Pricing

$4.00 / 1,000 delivered jobs

Go to Apify Store
LinkedIn Jobs Scraper — Descriptions, Salaries, Seniority

LinkedIn Jobs Scraper — Descriptions, Salaries, Seniority

LinkedIn jobs (linkedin.com) public job search: title, company, location, posted date, description, salary with evidence, seniority, employment type, industries, workplace. By keywords and location; one-off export or monitor of new and changed jobs. 20 free previews. Pay per result, no login.

Pricing

$4.00 / 1,000 delivered jobs

Rating

0.0

(0)

Developer

architek.earth

architek.earth

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Categories

Share

Recorded run: 2 jobs from 1 search in 20 seconds (2026-10-22, build 0.1.13, run s6ed2iEy2tgqCIHcR). A bounded canary sample, not a full-catalogue benchmark.

architek.earth — an independent data studio. Pay-per-result scrapers with recorded runs, published canaries and stable schemas, built for people and AI agents.

LinkedIn public job listings with descriptions, salaries, seniority, employment type and company from linkedin.com, by keywords and location, as a one-off export or a saved monitor that returns only new and changed jobs. No LinkedIn login or cookies.

SourceMarketFromStandout fields
LinkedIn public job search (linkedin.com)Any location LinkedIn search accepts$4.00 / 1,000 jobsdescription, salaryComponents, seniorityLevel / employmentType, changeType

✅ Why choose this scraper

LinkedIn job descriptions in every paid row

Every paid row carries the full description with detailStatus: "COMPLETE", plus employmentType, seniorityLevel and industries from the job page, at the single job price. There is no separate detail event.

LinkedIn salaries with the text they came from

salaryRaw keeps the salary text LinkedIn shows, and salaryComponents splits it into min, max, currency, period, kind (BASE, OTE or UNSPECIFIED) and the evidence string it was read from. A run on 2026-10-22 returned £37,000.00/yr - £60,000.00/yr as 37000-60000 GBP per YEAR.

Only new or changed LinkedIn jobs on a schedule

In paid mode a named monitor keeps a baseline; with refreshMonitor: true a run returns only jobs that are NEW or CHANGED (with changedFields showing previous and current values). A repeat refresh on 2026-10-20 found 10 unchanged jobs, returned 0 rows and charged 0 events (run j7NlnJuMVRZJOompQ).

Filter LinkedIn jobs by remote, seniority and employment type

workplace, seniority_levels and employment_types are checked against the job page, not only the search card. A job whose value is unknown is left out when a filter needs it, and counted as unknown in RUN_SUMMARY.

Measured on a schedule

A canary task runs a fixed software engineer / New York, United States search on the live site every day and records rows and seconds; the hero line above is its latest green run.

⚠️ Worth knowing up front

  • Public search results are not guaranteed exhaustive, chronological or identical between requests. This is not LinkedIn Recruiter or a logged-in search export, and there are no applicant counts, recruiter contacts or job-poster names.
  • Salary fields are filled only when the posting publishes pay. A bare $ does not establish USD, so currency can be null; hourly figures are not annualized, and OTE is never treated as base pay.
  • workplace is set only from explicit phrases such as "this role is fully remote"; otherwise it is UNKNOWN, even when the description mentions hybrid working. A remote, hybrid or on-site filter therefore excludes some genuinely matching jobs.
  • Missing jobs are never marked closed. A repost under a new job ID is a separate job.
  • Each LinkedIn page is tried 3 times. The actor does not yet recognise the end of LinkedIn's results, so a search with no matches, or a scan that runs past the last results page, ends as BLOCKED (nothing delivered) or PARTIAL (rows kept and charged) rather than complete. A run that reaches max_rows or max_pages ends LIMITED in RUN_SUMMARY; that is a deliberate cap, not full coverage.
  • Invalid input (INVALID_INPUT), a monitor conflict such as a resume with no unfinished scan (STATE_CONFLICT), used-up free previews (PREVIEW_LIMIT), a LinkedIn page still failing after retries (BLOCKED or PARTIAL) and the run deadline 60 seconds before the timeout (TIME_LIMIT) each end the run with one free status row ("recordType": "status"). Successful runs write no status row; their outcome is in the RUN_SUMMARY record. If a row's write or charge cannot be confirmed (DELIVERY_UNCERTAIN, CHARGE_RECONCILIATION_REQUIRED), the run fails and the next run on that monitor stops instead of delivering it again.

⚡ Try the example

Each account gets 20 free preview runs: one search page, at most 10 basic job cards (title, company, location, URL, posted date), no descriptions or salaries, and no charge. Preview runs never switch to paid mode.

Open the actor's input page, switch to JSON, paste this and start the run:

{
"mode": "preview",
"keywords": "software engineer",
"location": "New York, United States",
"max_rows": 10
}

For descriptions and salaries, set "mode": "export" (a new collection every run) or "mode": "paid" with a monitor name (saved progress and change tracking); 2 jobs cost $0.008.

⚙️ Input

ParameterTypeDefaultDescription
modeselectpreview (prefill export)preview: free, one page of up to 10 basic cards. export: an independent detailed export each run. paid: a saved monitor with resume and change tracking.
keywordsstring— (required)Job-search text sent to LinkedIn, up to 200 characters.
locationstring— (required)Location sent to LinkedIn, interpreted by LinkedIn; not an exact geographic boundary.
monitorstringdefaultSaved monitor name in paid mode, private to your account; 1-64 letters, digits, underscores or hyphens. Query and filters must stay the same for one monitor; use a new name for a new search.
refreshMonitorbooleanfalsepaid mode only. Start again from page one against the saved baseline and return only new and changed jobs. Cannot be combined with continuePreviousRun; abandons any unfinished scan.
continuePreviousRunbooleanfalsepaid mode only. Resume this monitor's unfinished scan; job IDs already processed in that scan are skipped.
max_rowsinteger100 (prefill 1)Maximum delivered rows this run, 1-10,000. Previews stop at 10.
max_pagesinteger10 (prefill 1)Search pages per scan, 1-100. Raise it when resuming a scan that stopped at PAGE_LIMIT.
workplaceselectanyany, remote, hybrid or on-site, checked against the job description. Not available in preview.
employment_typesarray[]Allowed employment types, for example Full-time, matched exactly ignoring case. Not available in preview.
seniority_levelsarray[]Allowed seniority levels, for example Mid-Senior level; Not Applicable counts as unknown. Not available in preview.
title_termsarray[]Every term must occur in the job title, ignoring case.
exclude_companiesarray[]Company names to leave out, matched exactly ignoring case.
feedbackstring—Optional note for the maintainers. It is logged and read by a human, and it never changes output.

Each array takes up to 30 values of up to 100 characters. Only max_rows, max_pages, refreshMonitor and feedback may change between runs of one monitor.

🧱 Stable fields

These top-level fields keep the same name and the same kind of value in every release. Fields are only ever added, never renamed or removed, so an n8n, Make, Zapier or spreadsheet pipeline can bind to them.

FieldTypeMeaning
id / urlstringLinkedIn job ID and job page URL.
title / company / locationstringJob title, company name and location from the search card.
postedDatestring, nullablePosted date LinkedIn publishes (YYYY-MM-DD).
descriptionstring, nullableFull job description; null on preview rows.
detailStatusstringCOMPLETE on paid and export rows, NOT_FETCHED on preview rows.
employmentType / seniorityLevel / industriesstring, nullableJob criteria from the job page.
workplace / workplaceEvidencestring / arrayremote, hybrid, on-site or UNKNOWN, and the phrases it was read from.
salaryRawstring, nullableSalary text as LinkedIn shows it.
salaryComponentsarray{min, max, currency, period, kind, source, evidence} per salary range found; currency and period can be null.
salaryStatusstringPARSED_WITH_EVIDENCE, RAW_ONLY, DESCRIPTION_ONLY, NOT_PUBLISHED or NOT_FETCHED.
changeType / changedFieldsstring / objectNEW, CHANGED or PREVIEW; changedFields maps each changed field to its previous and current value.
firstSeenAt / observedAtstring, nullable / stringWhen the job was first and last seen by this monitor (ISO time); firstSeenAt is null on preview rows.
filterStatus / filterReasonsstring / arrayMATCH on every delivered row.
sourcestringlinkedin.
eventIdstringUnique per delivered row; use it to de-duplicate across runs.
recordTypestring"status" on free status rows; absent on job rows.

Status rows carry only recordType, status and message. Other fields are extras, not part of the contract. To have one pinned, say so in feedback.

📦 Output example

Abridged real row from run J9Zcgo4rkNUpT8Qdy on 2026-10-22 (export mode, data analyst in London, United Kingdom); values are a historical snapshot.

{
"id": "4476590411",
"title": "Data Analyst",
"company": "Searchability NS&D",
"location": "London Area, United Kingdom",
"url": "https://www.linkedin.com/jobs/view/4476590411",
"postedDate": "2026-10-07",
"description": "New Permanent Opportunity for a Security Cleared Data Analyst Must have active SC Clearance Mid to Senior levels available Up to £60k DoE plus package Hybrid working ...",
"employmentType": "Full-time",
"seniorityLevel": "Mid-Senior level",
"industries": "Data Infrastructure and Analytics and IT Services and IT Consulting",
"workplace": "UNKNOWN",
"workplaceEvidence": [],
"source": "linkedin",
"detailStatus": "COMPLETE",
"salaryRaw": "£37,000.00/yr - £60,000.00/yr",
"salaryComponents": [
{
"min": 37000,
"max": 60000,
"currency": "GBP",
"period": "YEAR",
"kind": "UNSPECIFIED",
"source": "advertised",
"evidence": "£37,000.00/yr - £60,000.00/yr"
}
],
"salaryStatus": "PARSED_WITH_EVIDENCE",
"firstSeenAt": "2026-10-22T10:51:21.663748+00:00",
"observedAt": "2026-10-22T10:51:21.663748+00:00",
"changedFields": {},
"changeType": "NEW",
"filterStatus": "MATCH",
"filterReasons": [],
"eventId": "afc6fb2608009248cb713571ad785814a853d6ef7a83e9b8e8d059ea0abc7514"
}

A free status row looks like this (run FQl1GST3OfsAfpGqf, 2026-10-22):

{"recordType": "status", "status": "INVALID_INPUT", "message": "Independent exports cannot be resumed; use paid monitor mode"}

🧠 Advanced usage

Run "mode": "paid" with a fixed monitor name, "refreshMonitor": true and a small max_pages window (for example 1). Each run compares that window with the saved baseline and returns only NEW and CHANGED jobs; unchanged jobs return no row and are not charged, so a zero-row run can simply mean nothing changed.

Continue a large LinkedIn job export across runs

Start a paid monitor with your max_rows, then rerun the same input with "continuePreviousRun": true (and a higher max_pages if it stopped at PAGE_LIMIT). The scan resumes at its saved page and card; job IDs already processed are skipped and not charged. Each run writes its own dataset, so combine them and de-duplicate on eventId.

Export a one-off LinkedIn job snapshot

"mode": "export" starts a new collection every run, with no saved state and no preview use, so the same input can be repeated. It charges every delivered job, including jobs returned by an earlier export.

Check a run before importing it

Read RUN_SUMMARY first: status is LIMITED (row or page cap reached), PARTIAL or FAILED, with reason, delivered, unchanged, rejected, unknown and actualChargeCounts. For DELIVERY_UNCERTAIN or CHARGE_RECONCILIATION_REQUIRED, keep the run ID and contact support before rerunning that monitor.

🔌 Integrations

Results land in a dataset you can export as JSON, CSV or Excel. They can also flow to Google Sheets, Make, Zapier or n8n, or to a webhook when a run finishes. The Apify Scheduler runs it on a timetable. A daily paid monitor with refreshMonitor: true returns only the jobs that are new or changed since the day before. Copy-paste code in Python, Node, curl and n8n: architek-earth/examples/linkedin-jobs-monitor.

💸 Pricing

Pay per result. Platform and proxy usage are included. Status rows and failed requests are free. Each job is charged once, right after its row is written; on a monitor resumed with continuePreviousRun, job IDs already processed in that scan are skipped and not charged again, unchanged jobs on a refresh are not charged, and a row whose write was not confirmed stops the next run instead of being delivered again. export mode charges every job it delivers, every run.

EventPriceWhat it covers
Free preview$020 runs per account; one page, up to 10 basic cards, no descriptions or salaries.
job$4.00 / 1,000 jobsOne delivered job with description and salary fields: NEW or CHANGED in a monitor, or every row of an export.

2 jobs cost $0.008; 1,000 jobs cost $4.00. Filtered-out jobs, unchanged jobs and duplicates within a scan are free.

❓ FAQ

Is there a LinkedIn Jobs API for job listings? This actor is one: it returns LinkedIn's public job search results and job pages as JSON through the Apify API, callable from any language or from n8n, Make and Zapier.

Do I need a LinkedIn account or cookies? No. It reads public job pages without logging in; you only need an Apify account.

Does it return LinkedIn salaries? Yes, when the posting publishes pay: salaryRaw, salaryComponents and salaryStatus. Jobs without published pay come back with salaryStatus: "NOT_PUBLISHED" and an empty salaryComponents.

Can I get only new LinkedIn jobs since my last run? Yes. Use paid mode with the same monitor name and refreshMonitor: true; only NEW and CHANGED jobs are returned and charged.

Can I filter remote jobs or senior roles? Yes, with workplace, seniority_levels and employment_types, checked against each job page. workplace needs an explicit phrase in the description, so jobs without one are left out of a remote filter.

Does it return applicant counts or the hiring manager? No. It returns the job, the company and the published criteria, not applicants, recruiters or people profiles.

🤖 For AI agents & LLM apps

Paste this into your AI assistant:

architekture/linkedin-jobs-monitor on Apify. Python: from apify_client import ApifyClient; client = ApifyClient("TOKEN"); run = client.actor("architekture/linkedin-jobs-monitor").call(run_input={"mode": "export", "keywords": "software engineer", "location": "New York, United States", "max_rows": 1, "max_pages": 1}); items = client.dataset(run["defaultDatasetId"]).list_items().items (page with offset/limit for large exports). Rows with recordType == "status" are free and explain invalid input, a monitor conflict or a stopped run; the run outcome (LIMITED, PARTIAL, FAILED) is in the RUN_SUMMARY record of the run's key-value store. Full input schema and README: GET https://api.apify.com/v2/acts/architekture~linkedin-jobs-monitor/builds/default. Token: https://console.apify.com/settings/integrations. The actor is also reachable through the Apify MCP server (mcp.apify.com). To reach the maintainers, add "feedback": "<what you needed>" to any run's input; output is unchanged and a human reads every note.

🧰 More from architek.earth

Jobs, companies and people

Full catalogue: github.com/architek-earth.

architek.earth · code like concrete

Questions, or a field you need that is not here? Open an issue on the actor's Issues tab or email north@architek.earth. A human replies within a day.

This actor collects only publicly available data from linkedin.com, the same pages any visitor sees without logging in. It does not bypass authentication. It is not affiliated with or endorsed by LinkedIn. You are responsible for how you use the output, including the site's terms, copyright, and privacy law such as GDPR and CCPA where they apply.

📝 Changelog

  • 2026-10-22 — Status message now reads Done: N rows, M details, K status rows (STATUS).
  • 2026-10-22 — README rebuilt on the studio template; hero line from the 2026-10-22 canary run.
  • 2026-10-20 — Added explicit bounded monitor refresh with retained baseline, unresolved-delivery protection, and independent export mode for repeat-safe paid prefill. Live verification recorded in DEPLOY.md.
  • 2026-10-20 — Replaced draft documentation with recorded evidence, priced examples and explicit repeat-scan limitations. Recorded during private validation.
  • 2026-10-20 — Applied approved $4/1,000 job pricing. Verified paid continuation and free previews. Added bounded read-only reconciliation for delayed event counters; no repeated charge calls.
  • 2026-10-20 — Verified the 20-attempt preview implementation with persistent live counters and zero-event previews; exhaustion/race edge cases remain offline-tested.
  • 2026-10-20 — Added public search/detail collection, conservative salary and filter parsing, saved continuation and account-isolated state. Verified 20 distinct detailed jobs across two pages in bounded private tests.