LinkedIn Job Postings Scraper - Salary, Company, Easy Apply avatar

LinkedIn Job Postings Scraper - Salary, Company, Easy Apply

Pricing

$1.49 / 1,000 jobs

Go to Apify Store
LinkedIn Job Postings Scraper - Salary, Company, Easy Apply

LinkedIn Job Postings Scraper - Salary, Company, Easy Apply

4646 unique jobs from one keyword, 4.65 times LinkedIn's 1000-job search ceiling. Scrape LinkedIn job postings with the full job description, 47 fields per posting, no login. Employment type, seniority and workplace filters are applied here, because LinkedIn ignores 8 of its own URL parameters.

Pricing

$1.49 / 1,000 jobs

Rating

0.0

(0)

Developer

Snow Leo Data

Snow Leo Data

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

a day ago

Last modified

Share

LinkedIn Jobs Scraper

Scrape LinkedIn job postings without a login, a cookie, a proxy or a browser. Title, company, location, pay range, employment type, seniority, job function, industries, applicant count and the full job description - 47 fields per posting, straight from LinkedIn's public guest pages.

Two things make this Actor different from the others in the niche, and both are measurements you can repeat, not claims:

  1. Its filters actually filter. LinkedIn's guest search accepts f_JT (employment type), f_WT (workplace), f_E (experience), f_SB (salary), f_F, f_I and sortBy - and acts on none of them. A scraper that passes those straight through hands you a filter that quietly does nothing. This one applies them itself, on the job page where the values really live.
  2. It goes past LinkedIn's 1000-job ceiling. One query can never return more than 1000 jobs - start=1000 is HTTP 400. Walking a ladder of freshness windows turned 1000 jobs into 4646 unique jobs from the same single query.

What you get

Every row carries, when LinkedIn publishes it:

GroupFields
Identityjob_id, job_url, apply_url, found_via, rank, scraped_at
Roletitle, employment_type, seniority_level, job_function, industries
Employercompany, company_url, company_slug, company_logo
Placelocation, city, region, country, workplace_type, workplace_signal
Timeposted_date, posted_ago, is_new_posting, hiring_badge
Paysalary_min, salary_max, salary_currency, salary_period, salary_text
Interestapplicants, applicants_precision, applicants_text
Applyapply_type, easy_apply
Job posterposter_name, poster_url, poster_headline
Textdescription, description_html, description_markdown, description_length
Contactscontact_emails, contact_phones, contact_links
Monitoringchange_type, changed_fields

How often each one is really filled, counted over 30 live job pages from five countries: title, company, company page, location, posting date, the four job criteria and the description - 30 of 30. Applicant count - 14 of 30. A published pay range - 2 of 30. Apply route - 30 of 30, of which 27 external and 3 Easy Apply. A named job poster with a link to their profile - 4 of 24, and included in the row price, where the nearest competitor charges $0.02 extra per job for it.

A second, larger census over the 100 rows of one live cloud run, all of them United States postings: applicant count filled on 77, a published pay range on 16, a named job poster on 18. The two samples disagree because they are different slices of LinkedIn - which is the point of showing both rather than one flattering number. What holds in both: pay is the field employers publish least often, so do not build a pipeline that assumes it. Nothing is invented to fill a gap: a field LinkedIn does not publish is null, and a numeric field is never an empty string.

Getting past the 1000-job ceiling

start=1000 answers HTTP 400. A full walk of software engineer in the United States, from start 0 to start 990, returned 1000 unique job ids and that was the end of that query.

f_TPR=r<seconds> is one of the parameters LinkedIn does honour, and it takes any number of seconds - not just the four the LinkedIn interface offers. Each window gets its own top-1000, and the windows are not nested slices of one result. Measured on that same query:

Freshness windowUnique jobsNew onesRunning total
no window100010001000
past hour9139101910
past 4 hours9507072617
past 12 hours9976383255
past 24 hours9483373592
past 3 days10004874079
past week10003894468
past month9931784646

That is 4.65 times the ceiling of one query, out of one keyword in one country, for 838 requests. Add a second keyword or a second location and each of them brings its own ladder. The ladder is on by default and never reaches outside the freshness you asked for: choose "Past 24 hours" and the widest rung is 24 hours.

