LinkedIn Public Jobs Scraper (no login) | Salary & Level avatar

LinkedIn Public Jobs Scraper (no login) | Salary & Level

Pricing

from $0.85 / 1,000 job extracteds

Go to Apify Store
LinkedIn Public Jobs Scraper (no login) | Salary & Level

LinkedIn Public Jobs Scraper (no login) | Salary & Level

Search public guest LinkedIn job listings with no login or cookies: keywords, location, date, remote/hybrid, level, type, company. Title, company, salary, seniority, industries, full description. Deduped. Pay per job.

Pricing

from $0.85 / 1,000 job extracteds

Rating

0.0

(0)

Developer

Detroit Web Agent - Open Data

Detroit Web Agent - Open Data

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 hours ago

Last modified

Share

LinkedIn Public Jobs Scraper (no login): salary, seniority, industries

Search LinkedIn's public job listings the same way a logged-out visitor sees them. No account, no cookies, no profiles. Each row is one job with company, location, posted date (ISO), salary when the posting shows one, seniority, employment type, industries, applicant count text, company details and the full description.

  • In short: LinkedIn Public Jobs Scraper (Apify actor transparent_meteorite/linkedin-public-jobs) reads only LinkedIn's public guest job pages (jobs-guest search and job-posting endpoints, plus the public company page) and returns clean, deduplicated job rows. No login, no cookies, no personal data.
  • Who it is for: recruiters and staffing teams, job boards and aggregators, sales teams using hiring as a buying signal, labour-market and salary researchers.
  • Input: keywords, location or geoId, date posted (24h / week / month), workplace (on-site / remote / hybrid), experience level, job type, company filter, max jobs (1 to 1,000 per run).
  • Output: 32 fields per job: jobId, title, company, companyUrl, location, postedAt, applicantsText, applicantsCount, salaryText/Min/Max/Currency/Period, seniority, employmentType, jobFunction, industries, workplaceType, applyType, applyUrl, jobUrl, isActivelyHiring, seven company fields, description (and optional HTML), scrapedAt.
  • Extras: dedupe across pages, new-jobs-only mode with a named key-value store, retries with exponential backoff, Apify Proxy with direct fallback.
  • Pricing: pay per event. $0.0017 per job on the Apify free plan, $0.00085 per job on paid plans, plus a start event of $0.00005 per GB of run memory. Duplicates and already-seen jobs are never charged.
  • Limits: public guest pages only; about 1,000 results per query; jobFunction is always null; salary only when the posting publishes it.

Run in 30 seconds

  1. Open the prefilled run (free Apify account; it creates a saved task with a working input).
  2. Press Start. The input is small on purpose, so the first run is quick and costs a few cents at most.
  3. Open the Output tab, switch to Table view, download as CSV, Excel or JSON.

Prefilled input:

{
"keywords": "software engineer",
"location": "United States",
"maxItems": 10
}

Ready-made presets by use case (each opens as a saved task you can edit):

Why choose this over alternatives

  • No login and no cookies: only the pages LinkedIn serves to logged-out visitors.
  • Lower price per job on the free plan and on paid plans than the alternative we tested.
  • Deduped across pages, with a new-jobs-only mode for schedules.
  • On the 16 fields both tools return, fill rates were level in our test; see the table.
This actorTypical alternatives
Rows returned for a 25-job request25 of 2525 of 25
Time for 25 jobs27 to 31 s81 s
Price per job, free plan$0.0017$0.002
Price per job, paid plans$0.00085$0.001
Fill rate on the 16 shared fields92.2%92.0%
Company logo present25 of 25not measured

Measured 2026-10-11 (source: head-to-head test runs on the same query, "data analyst", United States, past week, 25 jobs each, one run per side, both runs succeeded). One run each is a small sample; your timings will vary with LinkedIn's rate limiting.

What it does and who it is for

