LinkedIn Jobs Scraper avatar

LinkedIn Jobs Scraper

Pricing

from $0.25 / 1,000 job postings

Go to Apify Store
LinkedIn Jobs Scraper

LinkedIn Jobs Scraper

Reliable LinkedIn jobs data API for production applications. Typed, structured postings -- descriptions, salaries, applicant counts and the job poster -- by search or by URL. No login, no cookies, no account of yours involved.

Pricing

from $0.25 / 1,000 job postings

Rating

5.0

(2)

Developer

Zyra

Zyra

Maintained by Community

Actor stats

3

Bookmarked

13

Total users

7

Monthly active users

9.3 hours

Issues response

6 days ago

Last modified

Categories

Share

A reliable LinkedIn jobs data API for production applications.

Query LinkedIn's public job listings and get back typed, structured records — job titles, companies, locations, salaries, full descriptions, applicant counts, and the person who posted the job where LinkedIn names them.

Built to be scheduled and depended on, not run once by hand:

  • Typed output. applicants is a number, posted_at is an ISO 8601 datetime, industries is a list. Nothing downstream has to parse strings.
  • Stable field names. Documented in the output schema and versioned, so the table you load into does not break under you.
  • Honest nulls. has_details tells you whether a posting's page was actually read, so a blank salary means "this job does not state one" rather than "we never looked".
  • Safe to re-run. A run that gets moved between machines mid-way resumes where it left off and never writes the same posting twice.
  • Only real filters. Every filter here is one LinkedIn actually applies. It accepts a dozen more and quietly ignores them; this actor refuses those rather than return a full page of postings that ignore what you asked for.

No login. No cookies. No LinkedIn account of yours is involved at any point. Everything returned is what LinkedIn shows a logged-out visitor.


What you can do with it

  • Job boards and aggregators — keep a feed of fresh listings for a role, a city, or a set of companies.
  • Recruitment and talent research — watch who your competitors are hiring and what they are paying.
  • Lead generation — a company hiring three sales engineers is a company with a budget. The poster's name and profile come with the listing, where LinkedIn shows one.
  • Market research — salary bands, remote-work adoption, and which skills keep appearing, over time.

Quick start

Paste a LinkedIn search URL, or fill in the fields:

{
"keywords": "python developer",
"location": "Berlin",
"maxItems": 100,
"fetchDetails": true
}

That returns 100 job postings with their full descriptions. To also get who posted each one:

{
"keywords": "recruiter",
"location": "United States",
"maxItems": 100,
"fetchDetails": true,
"withRecruiter": true
}

Input

Everything is optional, but a run needs something to search for: either jobUrls, or at least one of keywords, location, geoId, companyIds — or a searchUrl that carries them. A bad input fails immediately and tells you why, before any work is done.

What to search for

FieldTypeDescription
searchUrlstringPaste a LinkedIn jobs search URL and its filters are read out of it. Anything you also set below wins.
keywordsstringFree text, as you would type it into LinkedIn's search box.
locationstringA place name, e.g. Berlin or United Kingdom. Resolved to LinkedIn's own place id when the name is unambiguous.
geoIdstringLinkedIn's numeric id for a place. Use it when you already know exactly which "London" you mean.
distanceintegerRadius in miles around the location, 0100. LinkedIn ignores it for remote searches.
datePostedstringany_time, past_month, past_week, past_24_hours
easyApplybooleanOnly postings applied to on LinkedIn itself.
under10ApplicantsbooleanLinkedIn's own "under 10 applicants" filter. LinkedIn applies it itself, so it costs nothing extra — but the applicants field still usually reads 25, because the logged-out count is approximate and floors there.
companyIdsarrayNumeric LinkedIn company ids, to restrict the search to those employers.

The filters LinkedIn dropped

LinkedIn's public job search accepts every filter and quietly applies only some of them. As of September 2026 just 9 of the 24 parameters it accepts do anything at all; experience level, job type, job function, industry, workplace type, salary and sort-by are among the ones it discards. This is not subtle: search LinkedIn for volunteer roles and it returns full-time ones.

So this actor does not offer them. Sending one is a clear error naming what to do instead, because a filter that is accepted and then narrows nothing is worse than no filter: you get a full page of postings that ignore it, with nothing anywhere saying so.

You can still get the answer — from the data rather than the search. Switch Fetch full postings on and every row carries the facts those filters used to test, so you can filter the dataset yourself:

Instead ofFilter the dataset on
experienceLevelsseniorityMid-Senior level, Entry level, …
jobTypesemployment_typeFull-time, Contract, …
jobFunctionsjob_function — but match a substring: LinkedIn writes Engineering and Information Technology
industriesindustries — a list, e.g. ["Software Development"]
salarysalary — about one posting in ten states pay

workplaceTypes is the one with no answer: nothing on a public posting says whether it is remote, hybrid or on-site. Put remote in keywords instead — postings that offer it nearly always say so in the title or description.