The filters LinkedIn ignores, and what this Actor does about them

Measured three independent ways on 2026-09-17: comparing the returned job ids against the same query without the parameter, combining the parameter with one that does work, and reading the job pages of what came back.

ParameterWhat it claimsWhat it does
keywords, location, geoIdsearchworks
f_TPRposted withinworks - the id set moves with the window
f_Cone companyworks - 0 of 30 jobs overlap the unfiltered search
f_ALEasy Apply onlyworks - 0 of 30 overlap
f_EAunder 10 applicantsworks - 1 of 10 overlap
distanceradius in milesworks - 5 miles around Chicago returns Chicago, 100 miles reaches Milwaukee
f_JTemployment typeignored - 30 of 30 identical to no filter
f_WTworkplace typeignored - 30 of 30 identical
f_Eexperience levelignored - 30 of 30 identical
f_SBsalary bandignored - 30 of 30 identical
f_Fjob functionignored - 10 of 10 identical
f_Iindustryignored - 10 of 10 identical
f_BEbenefitsignored - 30 of 30 identical
sortByrelevance or dateignored - 30 of 30 identical

The ignored ones were retried as "I", as INTERNSHIP, as 1,2, under geoId instead of location, against the HTML search page instead of the API, and together with the working f_TPR. The id set never moved. The job pages settle it: a search with f_JT=I for internships returned six jobs whose pages all say "Employment type: Full-time" - the same six the unfiltered search returned.

So this Actor asks LinkedIn only for what LinkedIn honours, and applies employment type, seniority, workplace type, apply route and salary itself. When one of those filters is on, the job page is fetched automatically even if you turned descriptions off - otherwise the filter would read an empty field and drop every row.

The location trap

LinkedIn does not refuse a place it does not know. It silently answers with jobs from somewhere else. Measured: location=Zzqqxxnotaplace returns HTTP 200 and the same ten United States jobs as location=United States - zero of them in the place that was asked for.

"Check that the location is the one you asked for" is on by default. It drops rows that do not answer your location, puts the share that matched in the run report, and if almost nothing matched it names the reason instead of handing you another country's jobs. The comparison is loose on purpose: "Berlin, Germany" accepts "Berlin, Berlin, Germany", and "United States" accepts "Chantilly, VA".

Monitor mode: pay only for what is new

Turn on Monitor mode and the Actor remembers every job it has delivered. The next run returns only postings that are new or have changed, each labelled change_type NEW / UPDATED / UNCHANGED, and changed_fields says what moved and what it was before. Watched values: title, company, location, employment type, seniority, workplace type, pay range, apply route, applicant count, description length and posting date. The scrape timestamp is deliberately not watched - otherwise every row would look changed on every run.

A monitor run with nothing to report writes zero rows, and that is correct behaviour, not a failure: you are not charged for jobs you already have.

An empty result always says why

A green run with no rows is the worst thing a scraper can do to you. This one always names the reason in the log and in the empty_reason field of the REPORT record. Reasons that are decided by your input alone are checked before a single byte is downloaded, so you are not charged time for a result that was never possible:

no-target-given, no-search-terms-and-no-locations, all-job-ids-invalid, employment-type-filter-matches-nothing, seniority-filter-matches-nothing, workplace-filter-matches-nothing, date-not-understood, date-window-is-empty, freshness-window-excludes-the-date-window, easy-apply-only-with-external-apply-filter, salary-range-is-empty, charge-limit-reached.

Reasons that only become visible after the crawl: filtered-out, nothing-changed, source-returned-nothing, location-not-recognised, job-not-found, source-rate-limited, all-queries-unreachable.

The REPORT record also carries the per-filter drop counts, the queries that were run, how many cards were seen, how many job pages were opened, how many duplicates were skipped and the HTTP counters.

Modes

  • Search by keyword and location - the usual way. Every keyword and every location is a separate query with its own ceiling.
  • Job URLs or IDs - open exactly the postings you list. Accepts a full /jobs/view/ address or the bare job id, and can take them from another Actor's dataset.
  • One company - jobs of a single employer. LinkedIn's guest search filters by numeric company id only; a company slug in that parameter is ignored by the source, so this Actor refuses it loudly instead of quietly returning the whole search.

