LinkedIn Salary Benchmark — Pay Ranges by Title & Location avatar

LinkedIn Salary Benchmark — Pay Ranges by Title & Location

Pricing

from $40.00 / 1,000 benchmarks

Go to Apify Store
LinkedIn Salary Benchmark — Pay Ranges by Title & Location

LinkedIn Salary Benchmark — Pay Ranges by Title & Location

Salary benchmarks from live LinkedIn postings: give a job title and a place, get the median and p10-p90 pay, by seniority and title level, with top companies and the change since your last run. Hourly and monthly pay is converted to yearly. No login.

Pricing

from $40.00 / 1,000 benchmarks

Rating

0.0

(0)

Developer

Northbell

Northbell

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Share

Give it job titles and places — data engineer | Germany, or {"title": "registered nurse", "location": "Canada"} — and get one row per benchmark: the median, p10 / p25 / p75 / p90 and range of the pay that live LinkedIn postings advertise, how many postings state pay at all, the pay by seniority and by level in the title, the top companies and five postings to check the number against. It reads up to hundreds of public postings per benchmark, turns hourly and monthly pay into yearly, and keeps each currency apart. $50 per 1,000 benchmarks ($0.05 each), no login, no API key.

Run it again — or put it on a monthly schedule — and every row also tells you what moved since the last run: the previous median, the change in money and in percent, and how many postings stood behind it. LinkedIn only shows the postings that are open today; this keeps the history for you.

What you get

One row per job title, location and currency (type: benchmark). All pay figures are yearly, in the currency the postings were written in.

FieldWhat it is
title, location, currency, periodBasis, datePosted, titleMatchthe benchmark you asked for; currency is null only when no posting stated pay
jobsListed, jobsOffTitle, jobsRead, jobsClosed, jobsFailedhow many postings LinkedIn listed, how many were left out because the title lacked your words, how many were opened and read, how many had closed since, how many could not be read
jobsWithPay, paySharepostings that state pay in this currency, and that as a share of jobsRead (0 to 1)
min, p10, p25, median, p75, p90, maxthe distribution of one number per posting: the middle of its pay range (a posting that says $100k–$140k counts once, at $120k)
typicalRangeLow, typicalRangeHighthe median advertised floor and ceiling: the range employers typically post
statedPeriodshow the pay was written, e.g. {"year": 41, "hour": 6} (hourly × 2080, monthly × 12, weekly × 52, daily × 260)
byExperienceLinkedIn's own seniority label → {n, median}. LinkedIn leaves most postings at "Not Applicable", so that is often the largest bucket
byTitleLevellevel read from the title words (Intern, Junior, Senior, Lead / Principal, Manager / Director, No level in title) → {n, median}
topCompaniesup to 10 companies with the most postings that state pay: {company, n, median}
otherCurrenciespay found in other currencies in the same search: [{currency, n}]
sampleJobUrlsfive postings whose pay sits closest to the median: open them to check the figure
lowSample, notelowSample is true when fewer than 5 postings state pay; note says why a row should be read with care
previousMedian, medianChange, medianChangePct, previousJobsWithPay, previousObservedAt, isNewthe change since your previous run of the same title, location and currency (null on the first run, isNew is true)
observedAtwhen the row was made

Searches that fail come back as free rows with type: "error" (input, title, location, kind, message).

Long lists and the run timeout. At the default pace a 100-posting benchmark takes about 6 minutes. Two minutes before the run's time limit, the benchmark in progress stops reading and returns what it read (its note says so), and the ones not started come back as free kind: "time-limit" rows — nothing read is lost to a timeout.

Use it for

  • Pricing an offer or a raise against what employers actually advertise for the same title in the same place, with a realistic range instead of a single average
  • Compensation and recruiting teams — a monthly benchmark per role and city, with the change since last month
  • Market research — which roles and countries are open about pay (payShare), how wide the posted ranges are, who pays most

Input

{
"benchmarks": [
{ "title": "data engineer", "location": "United States" },
"registered nurse | Canada"
],
"maxJobsPerBenchmark": 100,
"datePosted": "past-month"
}
  • benchmarks — up to 50 pairs per run, each {"title", "location"} or the text title | location. The location is a country, region or city as LinkedIn spells it; Worldwide works too.
  • maxJobsPerBenchmark — postings to read per benchmark, 10 to 500 (default 100).
  • datePosted — past-month (default), past-week or any.
  • titleMatch — all-words (default) keeps postings whose title contains every word you typed, so a data engineer benchmark is not filled up with software engineers. any keeps everything LinkedIn lists for those words (use it for a title with OR or quotes).
  • maxRequestsPerMinute — how fast pages are requested (default 20).