You describe a job search (keywords, place, filters). The actor pages through LinkedIn's public guest search, opens the public posting page for each job, optionally reads the public company page once per company, and writes one flat row per job to an Apify dataset. You can download the dataset as JSON, CSV, Excel, XML or HTML, or read it through the API.

It is built for people who need job listings as data:

  • Recruiters and staffing teams tracking which companies are hiring for a role in a region.
  • Job boards and aggregators that need fresh listings with a stable id for upserts.
  • Sales and research teams that treat hiring as a signal (a company posting ten data roles is investing in data).
  • Salary and labour-market researchers who want the pay ranges that postings publish, parsed into numbers.

It is not a profile scraper. It never reads member profiles and never outputs recruiter or poster names.

Quick start

  1. Enter keywords (for example data analyst) and a location.
  2. Optionally narrow by date posted, remote/hybrid, level, job type or company.
  3. Set Max jobs and run. Download as JSON, CSV or Excel, or call the API.
{ "keywords": "data analyst", "location": "United States", "datePosted": "week", "workplace": ["remote"], "maxItems": 50 }

What you get: output fields

Every row has the same 31 keys (32 when you ask for description HTML). A field is null when LinkedIn does not publish it for that job.

FieldTypeMeaning
jobIdstringLinkedIn's job id. Stable, use it as your upsert key
jobUrlstringClean public job URL, tracking parameters removed
title, company, locationstringAs shown on the public listing
companyUrlstringPublic LinkedIn company page URL
postedAtstringISO timestamp. LinkedIn publishes the date only, so the time part is midnight UTC
applicantsTextstringText exactly as shown, for example "Over 200 applicants". Null when not shown
applicantsCountnumberThe number parsed from applicantsText (200 for "Over 200 applicants"). Null when LinkedIn shows no count
salaryTextstringThe pay text as posted, for example $175,000.00/yr - $210,000.00/yr. Null when the posting publishes no pay
salaryMin, salaryMaxnumberParsed from salaryText
salaryCurrency, salaryPeriodstringFor example USD and year
seniority, employmentTypestringThe posting's own criteria, for example "Mid-Senior level", "Full-time". LinkedIn itself often shows "Not Applicable" for seniority
jobFunctionnullAlways null today: LinkedIn no longer shows job function to logged-out visitors. The key is kept so your columns stay stable
industriesarrayThe posting's industries. Split from LinkedIn's sentence, so the last item can begin with "and"
workplaceTypestringremote, hybrid or onsite. Exact when you filter to one workplace type; otherwise inferred from title, location and description when stated, else null
applyTypestringlinkedin (Easy Apply) or external
applyUrlstringThe employer's application URL when the public page exposes it, otherwise the LinkedIn job URL
isActivelyHiringbooleantrue when the listing carries LinkedIn's "actively hiring" badge, else null
companyDescription, companyWebsite, companyIndustrystringFrom the public company page (needs includeCompanyDetails, on by default)
companySize, companyHeadquartersstringFor example "11-50 employees", "New York, New York"
companyLogostringLogo image URL, with a fallback to the logo on the search card
companySloganstringThe company tagline. Null when the company publishes none
companyAddressobject{streetAddress, addressLocality, addressRegion, postalCode, addressCountry} from the company page's structured data. Passed through as published
descriptionstringPlain-text job description. Null when includeDescription is off
descriptionHtmlstringOnly when includeDescriptionHtml is on
scrapedAtstringISO timestamp of extraction

Sample output

A real row from a test run on 2026-10-11 (build 0.1.9). The two long text fields and the logo URL are shortened with ...; nothing else is changed.