Honest limitations

  • The external apply link is not available to a guest. When a job applies on the employer's own site, LinkedIn puts that address behind the login wall. The row says apply_type: EXTERNAL and apply_url is the LinkedIn job page. Only a logged-in scraper can get the employer URL, and this Actor does not log in to anything.
  • Pay is rarely published. 2 job pages in 30 carried a range. A salary filter therefore leaves jobs with no published pay in the result by default - absence of data is not a mismatch. Turn on "Require a published salary" to drop them, knowing that drops about 28 of every 30 postings.
  • Workplace type is inferred, and every row says so. LinkedIn gives a guest no workplace field and ignores f_WT. The answer is read from the location, then the title, then the description, and workplace_signal names which one it came from - so you can keep only the rows you trust.
  • sortBy does nothing. Not this Actor's choice: the source ignores it. Order inside a query is LinkedIn's relevance order; rank records it.
  • There is no automatic split by city. The reference Actor in this niche offers one as its way past the ceiling; here the freshness ladder does that job and was measured at 4.65x, and several locations can be listed by hand, each with its own ceiling.
  • Company headcount, followers and firmographics are not included. Those live on the company page, not the job page, and would be an extra request and an extra charge per company.
  • One query is capped at 1000 jobs by LinkedIn, not by this Actor. The freshness ladder multiplies that, but there is no way to enumerate every job in a country.

Paste a LinkedIn search URL

The URL in your clipboard works as input. Its query string is unpacked into the plain fields - keyword, location, geo id, radius, freshness window, Easy Apply, under 10 applicants - rather than forwarded as-is. Forwarding it would also forward the parameters LinkedIn ignores, and you would believe your employment-type filter had been applied. Those parameters are listed in the run report under search_url_parameters_ignored_by_linkedin, and the matching fields of this Actor apply them for real.

Input in short

Search terms or LinkedIn search URLs, locations or geo ids, radius in miles, freshness window, Easy Apply only, under 10 applicants only, the ceiling ladder on or off, maximum results, a per-query quota, job ids to skip, whether to open the job page, description format (text, HTML, markdown, all three or none), contact extraction, and the filters: employment type, seniority level, workplace type, apply route, title contains / excludes, description contains, company contains, location contains, minimum and maximum salary, require a published salary, posted after / before, location verification, monitor mode, compact rows, drop empty fields, parallel job pages.

maxItems limits what is delivered, not what is crawled. Because the filters above run after download, stopping the crawl at the collected count would hand you nothing whenever a filter is narrow.

FAQ

Do I need a LinkedIn account, cookies or a proxy? No. The Actor uses only public guest endpoints that answer without any authentication. It never logs in and never touches an account.

Why does it open a second page per job? The search card carries 12 fields. The employment type, seniority, job function, industries, applicant count, pay range and the description live on the job page only. Turn "Open the job page" off for a fast, cheap list of cards; leave it on for the full row.

Will the same job be charged twice? No. Jobs are deduplicated by LinkedIn's own job id across every query and every freshness window in the run, and the report counts how many duplicates were skipped. Titles are cleaned of &nbsp; before comparison, because a title with an invisible leading space does not match a clean one.

How many results can I get? One query tops out at 1000. The freshness ladder took that to 4646 unique jobs from one keyword in one country. More keywords and more locations multiply it further.

Why is applicants sometimes 25 with applicants_precision: at-most? Because LinkedIn wrote "Be among the first 25 applicants", which means fewer than 25 people applied - a ceiling, not a count. "Over 200 applicants" gives at-least. The row tells you which it is instead of pretending to a number.

What happens if the run is interrupted? Rows already written stay written, and memory is saved in a finally block. Nothing is marked delivered before it has actually been delivered, so the next run continues rather than starting over or skipping what never arrived.

Can I feed job ids from another Actor? Yes - give the dataset id and the field name in the input, and this Actor will open those postings.

Is this legal? It reads pages LinkedIn serves to anyone with no account and no login, exactly as a browser would. You are responsible for how you use the data, including under GDPR and LinkedIn's own terms.