LinkedIn Jobs Scraper - Full Descriptions & Job Feeds
Pricing
from $0.90 / 1,000 jobs
LinkedIn Jobs Scraper - Full Descriptions & Job Feeds
Search public LinkedIn jobs with full descriptions, company data, source filters and persistent only-new monitoring. No LinkedIn login or third-party API key.
Pricing
from $0.90 / 1,000 jobs
Rating
0.0
(0)
Developer
Ben
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
LinkedIn Jobs Scraper: full descriptions and new-job monitoring
Search public LinkedIn job postings and export a consistent dataset with the full public description, employer, location, posting date and employment criteria. Use keywords and a location, or provide exact job URLs. Enable onlyNew to remember exported job IDs and omit them from later runs of the same saved search.
This Actor retrieves LinkedIn's public job-search cards and public job-detail pages directly. It does not require a LinkedIn login, session cookie or separate data-provider API key. Default runs use HTTP without a browser or proxy. Optional proxy configuration is available for accounts that need a different source route.
For recurring hiring feeds: collect full public job descriptions and retain job IDs between runs with onlyNew. Result events cost $1.80 per 1,000 jobs on Free or $0.90 on paid plans, plus the start event. This Actor returns jobs, not personal profiles or candidate contact details.
Quick start
Start with a tested example for a Berlin Python job feed:
Open the example and choose Try for free to copy its input into your account. Check the input and set a maximum run charge before starting; Actor fees still follow the pricing below. When the run finishes, open the run's dataset and select the Overview view and download JSON for nested fields or CSV/Excel for a table.
| Your goal | Input to use |
|---|---|
| Find recent jobs | keywords, location and timeRange |
| Read known postings | jobUrls; this bypasses search filters |
| Receive only new IDs | onlyNew with a separate monitorId per search |
The real output example shows the record shape. The Python workflow saves your own export to a local JSON file with a $0.05 maximum Actor charge.
Start with a small search
{"keywords": "python","location": "Berlin, Germany","maxResults": 3,"maxPages": 2,"timeRange": "week","sortBy": "date","strictKeywords": true,"onlyNew": false}
The Actor asks LinkedIn for recent Python jobs in Berlin, reads the public descriptions, checks the keywords, and returns up to three matching jobs. It examines no more than two search pages. Fewer results can be correct: the source may have fewer matches, repeat an earlier page, or return recommendations that do not satisfy the strict keyword check.
Inspect the first dataset before increasing the cap. The overview table shows title, employer, location, posting date, employment type, salary text and the source link. JSON contains the full description and additional fields. Export CSV, Excel or JSON from Apify, or consume the default dataset through its API.
Why check the keywords again?
LinkedIn's guest endpoint can return unrelated recommendations even for a nonsense keyword. A technically successful HTTP request does not establish that those jobs match your search. With strictKeywords: true, every whitespace-separated keyword word must appear in the title or public description, ignoring case and respecting word boundaries. For example, Java does not match a record solely because it mentions JavaScript.
This is a literal text check, not a relevance score. Use simple words such as python or software engineer. For LinkedIn-specific Boolean syntax or a broader source-defined search, turn strict matching off and inspect the resulting relevance yourself. The Actor does not replace missing matches with invented listings.
Example output
One record from a verified September 9, 2026 cloud run. Only the description is shortened here; the Actor exports the complete public description, including paragraph breaks. The dated vacancy may no longer be open.
{"job_id": "4462232588","title": "Python Developer","company": "Russell Tobin","company_url": "https://www.linkedin.com/company/russell-tobin-&-associates-llc","location": "Berlin, Germany","posted_date": "2026-09-07","posted_text": "1 day ago","salary": null,"url": "https://www.linkedin.com/jobs/view/4462232588","description": "Role: Software Engineer Employment Type: Contract – 12 Months minimum Location: Germany - Berlin, Cologne, Frankfurt, Dusseldorf, Munich, Hamburg, Stuttgart Travel & Mode of work: Travel required at client’s locations & Hybrid mode of work Experience required – 5-12 years Max NOTE – No architect, Manager, Full stack, Frontend required Required- have experience using tools like Python, Kedro, AWS, Azure, Databricks, Docker, Kubernetes, Django, Flask, and MySQL / Postgres advocate using the right tech for the right task—you’ll be up to date with the latest [description shortened in this documentation example]","applicants_text": "100 applicants","apply_url": null,"seniority": "Mid-Senior level","employment_type": "Contract","job_function": null,"industries": "Technology, Information and Media","scraped_at": "2026-09-09T17:50:26.850503+00:00","is_new": false,"monitor_id": null}
Returned fields
| Field | Meaning |
|---|---|
job_id | LinkedIn's numeric job ID, returned as a string |
title | Title from the job-detail page |
company, company_url | Employer name and public LinkedIn company URL |
location | Location text supplied by LinkedIn |
posted_date | Calendar date from the search card, when available |
posted_text | Source wording such as “4 days ago” |
description | Complete public description as plain text, retaining paragraph and list breaks |
seniority, employment_type | Explicit job criteria from the detail page |
job_function, industries | Source job criteria, when exposed |
salary | Source salary text, without guessing a currency or annualizing a range |
applicants_text | Source applicant wording; it may be a range rather than an exact count |
apply_url | External application URL only when the public page exposes it |
url | Stable LinkedIn job URL without search tracking parameters |
scraped_at | UTC collection timestamp |
is_new, monitor_id | Optional monitor metadata |
Missing source fields are not inferred. A direct job URL may have relative posting text without a machine-readable calendar date. Salary and external application URLs are not guaranteed. Company information describes the employer named on the posting; the Actor does not identify employees or infer their contact addresses.
Source filters
location is passed to LinkedIn as entered. timeRange accepts all, day, week or month; sortBy accepts date or relevance. Available posting dates are also checked against the selected recency window at calendar-day precision. Date-only source data cannot establish an exact elapsed-hour boundary.
Optional array filters use LinkedIn's source values:
| Input | Accepted values |
|---|---|
workTypes | 1 onsite, 2 remote, 3 hybrid |
jobTypes | F full time, P part time, C contract, T temporary, I internship, V volunteer, O other |
experienceLevels | 1 internship, 2 entry, 3 associate, 4 mid senior, 5 director, 6 executive |
These filters are submitted to the source. The Actor preserves the returned criteria instead of claiming that every employer has categorized its listing correctly. An empty filter array means any value. Keep the query and all filter settings stable when comparing recurring snapshots.
Exact job URLs
{"jobUrls": ["https://www.linkedin.com/jobs/view/4462980082"],"maxResults": 1}
That ID came from a real September 9, 2026 local source check for a Software Engineer (Python) role. It is a dated example, not a promise that the vacancy remains open. Replace it with a current public job URL. Numeric IDs are also accepted. Direct mode bypasses search, keyword and recency filters, deduplicates repeated IDs, and verifies that each detail response belongs to the requested job.
Recurring only-new feed
{"keywords": "python","location": "Berlin, Germany","timeRange": "week","maxResults": 25,"maxPages": 5,"onlyNew": true,"monitorId": "berlin-python-jobs","firstRunMode": "seedOnly"}
Save this input as an Apify Task and schedule it after inspecting a small run. A seedOnly first run records its snapshot without returning the existing backlog. Later runs compare their bounded snapshots against the saved IDs. Use emitAll to export the initial snapshot instead.
Monitor state lives in a named key-value store in the account running the Actor. Different customers do not share IDs. Use one explicit monitorId per search, avoid overlapping runs, and create a new ID when changing filters. resetState: true clears only the selected baseline; turn it off again before scheduling.
The monitor retains at most 5,000 IDs for 90 days. An ID that ages out can appear as new if the source returns it again. maxResults bounds the collected snapshot before only-new filtering, so an unchanged run can correctly produce zero rows. It does not continue indefinitely until it finds that many unseen jobs. Increase the snapshot/page limits when the current window is too small for your use case.
Only successfully exported IDs are marked seen after a spending-limit stop. Unexported jobs remain eligible for a later run. A source failure stops collection before the monitor baseline is updated. This is a new-job feed; it does not detect every description edit or treat a missing search result as proof that a job closed.
Pricing and limits
Free-plan pricing is $0.0018 per exported job, or $1.80 per 1,000 jobs. Bronze, Silver, Gold, Platinum and Diamond pay $0.0009 per job, or $0.90 per 1,000 jobs. Full public descriptions and monitor mode are included; there is no separate description or monitoring event.
At 512 MB, the start event is $0.00005 on Free, $0.000045 on Bronze, $0.00004 on Silver and $0.000035 on Gold, Platinum or Diamond. A ten-job export costs $0.01805 on Free or $0.009035 on Gold. A seed-only run or an unchanged repeat has no result charge, but still has the start charge. Larger memory settings use one start event per GB, with a minimum of one. These are pricing examples, not forecasts of output or future bills; inspect Apify's current pricing panel and set a maximum run charge.
The result cap is 500 and the page cap is 50. A page currently contains about ten search cards, but the source can change that. Requests run sequentially with a delay. Default memory is 512 MB and the default timeout is five minutes; large searches or slow routes can require a larger timeout. Optional proxy availability and usage depend on your account. No paid external data provider is required by the default engine.
Python API workflow
Copy the first input JSON above into input.json and save this code as export.py. In a Python virtual environment, install apify-client with python -m pip install apify-client. Set APIFY_TOKEN to your own Apify token in your environment, then run python export.py. Keep the token out of source files and shared screenshots.
import jsonimport osfrom decimal import Decimalfrom pathlib import Pathfrom apify_client import ApifyClientclient = ApifyClient(os.environ["APIFY_TOKEN"])run = client.actor("benthepythondev/linkedin-jobs-scraper").call(run_input=json.loads(Path("input.json").read_text(encoding="utf-8")),max_total_charge_usd=Decimal("0.05"),memory_mbytes=512,timeout_secs=300,)if not run or run["status"] != "SUCCEEDED":raise RuntimeError(f"Inspect the run before retrying: {run and run['id']}")rows = list(client.dataset(run["defaultDatasetId"]).iterate_items())Path("berlin-python-jobs.json").write_text(json.dumps(rows, ensure_ascii=False, indent=2), encoding="utf-8")print(f"Saved {len(rows)} records from run {run['id']}")
This starts a run in your account, waits for completion and saves berlin-python-jobs.json. The $0.05 limit bounds Actor charges; it is not a promised bill or result count. Check the printed run ID and server log if the result is shorter than expected. If your client loses its connection, inspect the existing run in Console before starting another one. See the official Python client for other run options.
Use job_id as the destination key and keep the complete description. The example exports a snapshot. To create a recurring feed, first test the documented only-new input, then save it as your own Task. An unchanged monitor can return zero rows. A job absent from one bounded search is not confirmed closed.
For recurring runs without writing a scheduler, copy a tested public Task into your account and attach an Apify Schedule to that copy. Keep its maximum-charge limit and avoid overlapping runs when using monitor state. The Python example above is also suitable for an existing scheduler; run it from the directory containing input.json and provide the token through that scheduler's secret settings.
Failures, support and related feeds
A blocked, rate-limited or malformed source page is surfaced as a failed run rather than a successful empty export. Temporary rate limits and server errors receive bounded retries. Persistent restrictions, removed direct jobs and missing descriptions still need attention; reduce the scope or inspect the failed run before retrying. No scraper can promise uninterrupted source access.
For a reproducible issue, open an Actor issue with the run URL, relevant input and expected result. Do not include credentials. An honest review after a useful export helps other users evaluate the Actor.
For other job sources, see the Remote Jobs feed and Greenhouse jobs. They have separate source coverage and pricing. This Actor covers public job advertisements; it does not scrape private LinkedIn profiles, employee lists or messages.
Keywords: LinkedIn jobs scraper, LinkedIn job search API, public job postings, full job descriptions, hiring data, job monitoring, new job alerts, recruitment data, LinkedIn jobs CSV, Berlin Python jobs.