LinkedIn Jobs Scraper
Scrape public LinkedIn jobs by keyword, location, URL or ID. Filter by date, workplace type and experience. Full descriptions. $0.80 per 1,000 jobs.
How it works
- 1Open it on Apify
Hit Run on Apify — it opens the tool in the cloud, no install.
- 2Set the inputs
Adjust
searchQueries,locations,country(sensible defaults are pre-filled). - 3Click Run
The tool runs on Apify’s cloud and collects the data for you.
- 4Export the results
Download as JSON, CSV or Excel, or pipe straight into your app, Google Sheets, or an AI agent.
Pricing
$0.0008 per LinkedIn job = $0.8 per 1,000
| You are charged for | When | Price |
|---|---|---|
| LinkedIn job | One successfully saved LinkedIn job listing. | $0.0008 |
Pay-per-event pricing: you are billed per result, not per subscription. Billing is handled by Apify on your own account. These are the live Apify store prices, in effect since 2026-08-04, and they are what you are actually charged.
Inputs
| Field | What it does | Type |
|---|---|---|
searchQueries | Job titles or keywords. Every query is searched against every location. Leave empty when using direct job URLs or IDs. | array |
locations | Cities, regions, or countries. Every location is combined with every query. Leave empty for an unrestricted location search. | array |
country | Optional country name appended to each search location, for example Canada or United States. It is also used alone when no location is supplied. | string |
jobUrls | Public LinkedIn /jobs/view/ URLs to fetch directly. URLs and IDs are deduplicated with search results. | array |
jobIds | Numeric LinkedIn job IDs to fetch directly. | array |
datePosted | LinkedIn's server-side posting-age filter. | string |
easyApply | Only jobs you can apply to on LinkedIn itself, without being sent to the company's own site. | boolean |
under10Applicants | Only jobs few people have applied to yet. This is LinkedIn's own filter. | boolean |
companyIds | Only jobs from these companies, one LinkedIn company id per line, such as 1441 for Google. To find an id, filter jobs by the company on LinkedIn and copy the number after f_C= in the address bar; pasting that whole address works too. A company name won't work. | array |
distance | How far from each location to look, in miles. Needs at least one location. | string |
includeDetails | Fetch each job detail page for description, seniority, employment type, job function, industries, and applicant count. Direct URLs and IDs always fetch details. Turning this off makes searches faster and cheaper. | boolean |
maxItems | Maximum unique real job rows across all searches, URLs, and IDs. | integer |
maxPagesPerSearch | Safety limit for each query/location combination. LinkedIn currently returns about 10 jobs per guest page. | integer |
countryCode | Optional two-letter country code, for example US, CA, GB or DE. Used only if a direct request is refused and the fallback is needed. | string |
maxProxyRetries | How many times to retry one refused request before giving up on it. | integer |
requestTimeoutSecs | Timeout for each lightweight LinkedIn guest endpoint request. | integer |
What you get
A structured dataset — each result includes fields like:
titlecompanylocationworkplaceTypeemploymentTypepostedAturlExport every run as JSON, CSV or Excel, or send it to your app, a database, Google Sheets, or an AI agent.
Ready-made automation using this tool
A finished workflow you can import into n8n and run — this tool does the data-gathering inside it. Free, and yours to change.
Spot companies about to buy, from what they are hiring for
A company hiring its first support manager is telling you it is about to need support tooling. This reads job posts, works out what each hire implies the company will need, scores how well that matches what you sell, and files only the strong ones in Notion.
Related tools in Job Market Scrapers
Other ready-to-run tools in the same category — all pay-per-use on the Apify cloud.
BuiltIn Jobs Scraper
Scrape Built In tech jobs: salary, all locations, skills, seniority, exact posted date. No login, no API key. $0.81 per 1,000.
Dice Jobs Scraper
Scrape Dice jobs: title, company, location, type, posting age and apply URL. Descriptions optional. No login. $0.90 per 1,000 jobs.
SEEK Jobs Scraper
Scrape SEEK jobs in Australia and New Zealand: salary ranges, workplace type and full descriptions. No login. $0.81 per 1,000, flat at every volume.
Career Site Jobs Scraper
Scrape open jobs from a career page, a domain or a company name. Works with 10 ATS platforms. Get title, location, department and apply URL.
Internshala Scraper
Scrape Internshala internships and fresher jobs by keyword, city, category, stipend or work from home. Duration, apply-by date, openings. $0.34/1,000.
XING Jobs Scraper
Scrape XING jobs in Germany, Austria and Switzerland. Company, employment type, career level, posted date, apply URL and salary. $0.51 per 1,000 jobs.
Where this tool sits
- Categories
- Job Market Scrapers
- Platforms
LinkedIn Jobs Scraper: public job postings by keyword, location, URL or ID
Search LinkedIn jobs without an account and get one row per posting: title, company, location, posting date, the canonical job URL and, when you ask for it, the full description, the seniority, the employment type and the applicant count.
The search itself works signed out because a job posting is a public page. What you get by default is the card, the same block of text you see in a results list. The description lives on the job's own page and is only fetched when you switch includeDetails on.
| Input | Keywords and locations, or direct job URLs and IDs |
| Output | One row per job |
| Ceiling | 5,000 jobs per run |
| Account needed | None |
| Price | $0.80 per 1,000 jobs, flat on every plan |
💼 What LinkedIn Jobs Scraper does
Give it job titles and places. Every query is run against every location, so three titles and four cities is twelve searches, and the results are pooled and deduplicated into one dataset. A job that two searches both find is written once and charged once.
Every filter here is LinkedIn's own, applied by LinkedIn before the results come back: date posted, companies, Easy Apply, under 10 applicants and distance. They narrow the search rather than just shrinking the output. Remote, job type, experience level and newest first are missing on purpose. LinkedIn ignores them on a public search, so offering them would only look like filtering.
You can also skip searching. Paste /jobs/view/ URLs or numeric job IDs and they are fetched directly, with their full detail pages, and merged with anything the searches found.
📥 What you give it
{
"searchQueries": ["software engineer"],
"locations": ["Toronto, Ontario"],
"datePosted": "pastWeek",
"includeDetails": true,
"maxItems": 200
}
| Field | Default | What it is |
|---|---|---|
searchQueries | prefilled with an example | Job titles or keywords. Each one is searched against each location. |
locations | prefilled with an example | Free text, passed to LinkedIn as typed. "Toronto, Ontario", "Germany", "Greater Boston". |
country | empty | A country name appended to each location, and used on its own when you give no location. |
jobUrls | empty | Public /jobs/view/ URLs to fetch directly. |
jobIds | empty | Numeric job IDs, same thing without the URL around them. |
datePosted | any | any, past24h, pastWeek, pastMonth. |
easyApply | off | Only jobs you apply to on LinkedIn itself, not on the company's own site. |
under10Applicants | off | Only jobs few people have applied to yet. LinkedIn's own filter. |
companyIds | [] | Only these companies' jobs, as LinkedIn company ids such as 1441 for Google. A name is refused: LinkedIn would ignore it and return every company. |
distance | any | 5, 10, 25, 50 or 100 miles around each location. Needs a location. |
includeDetails | off | Opens each job's own page for the description, seniority, employment type, job function, industries and applicant count. Direct URLs and IDs always do this. |
maxItems | 100 | 1 to 5,000. Unique jobs across the whole run. |
maxPagesPerSearch | 10 | 1 to 100. A guest results page holds about ten jobs, so ten pages is roughly a hundred per query and location. |
requestTimeoutSecs | 20 | 5 to 60 seconds per request. |
proxyConfiguration, countryCode, maxProxyRetries | as they come | Advanced network settings. The defaults suit almost every run. Leave them alone unless you have a reason. |
Switch includeDetails on if you want the job text. It is off out of the box, and without it description, seniorityLevel, employmentType and applicants are null on every search row. It costs the same per job either way.
Full descriptions on thousands of jobs is a lot to ask of one run. Split that into batches of a few hundred and you will have a better time of it.
📤 What you get back
A real row from a recent run, from a card-only search, with the logo link cut short:
{
"ok": true,
"sourceType": "search",
"jobId": "4445788134",
"title": "Software Engineer (C++) - Platform & Emerging Technologies",
"company": "Autodesk",
"companyUrl": "https://www.linkedin.com/company/autodesk?trk=public_jobs_jserp-result_job-search-card-subtitle",
"companyLogo": "https://media.licdn.com/dms/image/v2/D560BAQGrSi2UOCdL5g/company-logo_100_100/...",
"location": "Toronto, Ontario, Canada",
"salary": null,
"postedAt": "2026-09-10",
"postedText": "3 days ago",
"workplaceType": null,
"employmentType": null,
"seniorityLevel": null,
"jobFunction": [],
"industries": [],
"applicants": null,
"description": null,
"descriptionHtml": null,
"url": "https://www.linkedin.com/jobs/view/4445788134",
"searchQuery": "software engineer",
"searchLocation": "Toronto, Ontario, Canada",
"detailFetched": false
}
That is what a row looks like with includeDetails off. Everything below postedText fills in when it is on.
| Field | What it is |
|---|---|
jobId | LinkedIn's own id. Your dedupe key, and the thing to store if you re-check postings later. |
postedAt | The date from the posting's own timestamp. postedText keeps LinkedIn's wording, "3 days ago", for when you want what the page said. |
salary | Only what the card states, which for most postings is nothing. |
workplaceType | Always empty. LinkedIn's public job pages don't say whether a job is remote, hybrid or on-site. |
applicants | LinkedIn's applicant count, detail pages only, and it is a rounded figure on their side. |
description, descriptionHtml | The posting text, plain and with markup. Detail pages only. |
detailFetched | Whether that job's own page was opened. The fastest way to tell why a field is empty. |
sourceType | search for a job a search found, direct for one you supplied by URL or id. |
🧾 Reading the output
Three kinds of row, and only real jobs cost anything.
| Row | How to spot it | Charged |
|---|---|---|
| A job | sourceType: "search" or "direct" | yes |
| The sample row | _sample: true, on a run with no usable input | no |
| A diagnostic | ok: false and _diagnostic: true | no |
There is no charged field on these rows. sourceType is what separates a real job from a sample or a diagnostic.
One ordering quirk worth knowing: diagnostics are written as they happen, while job rows are written at the end of the run. So in dataset order the problems come first, and a run you abort half way through leaves you with the diagnostics and no jobs.
A diagnostic row carries an errorCode:
| Code | What it means |
|---|---|
BAD_INPUT | A job URL that is not a /jobs/view/ link, an id that is not numeric, a company given as a name instead of an id, a distance with no location, or a search that sets workplaceType, jobTypes, experienceLevels or sortBy: "recent", which LinkedIn ignores. Nothing is searched. |
NO_RESULTS | That query and location combination returned nothing. It also appears when everything a search found had already been returned by an earlier search in the same run. |
NOT_FOUND | A direct job URL or id that LinkedIn no longer has. Usually a filled or withdrawn posting. |
PARSE_ERROR | The detail page came back without the fields that make a job row. |
BLOCKED | LinkedIn answered with a wall instead of results. Give it a few minutes. |
NETWORK | The request failed or did not arrive in time. |
▶️ How to run it
1. Open LinkedIn Jobs Scraper and click Try for free. 2. Put job titles in Search queries and places in Locations. 3. Set Date posted and any other filter you want LinkedIn to apply. 4. Turn on Fetch full job details if you need the description, then set Maximum jobs. 5. Click Start, then download the dataset as JSON, CSV or Excel, or read it from the API.
💰 How much does it cost?
$0.80 per 1,000 jobs. Flat on every Apify plan, no volume tiers.
One charge per unique job row. A posting that several of your searches all find is written once and billed once. Sample rows and diagnostic rows are never charged, and a search that finds nothing costs you nothing for results. Fetching the full descriptions does not change the price.
💡 What people use it for
- Watching a set of titles in a set of cities on a schedule, and diffing the new ids against
yesterday's.
- Spotting which companies are hiring into a function right now, as a buying signal.
- Pulling descriptions for a market to see how a role is actually scoped and worded.
- Checking whether a specific posting is still live, by feeding its id back in.
🚧 What it does not do
- No applicant lists, no recruiter names, no contact details. None of that is on a public job
page.
- No salary for most postings. The field is there and it carries whatever the card says, which
is usually nothing. LinkedIn does not publish a salary unless the poster did.
- No applying for you, and no saved jobs. It can find Easy Apply jobs, but it only reads.
- **No remote, hybrid or on-site filter, no job type or experience level filter, and no newest
first.** LinkedIn ignores all four on a public search and sends back the same jobs, so a search that sets one stops with a message and nothing is charged.
- Location is text. It goes to LinkedIn as typed. Set
distanceto say how far around it to
look; otherwise LinkedIn decides what counts as nearby.
- No industry filter. LinkedIn's public job search accepts one and quietly ignores it, so it
would only look like filtering. With includeDetails on, every row lists the job's industries, so you can filter on those yourself.
- A search stops at the pages you allowed. A wide query will always have more behind it than
one run returns.
- No jobs that were never public, and none that have already been taken down.
🧭 Which job scraper do you need?
| If you want | Use |
|---|---|
| LinkedIn postings | This one |
| Indeed postings | Indeed Jobs Scraper |
| Google's aggregated job results | Google Jobs Scraper |
| Jobs straight from company career sites | Career Site Jobs Scraper |
| The company page behind a posting | LinkedIn Companies Scraper |
❓ Questions people ask
Do I need a LinkedIn account? No, and there is no cookie field. Job pages are public.
Why is the description empty? Because includeDetails was off. Check detailFetched on the row: false means that job's own page was never opened.
Why did I get fewer jobs than I asked for? Either the search genuinely has fewer, or maxPagesPerSearch stopped it. Widen the query, raise the page limit, or add locations.
Can I filter by salary? No. Too few postings publish one for that to be a useful filter.
How fresh is postedAt? It is the posting's own timestamp, so it is as accurate as what the employer set. Use datePosted if you only want recent ones.
Is scraping LinkedIn jobs legal? These are public pages LinkedIn publishes for search engines. Apify's write-up on scraping and the law is a reasonable starting point, and we are not lawyers.
🆘 If something breaks
Open the Issues tab on the actor page. Send the run ID and the exact query and location. The errorCode and searchQuery on the diagnostic row usually point straight at it.