The previous median is remembered per title, location and currency in a named key-value store in your Apify account, so the second run already shows changes. Schedule it (Apify → Schedules) for a history. A run that LinkedIn cut short (a low sample after refusals) is not remembered, so the next run is still compared with your last proper benchmark.

Pricing

$0.05 per benchmark ($50 per 1,000), plus $0.01 per run. One benchmark is priced for the work behind it: it opens up to maxJobsPerBenchmark postings, and each posting is one page request, plus one request per 10 listed. In live runs that was about 1.2 requests per posting, so the default 100 postings is roughly 125 requests and about 6 minutes at the default pace of 20 requests a minute. At 256 MB that is around 0.025 compute units of platform usage (about a cent), already included in the price; nothing is billed on top. The cost grows with the postings you ask for: 500 postings is five times that, about half an hour per benchmark.

  • Free: error rows (a place LinkedIn does not know, no posting with your title, LinkedIn refusing), a row where fewer than 3 postings state pay (jobsWithPay 0 to 2: one or two numbers are not a benchmark; note says so), and a row that LinkedIn cut short by refusing requests and that ended up a lowSample.
  • A search whose pay comes in two currencies (say a Germany search with some postings in US dollars) gives one row per currency that has at least 5 postings with pay, each one benchmark; smaller groups are only counted in otherCurrencies. Most searches give one row.
  • The $0.01 run-start fee is the one charge every run pays, even one that returns nothing.

Notes

  • Public data only: what anyone sees on LinkedIn without signing in. Rows carry company names and posting links, nothing else from a posting: no descriptions, recruiter names, emails or phone numbers.
  • Only postings that state pay count. Some do not, so payShare is part of the answer: measured on 30 postings each (October 8, 2026), 77% of data engineer postings in the United States stated pay, 57% of registered nurse in Canada, 50% of accountant in London, 30% of software engineer in "Remote" and none of product manager in Germany (it is 1 in 30 on some days). Where few postings state pay the row is flagged lowSample and explained in note; it is still returned.
  • How the number is made. The pay comes from the posting's description and from LinkedIn's own "Base pay range" box when the employer filled it in (about 1 posting in 10), read by the same parser as the LinkedIn Salary Scraper. Numbers that are not pay (funding, bonuses, insurance limits) are rejected. "Up to X" and "from X" postings count at the number given. Yearly figures outside 8,000–2,000,000 are not treated as pay. Two spellings are straightened out before reading: LinkedIn's CA$120,000 (Canadian dollars) and a currency written after the numbers (101,300 - 160,000 USD annually). Pay in any other layout (digits grouped with spaces, a table, an image) is not seen, so payShare is a floor, not an exact share.
  • Currencies. Pay is grouped by the currency it was written in; nothing is converted between currencies. A bare "$" in a posting located in Canada, Australia, New Zealand, Singapore or Hong Kong is read as that country's dollar, otherwise as USD. Recognised: USD, GBP, EUR, CAD, AUD, INR and those dollars. Pay written in other currencies (¥, CHF, kr…) is read as "no pay stated".
  • Hourly pay is annualised at 2080 hours, so part-time and contract rates are shown as a full-time year. statedPeriods shows how much of a benchmark was hourly.
  • Title filter. LinkedIn's search is loose, so by default only postings whose title has every word you typed are read (a word may continue by up to three letters: engineer matches engineers and engineering, but java does not match JavaScript). The same posting listed twice (same company, title and place) counts once. jobsOffTitle says how many were left out.
  • Fields that are empty on purpose: previousMedian, medianChange, medianChangePct, previousJobsWithPay and previousObservedAt are null on the first run of a title, location and currency; otherCurrencies is [] when all pay was in one currency; note is null unless something needs saying; with no pay stated (jobsWithPay 0) the currency and every figure are null and the lists are empty.
  • No remote share. LinkedIn's public pages do not say whether a posting is remote, hybrid or on-site, and descriptions mention remote work in too many ways to count honestly, so no remote figure is made up.
  • LinkedIn lists at most about 1,000 postings per search, so 500 is the ceiling. If LinkedIn rate-limits a request the Actor waits 5 s, then 10 s, and tries again; if it keeps refusing, the run stops reading, keeps what it has and says so in note.

To get the postings themselves with their pay, see LinkedIn Salary Scraper. To see how fast applicants arrive at a posting, see LinkedIn Jobs Scraper with Applicant Count.

For AI agents

{"benchmarks": ["data engineer | United States"], "maxJobsPerBenchmark": 100} returns one row with median, p10 … p90, jobsWithPay, payShare, byTitleLevel and topCompanies, all yearly in currency. Rows have type: benchmark or error. If lowSample is true, read note before quoting the number.

**Something broke or looks wrong?** Open an issue on this Actor's Issues tab and I'll look at it. **Found it useful?** A short review on the Store page helps other people find it.