LinkedIn Jobs Scraper – Job Postings by Keyword & Place
Pricing
from $0.30 / 1,000 job returneds
LinkedIn Jobs Scraper – Job Postings by Keyword & Place
Scrape public LinkedIn job postings for any keyword and place: title, company, location, posted date, link, full description, seniority, employment type, industries, applicants and salary. Filter by date posted, company, Easy Apply and under-10 applicants. No login, no key, no browser. Unofficial.
Pricing
from $0.30 / 1,000 job returneds
Rating
0.0
(0)
Developer
Simple Actor
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
2
Monthly active users
a day ago
Last modified
Categories
Share
Scrape public LinkedIn job postings as structured data. Give it a keyword and a place — "software engineer" in "United States", "registered nurse" in "Texas, United States", "marketing manager" in "London, England, United Kingdom" — and every job LinkedIn lists for the search comes back as a row: title, company, location, posted date, link and, by default, the full description with seniority level, employment type, industries, applicant count and the salary where the posting states one. In LinkedIn's own order, up to the 1,000 jobs LinkedIn shows for any search.
Built for job-market research, recruiting, lead generation (a company that is hiring is a company that is buying) and job boards: every open role of a kind in a place, with the text to read and the id to deduplicate on.
No login, no cookies, no API key, no browser, no account of yours involved. It reads what LinkedIn shows to anyone who opens a job search without signing in.
$0.30 per 1,000 jobs — $0.03 for a default 100-job search — plus Apify's standard $0.00005 per run start. Apify platform usage included, nothing else to pay. A search that cannot be read, a job that is not delivered and a job you told it to skip are never charged.
Unofficial. Not affiliated with, endorsed by, or sponsored by LinkedIn.
Features
- The whole posting, per job — description as text and as HTML, seniority level, employment type, industries, applicant count, pay range when stated, whether it applies on LinkedIn or on the employer's site, and whether it has closed.
- Everything LinkedIn lists — paged automatically to the end of LinkedIn's own results, up to the 1,000 jobs LinkedIn shows for a search, with no job twice.
- The filters LinkedIn still honours — posted within any window down to a minute, one or more companies by name or id, Easy Apply only, under 10 applicants only, and a distance around a city.
- Places resolved and echoed — every place goes through LinkedIn's own place lookup, and each row says which place and which LinkedIn geoId it was searched in. A place LinkedIn does not know is an error row, never a silent search of somewhere else.
- Trim before you pay — title must-include and must-not-include lists, and a list of job ids you already hold, are applied before a job is read, so a job they drop costs nothing.
- Specific jobs by link — paste job links or ids and get each as a row, with its description.
- Many searches in one run — several keywords, several places, every combination searched.
- Runs stay green. A place or company LinkedIn does not know, a job LinkedIn no longer has, a page it would not serve — each becomes an error row with a short code and a sentence, the run's status message counts them, and the run itself succeeds. The error rows are where to look for what could not be done.
- Cheap to run — plain HTTP at 128 MB, about one request per job, paced so LinkedIn keeps answering.
Input
| Field | What it does |
|---|---|
keywords | One search per line, as you would type it into LinkedIn's job search. Each is searched in each place. Leave empty to list every job in a place. |
locations | A city, region or country as LinkedIn names it, or a LinkedIn geoId. Leave empty to search worldwide. |
maxJobs | Jobs per search, default 100, ceiling 1,000 (LinkedIn's own). |
includeDescription | On by default: every job is read in full. Off, rows carry only what the search list shows — about ten times fewer requests. Same price per job either way. |
postedWithin | "24 hours", "3 days", "2 weeks", "90 minutes" — any window. |
companies | Only these companies, by name or LinkedIn id. |
easyApplyOnly | Only jobs that take applications on LinkedIn. |
underTenApplicantsOnly | Only jobs LinkedIn marks as having fewer than ten applicants. |
distanceMiles | 5 to 100 miles around a city. |
titleMustInclude / titleMustNotInclude | Words or phrases a job's title must or must not contain, applied before the job is read. |
skipJobIds | Job ids or links you already have; left out before anything is read. |
jobUrls | Specific jobs to read directly, as links or ids, one row each. |
{"keywords": ["software engineer"],"locations": ["United States"],"maxJobs": 100,"includeDescription": true,"postedWithin": "24 hours"}
Output
One item per job, in LinkedIn's order for each search. Rows are pushed as each job completes, so a run's dataset grows while it runs.
| Field | What it holds |
|---|---|
type | job, or error for a search or job that could not be answered |
searchKeywords, searchLocation, searchGeoId | The search this row answers: the keywords, the place as LinkedIn resolved it, and LinkedIn's id for that place |
searchTotalJobs | How many jobs LinkedIn says the search has (exact when small, capped near 12,000 when large) |
position | The job's rank in LinkedIn's order, 1 first |
jobId | LinkedIn's numeric id for the posting — stable and unique |
jobUrl | The posting's public page, without tracking parameters |
title | The job title |
companyName, companyUrl, companyLogoUrl, companyId | The employer: name, LinkedIn page, logo, numeric id (the id and the logo need a description read) |
location | Where the job is, as posted |
postedDate | YYYY-MM-DD, as LinkedIn's search list states it |
postedTimeAgo | LinkedIn's wording: "1 week ago", "3 days ago", "Just now" |
badge | The search card's label when it has one: "Be an early applicant", "Actively Hiring", "Medical insurance +3 benefits" |
descriptionText | The full description as plain text, paragraphs and bullet points on their own lines |
descriptionHtml | The same description as LinkedIn's HTML |
seniorityLevel | "Entry level", "Associate", "Mid-Senior level", "Director", "Executive" or "Not Applicable" |
employmentType | "Full-time", "Part-time", "Contract", "Temporary", "Internship", "Volunteer" or "Other" |
industries | The employer's industries, as one string |
applicants, applicantCount | LinkedIn's wording ("189 applicants", "Over 200 applicants", "Be among the first 25 applicants") and the number: exact, 200 for "over 200", null for "first 25" |
salaryText, salaryMin, salaryMax, salaryCurrency, salaryPeriod | The pay range exactly as posted, and as numbers with a currency code and a period ("year", "hour"). Null when the posting states none, which is most of them |
applyType | linkedin (Easy Apply) or external (the employer's own site) |
isClosed | True when LinkedIn says the posting is no longer accepting applications |
hasDescription, descriptionError | Whether the description was read, and if not, why (rate_limited, read_failed, not_found, parse_failed) |
scrapedAt | When the run read the row |
A real row, for a search of "software engineer" in "United States" (description shortened here):
{"type": "job","searchKeywords": "software engineer","searchLocation": "United States","searchGeoId": "103644278","searchTotalJobs": 11940,"position": 1,"jobId": "4419969671","jobUrl": "https://www.linkedin.com/jobs/view/senior-software-engineer-%E2%80%93-go-golang-at-general-motors-4419969671","title": "Senior Software Engineer – Go (Golang)","companyName": "General Motors","companyUrl": "https://www.linkedin.com/company/general-motors","companyLogoUrl": "https://media.licdn.com/dms/image/v2/D4E0BAQEfAZWzerj03w/company-logo_100_100/B4EZdkPGXYGcAQ-/0/1749733370529/general_motors_logo?e=2147483647&v=beta&t=ycfbgxUnJw_BkI-JSrXwekaeYZ6rWNAlvxirgJ7o00Q","location": "Warren, MI","postedDate": "2026-10-04","postedTimeAgo": "1 week ago","badge": "Actively Hiring","companyId": "1472","descriptionText": "Job Description\n\nAs a Senior Software Engineer – Go (Golang), you will design, develop, and deliver high-performance middleware and application software solutions supporting GM's next-generation in-vehicle infotainment platforms and connected vehicle systems. …","descriptionHtml": "<strong>Job Description<br><br></strong>As a Senior Software Engineer – Go (Golang), you will design, develop, and deliver …","seniorityLevel": "Not Applicable","employmentType": "Full-time","industries": "Motor Vehicle Manufacturing, Appliances, Electrical, and Electronics Manufacturing, and IT Services and IT Consulting","applicants": "189 applicants","applicantCount": 189,"salaryText": null,"salaryMin": null,"salaryMax": null,"salaryCurrency": null,"salaryPeriod": null,"applyType": "external","isClosed": false,"hasDescription": true,"descriptionError": null,"scrapedAt": "2026-10-11T16:29:34.603Z"}
A row with a stated salary carries it both ways:
"salaryText": "$123,500.00/yr - $150,000.00/yr","salaryMin": 123500,"salaryMax": 150000,"salaryCurrency": "USD","salaryPeriod": "year"
Error rows
The run succeeds whatever a search or a job does; what could not be done is a
row with type: "error", a short error code and an errorDescription
sentence, and the run's status message says how many there are. Error rows are
never charged.
error | When |
|---|---|
bad_input | Nothing to search, a window or distance that could not be read, or a job link that is not one |
location_not_found | LinkedIn's place lookup has no match for the place given |
company_not_found | LinkedIn's company lookup has no match for a company given |
not_found | A job id LinkedIn answers 404 for — never issued, or removed |
rate_limited | LinkedIn answered every attempt with its challenge page |
read_failed | The page could not be read for another reason, on every attempt |
parse_failed | LinkedIn answered with a page this Actor could not read a job from |
incomplete | The search was cut short — the run's timeout or maximum charge was reached, or LinkedIn withheld a page of results on every read — and the row says how many jobs were delivered |
An empty dataset therefore means one thing: every search was read and LinkedIn lists no jobs for it.
Pricing
$0.30 per 1,000 jobs, the same on every Apify plan, charged per job row as
it is delivered. Apify's standard apify-actor-start event adds $0.00005 per
run.
| Run | Jobs | Price |
|---|---|---|
| Default: one search, 100 jobs with descriptions | 100 | $0.030 |
| Ten searches of 100 | 1,000 | $0.30 |
| One search to LinkedIn's limit, list only | 1,000 | $0.30 |
| A search that finds nothing, or an unknown place | 0 | $0.00005 |
Apify platform usage — compute, proxy, storage — is included in the price. A
job whose description could not be read is still delivered with the listing's
fields, says why under descriptionError, and is charged as a job; a job that
is not delivered at all is not. Jobs removed by titleMustInclude,
titleMustNotInclude or skipJobIds are never read and never charged.
How it works, and what it cannot do
Where the data comes from. LinkedIn serves a job search, its paging and each job's description to readers who are not signed in. This Actor reads those pages, nothing else: no account, no cookie, no browser. About one request in ten is answered with LinkedIn's "too many requests" page; it is retried and clears. Requests are paced at about two and a half a second, which LinkedIn has sustained for runs of over a thousand jobs.
1,000 jobs per search is LinkedIn's limit, not this Actor's. LinkedIn
shows at most 1,000 jobs for any search, however many it counts
(searchTotalJobs says how many). To get more, split the search — by city
instead of country, by "posted within", by company — and the Actor
deduplicates across searches in a run.
Some things LinkedIn does not show to a reader who is not signed in, so no row carries them: the employer's own apply link (only whether the job applies on LinkedIn or externally), whether a job is remote, hybrid or on-site (it is usually in the title or the description), the job function, and an exact posting time (the date is given).
Job type, experience level, remote, salary and sort-order filters are not
offered, because LinkedIn dropped them from its public search in August 2026:
they return the same jobs as no filter at all. Filter the rows on
employmentType and seniorityLevel instead, or put the word in the keywords
("contract software engineer", "software engineer intern") and use the title
lists.
LinkedIn's search matches loosely. "nurse" returns nursing jobs of every
kind; a quoted phrase and NOT are not honoured. The titleMustInclude and
titleMustNotInclude lists are the precise tool, and they cost nothing
because they run before a job is read.
No person's data. Some postings show a "message the job poster" block with a named person. This Actor does not read it; a row describes the job.
FAQ
How long does a run take? A default 100-job search with descriptions takes about a minute and a half; a 1,000-job search with descriptions about a quarter of an hour; a 1,000-job list-only search about three minutes. Rows arrive as each job completes, so the dataset fills while the run goes.
Why does a row say descriptionError? LinkedIn answered that job's
description page with its challenge page on every attempt, or the posting was
removed between the search and the read. The listing's fields are real; run
the job again under jobUrls to fetch the description.
Can I poll for new jobs? Yes: run the same search on a schedule with
postedWithin set to the interval, and pass the ids you already hold as
skipJobIds so only new jobs are read and charged.
What is a geoId? LinkedIn's numeric id for a place; every row carries the
one it was searched in as searchGeoId. Pass it back as a location to search
exactly the same place again.
Why did a company filter return an error row? The name did not match a
company in LinkedIn's lookup. Check the spelling, or give the company's numeric
id (companyId on any of its job rows).
Why no proxy setting? The Actor reads LinkedIn through Apify's datacenter
proxy, which was measured to work as well as residential for these pages, and
the price includes it.
Legal
This Actor reads only pages LinkedIn serves to the public without a login, and only job postings — not profiles. Use the data in line with LinkedIn's terms and the law where you are, and give people the rights they have over their data; a job posting is an employer's public notice, not a person's record.