{
"jobId": "4438505532",
"title": "Senior Data Analyst",
"company": "Confido",
"companyUrl": "https://www.linkedin.com/company/confidotech",
"location": "New York, NY",
"postedAt": "2026-10-03T00:00:00.000Z",
"applicantsText": "Over 200 applicants",
"salaryText": "$175,000.00/yr - $210,000.00/yr",
"salaryMin": 175000,
"salaryMax": 210000,
"salaryCurrency": "USD",
"salaryPeriod": "year",
"seniority": "Not Applicable",
"employmentType": "Full-time",
"jobFunction": null,
"industries": ["Software Development"],
"workplaceType": null,
"applyType": "external",
"applyUrl": "https://www.linkedin.com/jobs/view/senior-data-analyst-at-confido-4438505532",
"jobUrl": "https://www.linkedin.com/jobs/view/senior-data-analyst-at-confido-4438505532",
"isActivelyHiring": null,
"companyDescription": "Confido is the AI infrastructure powering CPG brands from deduction to production plan...",
"companyLogo": "https://media.licdn.com/dms/image/v2/D4E0BAQH_cCz9VXIQ0A/company-logo_200_200/...",
"companyWebsite": "https://www.confidotech.com",
"companyIndustry": "Software Development",
"companySize": "11-50 employees",
"companyHeadquarters": "New York, New York",
"companySlogan": "The AI Infrastructure for CPG Brands from Deduction to Production Plan",
"companyAddress": {
"streetAddress": null,
"addressLocality": "New York",
"addressRegion": "New York",
"postalCode": "10001",
"addressCountry": "US"
},
"description": "The brands on your kitchen shelf are run by people doing extraordinary work that nobody sees...",
"scrapedAt": "2026-10-11T05:09:51.264Z"
}

Note what this row shows honestly: applyType is external but applyUrl is still the LinkedIn URL, because the public page did not expose the employer's link, and workplaceType is null because the search had no single workplace filter and the posting did not state one.

Input options

No field is required by the form, but an empty input runs the example search data analyst in United States.

InputDefaultWhat it does
keywordsnoneJob title or skill, for example registered nurse
locationUnited States when no geoId is givenCity, region or country as you would type it on LinkedIn, for example Detroit, Michigan
geoIdnoneLinkedIn's exact location id, for example 103644278 for the United States. Use it with or instead of location when a place name is ambiguous
datePostedanyany, 24h, week or month
workplaceallAny of onsite, remote, hybrid
experienceLevelallAny of internship, entry, associate, mid-senior, director, executive
jobTypeallAny of full-time, part-time, contract, temporary, volunteer, internship, other
companyFilterany companyNumeric LinkedIn company ids are passed to LinkedIn's own filter. Company names or linkedin.com/company/<slug> URLs are matched against the company name on each listing
searchUrlsnonePaste LinkedIn job search URLs (or single linkedin.com/jobs/view/... URLs). The URL's keywords, location, geoId, date, workplace, experience, job type, company, distance, Easy Apply and under-10-applicants filters are used; anything the URL does not set falls back to the fields here
splitByLocationnoneList of locations. One search per location, results merged and deduplicated by job id, maxItems shared fairly between them
distance0 (LinkedIn default)Radius in miles around a city-level location
easyApplyOnlyfalseOnly Easy Apply jobs
under10ApplicantsfalseOnly jobs with fewer than 10 applicants so far
companyIdsnoneNumeric LinkedIn company ids, filtered by LinkedIn itself
minAnnualSalary0 (off)Keep only jobs whose published pay (converted to a yearly figure) is at least this number. Jobs without published pay are dropped when set, and dropped jobs are not charged
maxItems25Stop after this many jobs, 1 to 1,000
includeDescriptiontruePlain-text description. Same price either way
includeDescriptionHtmlfalseAdds descriptionHtml with the original formatting
includeCompanyDetailstrueAdds the company fields. One extra request per unique company, cached for the run, skipped automatically when LinkedIn walls company pages
onlyNewSinceLastRunfalseSkip jobs delivered by earlier runs that share the same state store
stateStoreNamelinkedin-public-jobs-seenNamed key-value store that remembers delivered job ids
proxyConfigurationApify ProxyFalls back to a direct connection if the proxy keeps failing

