LinkedIn Jobs Scraper: New Jobs Only (No Login)
Pricing
from $0.70 / 1,000 jobs
LinkedIn Jobs Scraper: New Jobs Only (No Login)
Scrape public LinkedIn job search results: one row per job with title, company, location and posted date, plus pay, seniority and applicants. Use it for job alerts. The Only new jobs option returns only jobs added since your last run. No login, no apply links. Not affiliated with LinkedIn.
Pricing
from $0.70 / 1,000 jobs
Rating
0.0
(0)
Developer
Kizuki
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 hours ago
Last modified
Categories
Security
Limited permissionsShare
LinkedIn Jobs Scraper turns public LinkedIn job search results into clean JSON, CSV or Excel: one row per job with job ID, title, company, location and posting date and, read from the job page, the employment type, seniority level, industry, job function, the applicants line, the employer's LinkedIn page and the published pay range — all in one normalized schema (source: "linkedin"). It is built for monitoring rather than bulk export: with Only new jobs a scheduled run returns only the postings that no earlier run returned, so an hourly or daily run gives you a short list of what appeared instead of the whole search again.
It needs no login, no account cookies and no browser: it reads the same public search and job pages any visitor sees. Rows are charged only when they are returned — postings dropped by a filter or as duplicates, and a row whose job page stayed blocked, cost nothing.
This Actor is an independent tool. It is not affiliated with, endorsed by or sponsored by LinkedIn; the name only says which public job search it reads. Try it with an empty input: it runs a small default search (software engineer, United States, 10 jobs) and returns in about 11 seconds.
Use cases
- New-posting alerts. Save a search as a Task with Only new jobs and a schedule; each run returns only what appeared since the last one, ready for Slack, a sheet or a digest.
- One job table across sources. Every row has the same fields, so LinkedIn postings sit next to other sources you normalize the same way.
- Pay and seniority research. The pay range, seniority level, industry and job function of a posting, where the posting publishes them.
- Hiring-market tracking. Applicants counts and posting dates for one search, run after run.
- Agent and MCP use. A small
maxItemscall returns titles, companies and links for an assistant to read.
What data can you get?
| Field | Description |
|---|---|
source | linkedin |
jobId | LinkedIn's job posting ID (the number in the job page URL) |
companyBoard / companyName | Company name in lower case with hyphens, and as published |
title | Job title |
location | Location text as published (Berlin, Berlin, Germany, Remote) |
postedAt | Date the job was posted, YYYY-MM-DD (LinkedIn publishes the day, not the time) |
url | Job page on LinkedIn, without tracking parameters |
remote, workplaceType | true / "remote" when the search asked for remote jobs or the location says "Remote"; otherwise null (LinkedIn's guest pages do not publish a workplace type) |
employmentType | full_time, part_time, internship, contract or temporary, read from the job page (Read each job page only) |
seniority | Seniority level as the job page publishes it, for example Mid-Senior level (Read each job page only) |
industry / jobFunction | The job page's Industries and Job function criteria, for example Technology, Information and Internet (Read each job page only) |
applicantsCount / applicantsText | Applicants LinkedIn reports, as a number and as the page's own wording (Over 200 applicants -> 200) (Read each job page only) |
companyLinkedinUrl | The employer's LinkedIn page, taken from the job page (Read each job page only) |
compensationSummary / salaryMin / salaryMax / salaryCurrency / salaryPeriod | The pay range when the posting publishes one, as the page's text and as numbers with the currency and period (Read each job page only) |
description | Plain-text job description cut to 4,000 characters (Read each job page only) |
detailsStatus | How the job page went: read, blocked (LinkedIn refused it after the retries: the row keeps the listing fields, the page fields are null, and the row is not charged), unavailable (the page no longer showed a job) or null when Read each job page is off |
scrapedAt | When the row was collected |
isNew | With Only new jobs: whether an earlier run already returned the job; otherwise null |
Every row has every field. A value LinkedIn does not publish to guests is null — including country, department (LinkedIn's Industries criterion is an industry, not a department: it is in industry), the pay range when the posting publishes none, and applyUrl (the application form is behind a sign-in dialog; open url and apply there).
How to use LinkedIn Jobs Scraper
- Write the search: Keywords (
keywords:software engineer,react developer,nurse) and Location (location:Berlin, Germany,Japan,United States). LinkedIn matches keywords against the title and the description, so a keyword can return a job whose title does not contain it. - Optionally set Posted in the last N days (
daysSincePosted) and tick Remote jobs only (remoteOnly). - Leave Read each job page (
includeDetails) on (the default) for the description and the page fields; it costs one extra request per job. Turn it off for a bulk listing of titles, companies, locations and dates only. - Set Maximum results (
maxItems) and run the Actor. Read the dataset as JSON, CSV or Excel, or from the API. - For monitoring, save the input as a Task, tick Only new jobs (
onlyNewItems) and schedule it.
Input example
{"keywords": "data engineer","location": "Berlin, Germany","daysSincePosted": 7,"remoteOnly": false,"includeDetails": true,"onlyNewItems": true,"maxItems": 50}
Input
| Field | Default | What it does |
|---|---|---|
keywords | software engineer | Search text for LinkedIn's own search; matches titles and descriptions |
location | United States | Place name LinkedIn understands |
daysSincePosted | 0 | Keep jobs posted in the last N days (0: any, up to 30) |
remoteOnly | false | Keep LinkedIn's remote jobs |
includeDetails | true | Read each job page for the description and the page fields (one extra request per job) |
onlyNewItems | false | Return only jobs no earlier run of the same search returned |
maxItems | 10 | Maximum rows per run (1 to 500) |
maxConcurrency | 1 | How many requests the Actor may send at once (1 to 3); 3 reads a long list faster |
sessionMaxUsageCount | 50 | How many requests one proxy session serves before the Actor switches to another (1 to 50) |
proxyConfiguration | Apify proxy | Proxy settings (country, custom proxy URLs) |
proxyMode | staged | Which proxy the run may use: staged (the default), datacenter or residential |
Output
One row per posting, every field present, null where LinkedIn publishes nothing. Recording the posting's date, company and page fields in the same row is what makes a diff possible: with Only new jobs the next run can say what appeared.
Output example
Values below are illustrative.
{"source": "linkedin","jobId": "4474778507","companyBoard": "ingestro","companyName": "Ingestro","title": "Senior Frontend Engineer (gn)","department": null,"industry": "Technology, Information and Internet","jobFunction": "Engineering and Information Technology","team": null,"location": "Berlin, Berlin, Germany","secondaryLocations": [],"country": null,"remote": null,"workplaceType": null,"employmentType": "full_time","compensationSummary": "€85,000.00/yr - €105,000.00/yr","salaryMin": 85000,"salaryMax": 105000,"salaryCurrency": "EUR","salaryPeriod": "year","applicantsCount": 100,"applicantsText": "Over 100 applicants","companyLinkedinUrl": "https://www.linkedin.com/company/ingestro","detailsStatus": "read","postedAt": "2026-10-02","url": "https://de.linkedin.com/jobs/view/senior-frontend-engineer-gn-at-ingestro-4474778507","applyUrl": null,"description": "Build the reporting front end of a data platform ...","scrapedAt": "2026-11-06T04:52:00.000Z","isNew": null,"seniority": "Mid-Senior level"}
New jobs only
- Only new jobs (
onlyNewItems: true) returns only jobs that no earlier run with the same keywords, location, date window and remote flag returned. Each returned row hasisNew: true. When nothing is new, the run ends successfully with an empty dataset. - The history lives in a named key-value store in your Apify account (
state-linkedin-…), one record for the search. Changing the keywords, the location, the date window or the remote flag starts a new history; changingmaxItemsor Read each job page does not. - A job counts as returned only after it was written to the dataset. Jobs left out by
maxItemsor by your maximum cost come back as new on the next run. - Jobs not seen for 90 days are forgotten.
- Do not run the same search twice at the same time: both runs read the same history and return (and charge) the same new jobs.
- There is no closed-job report: LinkedIn's guest search is a ranked window of results, not the complete list of open postings, so a job missing from it is not known to be closed.
How much does it cost?
LinkedIn Jobs Scraper uses pay-per-event pricing: you pay for the jobs it returns, not for the time it runs. At the Free tier 1,000 jobs cost $1.20; at Gold, $0.70.
| Apify plan (discount tier) | Price per 1,000 jobs | Price per job |
|---|---|---|
| Free | $1.20 | $0.0012 |
| Starter (Bronze) | $0.90 | $0.0009 |
| Scale (Silver) | $0.80 | $0.0008 |
| Business (Gold) | $0.70 | $0.0007 |
Apify's Free plan includes USD 5 of monthly usage, which covers about 4,166 jobs at the Free tier price, run start fees aside.
- Run start fee: the platform default per run (see the Pricing tab).
- Jobs removed by the filters or as duplicates are not charged.
- A row whose job page stayed blocked (
detailsStatus: "blocked") is written but not charged: the page fields it would promise are empty. - With
onlyNewItems, jobs returned before are not charged again. - Maximum results and your run's maximum cost cap the charge; the Actor stops before it would go over.
Use with AI agents (MCP)
LinkedIn Jobs Scraper works as a tool for AI agents such as Claude, Cursor and n8n agents through the Apify MCP server. Load only this Actor with this server URL:
https://mcp.apify.com?tools=kizukidata/linkedin-jobs-feed
- Claude Code: run
claude mcp add apify "https://mcp.apify.com?tools=kizukidata/linkedin-jobs-feed" -t http. - Claude (web or desktop app): open Customize → Connectors, choose Add custom connector, name it
Apifyand paste the server URL above. - Cursor: add the server to
~/.cursor/mcp.json(or.cursor/mcp.jsonin a project):
{"mcpServers": {"apify": { "url": "https://mcp.apify.com?tools=kizukidata/linkedin-jobs-feed" }}}
- n8n: add the Apify node (on self-hosted n8n, install the community package
@apify/n8n-nodes-apify) and choose Run an Actor and get dataset; for an n8n AI Agent, add Apify Tool as a tool.
The server signs you in to Apify with OAuth. A client that cannot use OAuth can send your Apify API token in an Authorization: Bearer <APIFY_TOKEN> header instead.
What to send from an agent
Calling it with no arguments ({}) runs the small default search and returns 10 jobs in about 11 seconds. Pass maxItems to control how many jobs come back, and keywords / location / daysSincePosted / remoteOnly to narrow them. Read each job page is on by default; set includeDetails: false for a bulk listing of titles, companies, locations and dates only. When a run returns nothing, the dataset stays empty and the run's status message says why.
Integrations and API
Run the Actor from your own code or from an automation tool. Replace <APIFY_TOKEN> with your API token.
n8n, Make and Zapier
Use the Apify integration, choose Run Actor, and use the dataset items as the next step's input. A typical schedule job: keywords: "backend engineer", location: "Berlin, Germany", onlyNewItems: true, maxItems: 50, every hour. Each run returns only postings that appeared since the previous one, so the rows feed a Slack message, a sheet row or a notification without any further diffing on your side.
cURL
curl -X POST "https://api.apify.com/v2/acts/kizukidata~linkedin-jobs-feed/runs" \-H "Authorization: Bearer <APIFY_TOKEN>" \-H "Content-Type: application/json" \-d '{"keywords":"data engineer","location":"Berlin, Germany","onlyNewItems":true,"maxItems":50}'
Poll GET https://api.apify.com/v2/actor-runs/<RUN_ID> for the status, read defaultDatasetId, then fetch GET https://api.apify.com/v2/datasets/<DATASET_ID>/items?format=json.
Limitations
- Maximum results (
maxItems, default 10) caps each run. Runs started by AI agents usually have no spending limit, so this is the main cost control. - LinkedIn answers ten jobs per page. The Actor reads as many pages as
maxItemsneeds (aboutceil(maxItems / 10)), at most 50 pages, and stops earlier when the results end. - With Read each job page the run also sends one request per returned job, so 10 results take 11 requests and about 11 seconds.
- LinkedIn's guest search is a ranked window, not every open posting: the same search can omit jobs that exist. Widen it with keywords, a location or a date window.
- The posted date is a day, not a time, so "posted in the last 24 hours" is a day-level filter: the Actor asks LinkedIn for the shortest window that covers N and checks the published date again. A job whose date LinkedIn does not publish is kept.
- Pay data only where the posting publishes it. A posting that publishes none leaves the salary fields
null, and pay written only in the description stays indescription. A bare$on a posting outside the United States leavessalaryCurrencynull(it could be CAD, AUD or USD), while a prefixed or coded currency (CA$,€,GBP) is read as it is. - No workplace type is published to guests;
remotecomes from the remote filter or from a location that says "Remote", otherwise it isnull. companyBoardis a slug of the company name, not a LinkedIn company ID: two companies whose names slugify the same way share the identifier, butjobIdkeeps the rows apart.- Reposts: the duplicate rule drops a second row with the same company, title and location. Two genuinely different openings with the same title at the same company in the same city are also merged; the first one returned is kept.
- With Read each job page on, a job whose page answers 404 (the posting was removed between the search and the job page) is not returned in that run: the row comes from the job page. A page that answers without a job keeps the listing row and marks it
detailsStatus: "unavailable". - Descriptions are employer-written text; the Actor cleans the HTML but does not rewrite the wording.
- Rate limiting: LinkedIn throttles connections that search too frequently, and the Actor manages retries itself. When a search is throttled before any jobs can be read, the run ends with a
blockedstatus and nothing is charged. A run that already read jobs keeps them; a job page that stayed blocked is markeddetailsStatus: "blocked"and is not charged. Run it again later, or narrow the search so it needs fewer pages. - A keyword LinkedIn cannot match: LinkedIn sometimes ignores a search term and answers with a general feed. The Actor keeps a search page only when at least one card's title or company carries a word of the search; a page that answers another search gives 0 rows and
no_matches(nothing charged), instead of unrelated jobs. A search by company name still works, because the company line counts as an answer. - Each run writes an
OUTPUTrecord to its key-value store with the counts and the inputs it did not reach (uncheckedInputs).
FAQ
How do I get only the jobs posted since my last run?
Tick Only new jobs, save the input as a Task and schedule it. See New jobs only above.
Why do some rows have no description or employment type?
Those fields come from the job page, which is read only when Read each job page is on. Turning it on costs one extra request per job.
Why is a job I can see on LinkedIn missing?
LinkedIn's guest search shows a ranked window of results, and this Actor returns the first maxItems of it after de-duplication. Raise maxItems, narrow the keywords, or add a date window.
Is the remote flag reliable?
Only as far as LinkedIn's remote filter is: with Remote jobs only on, every row comes from LinkedIn's remote facet. Without it, remote is null unless the published location says "Remote". LinkedIn's guest pages do not state on-site or hybrid.
Is it legal to use this data?
The Actor reads pages that are public to any visitor: no login, no account cookies, no browser. LinkedIn's User Agreement prohibits automated collection from the service, and whether your use is permitted is your decision. If the rows contain personal data, handle it under the rules that apply to you, such as the GDPR in the EU. If you publish the rows, keep the url link and name LinkedIn as the source. This is not legal advice.
Related Kizuki Actors
Other Actors from Kizuki that work well with this one:
- LinkedIn Company Scraper — the employer behind each job: size, industry, followers and what changed.
- Google News Scraper — news about the companies you are watching.
Your feedback
Bugs, missing fields and feature requests go to the Actor's Issues tab in Apify Console. A job poster or employer who wants a posting left out can open an Issue with the job URL: postings are excluded by ID and by company before a row is written, and the request from the poster is answered in the Issue. Requests from third parties are not applied.