LinkedIn Salary Benchmark — Pay Ranges by Title & Location
Pricing
from $40.00 / 1,000 benchmarks
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
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
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.
| Field | What it is |
|---|---|
title, location, currency, periodBasis, datePosted, titleMatch | the benchmark you asked for; currency is null only when no posting stated pay |
jobsListed, jobsOffTitle, jobsRead, jobsClosed, jobsFailed | how 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, payShare | postings that state pay in this currency, and that as a share of jobsRead (0 to 1) |
min, p10, p25, median, p75, p90, max | the distribution of one number per posting: the middle of its pay range (a posting that says $100k–$140k counts once, at $120k) |
typicalRangeLow, typicalRangeHigh | the median advertised floor and ceiling: the range employers typically post |
statedPeriods | how the pay was written, e.g. {"year": 41, "hour": 6} (hourly × 2080, monthly × 12, weekly × 52, daily × 260) |
byExperience | LinkedIn's own seniority label → {n, median}. LinkedIn leaves most postings at "Not Applicable", so that is often the largest bucket |
byTitleLevel | level read from the title words (Intern, Junior, Senior, Lead / Principal, Manager / Director, No level in title) → {n, median} |
topCompanies | up to 10 companies with the most postings that state pay: {company, n, median} |
otherCurrencies | pay found in other currencies in the same search: [{currency, n}] |
sampleJobUrls | five postings whose pay sits closest to the median: open them to check the figure |
lowSample, note | lowSample is true when fewer than 5 postings state pay; note says why a row should be read with care |
previousMedian, medianChange, medianChangePct, previousJobsWithPay, previousObservedAt, isNew | the change since your previous run of the same title, location and currency (null on the first run, isNew is true) |
observedAt | when 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 texttitle | location. The location is a country, region or city as LinkedIn spells it;Worldwideworks too.maxJobsPerBenchmark— postings to read per benchmark, 10 to 500 (default 100).datePosted—past-month(default),past-weekorany.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.anykeeps 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 (
jobsWithPay0 to 2: one or two numbers are not a benchmark;notesays so), and a row that LinkedIn cut short by refusing requests and that ended up alowSample. - 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
payShareis 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 flaggedlowSampleand explained innote; 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, sopayShareis 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.
statedPeriodsshows 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.
jobsOffTitlesays how many were left out. - Fields that are empty on purpose:
previousMedian,medianChange,medianChangePct,previousJobsWithPayandpreviousObservedAtarenullon the first run of a title, location and currency;otherCurrenciesis[]when all pay was in one currency;noteisnullunless something needs saying; with no pay stated (jobsWithPay0) the currency and every figure arenulland 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.