Filters & search URL

Set the filters on LinkedIn's jobs page, copy the address bar, and paste it into searchUrls. The actor reads the same filters from the URL and runs them through LinkedIn's public no-login endpoint. Example: https://www.linkedin.com/jobs/search/?keywords=registered%20nurse&location=Detroit&f_WT=2&f_TPR=r604800&f_AL=true&distance=25.

Checked against live LinkedIn responses on 2026-10-13: date posted, workplace type, experience level, job type, company id, distance, Easy Apply and under-10-applicants change the results. LinkedIn's public endpoint ignores its salary bucket (f_SB2) and sort (sortBy) parameters, so they are not sent. Use minAnnualSalary for a salary floor (applied by the actor) and read postedAt to sort recent-first yourself.

Input examples

Remote contract roles posted in the last 24 hours:

{ "keywords": "react developer", "location": "United Kingdom", "datePosted": "24h", "workplace": ["remote"], "jobType": ["contract"], "maxItems": 100 }

Entry and associate roles at named companies. Names are matched as text against the listing's company name, so a name filter can scan many listings to find few matches; listings that do not match are not delivered and not charged:

{ "keywords": "software engineer", "location": "United States", "experienceLevel": ["entry", "associate"], "companyFilter": ["Google", "https://www.linkedin.com/company/stripe"], "maxItems": 200 }

A saved search that returns only jobs it has not delivered before:

{ "keywords": "data analyst", "location": "Detroit, Michigan", "datePosted": "week", "onlyNewSinceLastRun": true, "stateStoreName": "detroit-analyst-jobs", "maxItems": 100 }

Pricing

Pay per event. There is no subscription and no rental fee for this actor.

EventApify free planPaid plans (Bronze to Diamond)
Job extracted (one per job row delivered)$0.0017$0.00085
Actor start (one per GB of run memory, minimum one)$0.00005$0.00005

The default run memory is 4 GB, so the start costs $0.0002 per run unless you lower the memory in the run options.

Worked examples at the default memory:

Jobs deliveredFree planPaid plans
25 (the form default)25 x $0.0017 + $0.0002 = $0.042725 x $0.00085 + $0.0002 = $0.0215
100$0.1702$0.0852
1,000 (the maximum for one run)$1.7002$0.8502
10,000 (ten runs of 1,000)$17.002$8.502

A daily schedule that finds 50 new jobs every day delivers 1,500 jobs in 30 days: 1,500 x $0.00085 + 30 x $0.0002 = $1.281 on a paid plan, or $2.556 on the free plan.

Apify's free plan includes $5 of monthly platform credit, which covers about 2,900 jobs with this actor at the free-plan price.

What is not charged: duplicates inside a run, jobs skipped by new-only mode, listings removed by a company-name filter, and jobs whose page has been taken down (HTTP 404 or 410). To cap spend, set Maximum cost per run in the run options; the actor checks the remaining budget before each job and stops cleanly when the limit is reached.

How to run it

In Apify Console

Open the actor, fill in the form and press Start. Rows appear in the Output tab while the run is going. The status line shows progress as delivered/requested jobs. When the run ends, use Export for JSON, CSV, Excel, XML or HTML.

From the API or a client library

Set APIFY_TOKEN to your token from Apify Console > Settings > API & Integrations.

curl -X POST "https://api.apify.com/v2/acts/transparent_meteorite~linkedin-public-jobs/runs?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"keywords":"data analyst","location":"United States","datePosted":"week","maxItems":25}'

Python (pip install apify-client)

import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run_input = {"keywords": "data analyst", "location": "United States", "datePosted": "week", "maxItems": 25}
run = client.actor("transparent_meteorite/linkedin-public-jobs").call(run_input=run_input)
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
print(job["jobId"], job["title"], job["company"], job["salaryText"])