A LinkedIn search URL pasted into searchUrl may carry any of these codes; they are read past rather than rejected, since they were already doing nothing in the tab the URL came from.

Or: specific postings

FieldTypeDescription
jobUrlsarrayFetch these exact postings instead of searching. A posting URL, a share link, or the bare numeric id all work. Every search field above is ignored.

How much, and how detailed

FieldTypeDefaultDescription
maxItemsinteger100How many postings to return, up to 1000.
fetchDetailsbooleantrueOpen each posting for its description, seniority, employment type, job function, industries and applicant count.
withRecruiterbooleanfalseAlso return who posted the job. Needs fetchDetails.
includeDescriptionHtmlbooleanfalseKeep the description's original formatting alongside the plain text.

Performance

FieldTypeDefaultDescription
concurrencyinteger5How many postings to fetch at once.
maxRetriesinteger2Retries when a request comes back blocked or rate limited.
requestTimeoutSecsinteger120Searches read LinkedIn live, so this is generous by default.

Output

One row per posting:

{
"job_id": "4417748171",
"job_url": "https://www.linkedin.com/jobs/view/software-engineer-python-at-acme-4417748171",
"title": "Software Engineer, Python",
"company": "Acme Inc.",
"company_url": "https://www.linkedin.com/company/acme",
"location": "Berlin, Berlin, Germany",
"posted_at": "2026-07-16T09:42:00Z",
"posted_text": "1 month ago",
"salary": null,
"seniority": "Mid-Senior level",
"employment_type": "Full-time",
"job_function": "Engineering",
"industries": ["IT Services and IT Consulting"],
"applicants": 171,
"benefits": "Be an early applicant",
"description": "About the role…",
"logo_url": "https://media.licdn.com/…",
"recruiter_name": "Jane Doe",
"recruiter_url": "https://www.linkedin.com/in/jane-doe",
"recruiter_headline": "Talent Acquisition Leader @ Acme",
"scraped_at": "2026-08-31T10:20:20.838463Z",
"has_details": true
}

Fields

FieldNotes
job_id, job_urlLinkedIn's own id, and the posting's public URL
title, company, company_url, locationFrom the search result
posted_atA real timestamp, not a string you have to parse. LinkedIn states only a date, so this is midnight UTC for anything older than a day; inside the last day the time of day is recovered from posted_text and is good to the minute or hour it names.
posted_textWhat LinkedIn displayed, e.g. 3 days ago
logo_url, benefitsThe company logo, and lines like "Be among the first 25 applicants"
salaryAs written in the posting. Most postings do not state one.
seniority, employment_type, job_functionWith fetchDetails
industriesA list, not a comma-joined string
applicantsA number, not "64 applicants"
descriptionPlain text. Add includeDescriptionHtml for the markup too.
recruiter_name, recruiter_url, recruiter_headlineWith withRecruiter
has_detailsWhether the posting itself was opened — see below
scraped_atWhen this row was read

Types are real types. applicants is 171, not "171 applicants". posted_at is an ISO 8601 timestamp. industries is a list. You should not have to write a parser for a scraper's output.

A field LinkedIn did not state comes back null — never an empty string, never a guess. has_details tells you whether the posting was actually opened, so you can tell "this job has no salary" apart from "this row was never enriched".

Things worth knowing

About 1,000 results per search. LinkedIn's public job search stops producing new listings somewhere past a thousand, whatever is asked for. For more than that, split the search — by location, by datePosted, or by company — and run it more often.

Roughly half of postings name a poster. withRecruiter returns the person LinkedIn shows under "Direct message the job poster". Many postings do not show one, and for those the three recruiter fields are null. That means "this posting names nobody", not "we failed to look".

easy_apply and apply_url are null. LinkedIn stopped showing logged-out visitors whether a posting is Easy Apply or where an off-site application goes. Rather than guess, these come back empty. The easyApply filter still works — you can restrict a search to Easy Apply postings, you just are not told which ones they are afterwards.

The same search twice returns a slightly different slice. LinkedIn rotates results for logged-out visitors. Runs are de-duplicated internally by job id, so you never get the same posting twice in one dataset, but two runs minutes apart will not match exactly.

Public data only. What a logged-out visitor can see. Recruiter contact details, applicant lists and anything behind a login are not included.

Location names can be ambiguous. "Turkey" is a town in North Carolina as well as a country. When a name is resolved to a place, the run summary records which place was chosen — check it if results look geographically odd, and set geoId to settle it.

Polling for new jobs

Poll for what is new, on a schedule:

{
"keywords": "python developer",
"location": "Berlin",
"datePosted": "past_24_hours",
"maxItems": 100
}

datePosted is applied by LinkedIn itself, so a run that finds nothing new costs almost nothing. job_id is stable across runs, so keep the ones you have and drop the repeats on your side; sort on posted_at, which is a real timestamp, if you need the newest first.

Support

Found a bug, or a field you need that is not here? Open an issue on the actor's Issues tab.