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

(1)

Developer

Zyra

Zyra

Maintained by Community

Actor stats

2

Bookmarked

2

Total users

1

Monthly active users

2 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.
  • Incremental by design. skipJobIds drops postings you already have before they cost anything, so polling on a schedule stays cheap.

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.
experienceLevelsarrayinternship, entry_level, associate, mid_senior, director, executive
jobTypesarrayfull_time, part_time, contract, temporary, volunteer, internship, other
workplaceTypesarrayon_site, remote, hybrid
datePostedstringany_time, past_month, past_week, past_24_hours
salarystringMinimum USD band, 40k200k. LinkedIn applies this to US searches only.
sortBystringrelevance (LinkedIn's default) or date — use date when polling for what is new.
easyApplybooleanOnly postings applied to on LinkedIn itself.
companyIdsarrayNumeric LinkedIn company ids, to restrict the search to those employers.

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.

Narrowing the results

Applied before each posting is opened, so anything you discard costs nothing.

FieldTypeDescription
skipJobIdsarrayPostings you already have. Dropped before they are fetched — this is what makes a repeated run cheap.
titleIncludearrayKeep only titles containing at least one of these (case insensitive).
titleExcludearrayDrop titles containing any of these. Applied after the include list.

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:

{
"source": "linkedin",
"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",
"workplace_type": "Remote",
"posted_at": "2026-07-16T00:00: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
workplace_typeRemote, On-site or Hybrid, when stated
posted_atA real timestamp, not a string you have to parse
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

Sort by date, keep the ids you have seen, and skip them next time:

{
"keywords": "python developer",
"location": "Berlin",
"sortBy": "date",
"datePosted": "past_24_hours",
"maxItems": 100,
"skipJobIds": ["4417748171", "4448412664"]
}

Skipped postings are dropped before they are fetched, so a run that finds nothing new costs almost nothing.

Support

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