JavaScript / Node.js (npm install apify-client)

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('transparent_meteorite/linkedin-public-jobs').call({
keywords: 'data analyst', location: 'United States', datePosted: 'week', maxItems: 25,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, 'jobs');

On a schedule

Create a schedule in Apify Console (Schedules > Create new), add this actor with your saved input, and pick a cron expression such as 0 7 * * * for 07:00 every day. Turn on onlyNewSinceLastRun so each run returns, and charges for, only the jobs you have not received yet.

With n8n, Make, Zapier or AI agents

Start the actor with the Apify API (POST /v2/acts/<actor>/runs) or the Apify client in Python or JavaScript, wait for the run, then read GET /v2/datasets/<id>/items. The same input works as an n8n, Make or Zapier Apify node, and any MCP-capable agent can call it as an Apify tool. Every row has a stable jobId for upserts.

Webhooks work as for any Apify actor: add one for the "run succeeded" event and your endpoint receives the run id and dataset id as soon as the data is ready.

New jobs only (monitor mode)

Turn on onlyNewSinceLastRun and keep the same stateStoreName between runs. The actor stores the id of every job it delivers in that named key-value store. On the next run, jobs already in the store are skipped before their page is fetched and are not charged.

  • Use one store name per saved search, for example detroit-analyst-jobs and remote-react-contracts. Two searches sharing a store would hide each other's overlaps.
  • Ids are kept for 90 days, then dropped.
  • A job is marked as seen only after its row is pushed and charged, so an interrupted run does not lose jobs.
  • The mode reports new jobs. It does not report jobs that were removed or edited.

Run summary record

Each run writes a record named OUTPUT to its key-value store. It tells you what happened without reading the log:

KeyMeaning
queryThe search as the actor understood it
pagesFetched, cardsSeenSearch pages read and listings seen
duplicates, filteredOut, skippedAlreadySeenListings not delivered, and why
detailFailuresJob pages that could not be read after retries
delivered, chargeCallsRows written and events charged (these match)
chargeLimitReachedTrue when your maximum cost stopped the run
proxy, httpProxy used, and request, retry, blocked and failure counts
warningsPlain-language notes, for example that company pages were walled

Limits and honest caveats

  • Public guest pages only. No login, no cookies, no private data. Anything LinkedIn shows only to signed-in members is out of reach, including job function, the exact applicant number and most external apply links.
  • About 1,000 results per query. That is LinkedIn's own cap on the public search, and maxItems stops at 1,000 for the same reason. Split bigger searches by location, date window or job type.
  • Salary is there only when the posting publishes it. Many postings publish no pay, and those rows have null salary fields. The actor never estimates a salary.
  • Company fields depend on the public company page. In a 30-job test run, companyAddress was filled on 27 rows and companySlogan on 14; many companies publish no tagline. Some addresses carry LinkedIn's own placeholder text in streetAddress (for example "Headquarters"); it is passed through as published. If LinkedIn walls company pages for the run's IP, the company fields are skipped and a warning is written to OUTPUT.
  • A job whose detail page cannot be read is still delivered with the search-card fields (title, company, location, posted date, URL) and null detail fields, and it is charged. OUTPUT.detailFailures counts these. Jobs that return 404 or 410 are dropped and not charged.
  • Rate limiting. LinkedIn limits anonymous traffic. The actor retries each request up to 4 times with exponential backoff and rotates through the proxy. If LinkedIn keeps refusing, the run ends with the rows it has and a warning in OUTPUT; it never invents rows.
  • Order. Rows come in LinkedIn's own result order for the query, not strictly newest first.

FAQ

Do I need a LinkedIn account or cookies? No. The actor has no login step and sends no cookies.

Why is jobFunction always null? LinkedIn removed job function from the pages it serves to logged-out visitors. The column stays so that existing exports keep their shape.

Why does applyUrl equal the LinkedIn URL when applyType is external? The public page tells us the application happens on the employer's site but rarely exposes the link itself. applyType tells you which case you have.

Can I get more than 1,000 jobs? Yes, across several runs. Split the search by location (states, cities), by datePosted or by jobType, and merge on jobId.

How fast is it? A 25-job run took 27 to 31 seconds in our test on 2026-10-11, and a 30-job run with company details took 72 seconds. Speed depends on how hard LinkedIn is rate limiting at that moment.

Does it return recruiter or poster names? No. That is personal data and the actor does not collect it.

How do I search one country precisely? Use geoId. Free-text locations are interpreted by LinkedIn and can match a city of the same name elsewhere.

Is the description complete? Yes, the full public text. Turn on includeDescriptionHtml if you need the original formatting.

Troubleshooting

SymptomLikely cause and fix
Zero rows and a warning that LinkedIn kept refusing requestsRate limit on the run's IP. Run again later, or choose a different proxy group in the Proxy field
Fewer rows than maxItemsThe search has fewer public results, a company-name filter removed listings (OUTPUT.filteredOut), new-only mode skipped them (OUTPUT.skippedAlreadySeen), or your maximum cost was reached
Company fields are all nullCompany pages were walled for this run (see OUTPUT.warnings) or includeCompanyDetails is off
New-only run returns everything againThe stateStoreName changed, or the store was deleted. Keep the name identical between runs
Jobs from the wrong placeAmbiguous place name. Use geoId

Terms & responsible use

This actor requests only pages LinkedIn serves publicly to logged-out visitors. It does not log in, use cookies, bypass authentication or collect personal profiles. LinkedIn's User Agreement restricts automated access and scraping, and rules differ by country and by what you do with the data. You are responsible for how you use the output: respect LinkedIn's terms and applicable law (including privacy law), keep volumes modest, do not republish descriptions wholesale, and do not use the data to contact individuals. This is not legal advice. This actor is not affiliated with or endorsed by LinkedIn.

More resources

Works well with

Other actors from Detroit Web Agent - Open Data that pair with this one:

Changelog

  • 2026-10-09: First version (private): public guest search and job pages, salary parsing, dedupe, new-jobs-only mode.
  • 2026-10-11: Company logo falls back to the search-card logo when the company page is walled (build 0.1.8).
  • 2026-10-11: Added companySlogan and companyAddress from the public company page's structured data (build 0.1.9).
  • 2026-10-12: README expanded: full field table, sample output, pricing examples, API and scheduling guides, limits, FAQ and troubleshooting.
  • 2026-10-13: Added a Run in 30 seconds section with a one-click prefilled run link, saved use-case presets and cross-links to pairing actors.
  • 2026-10-13: Added searchUrls (paste a LinkedIn search or job URL), splitByLocation, distance, easyApplyOnly, under10Applicants, companyIds, minAnnualSalary and the numeric applicantsCount field.

Support

Found a job that should have been returned, a field that parsed wrongly, or need a field added? Open an issue on this actor's Issues tab with the run URL and what you expected. The run URL lets us see the exact input and the OUTPUT record.

Need more than the data?

This actor is built by Detroit Web Agent, a small Detroit studio that builds websites, lead pages and simple data tools for small businesses. If you want this data wired into a dashboard or an alert, get in touch.


Built and maintained by Detroit Web Agent - Open Data (Detroit, Michigan). Questions, bugs or a field you need: open an issue on this Actor's Issues tab and we will reply there.

Use it from AI assistants (MCP)

LinkedIn Public Jobs Scraper works as a tool for AI assistants through the official Apify MCP server. Add this server URL to any MCP client (Claude Desktop, Claude Code, Cursor, ChatGPT connectors, VS Code):

https://mcp.apify.com/?actors=transparent_meteorite/linkedin-public-jobs
{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com/?actors=transparent_meteorite/linkedin-public-jobs",
"headers": { "Authorization": "Bearer <YOUR_APIFY_TOKEN>" }
}
}
}