Skip to main content

Salary Benchmarking with Claude: Pay Bands from Live Job Ads

· 14 min read
Yassine El Haddad
Software Developer & Automation Specialist

I build production AI agents, web scrapers, and automation pipelines. Most of what I publish here comes from the actual problems they run into: proxies that get banned, anti-bot stacks that fingerprint your client, RAG that drifts when the underlying data moves. Stack: Python, TypeScript, Go, FastAPI, LangChain, Crawlee, Playwright, deployed on AWS, GCP, and Cloudflare.

Quick Answer

Connect Apify to Claude, then ask Claude to pull current job ads for one title and city from Indeed and LinkedIn, keep only the ads that are really the same job, read the posted pay range from each, convert everything to annual pay, and report the 25th, 50th and 75th percentiles with the sample size. In our test for Data Analyst in Denver, 124 of 137 Indeed ads had a usable pay range, and that Indeed run cost about a cent.

Salary surveys are expensive, a year old, and built on job titles you cannot inspect. Job ads are the opposite: free to read, posted this month, and in a growing number of places legally required to state a pay range. What they lack is cleanup. Half the ads for "Data Analyst" are not data analyst jobs, and half the pay ranges hide inside the description text. That cleanup is reading work, which is where Claude earns its place.

What you end up with​

One chat and a short report:

  • A pay band: P25, median and P75 of annual base pay, with the number of ads behind each figure.
  • A split by seniority where the sample is big enough to support one.
  • The evidence table: every ad that counted, with employer, title, posted range and link, so anyone can check the number.
  • An exclusions list: the ads Claude dropped and why (wrong role, no pay, a range for another city, too old).

Use it to sanity-check an offer, set a hiring budget, or prepare a raise conversation. It measures advertised base pay, which is not the same as what people end up being paid. The limits are covered below.

Let Claude run the benchmark for you​

Paste this into Claude (Desktop or Cowork) with the Apify connector enabled. Claude interviews you, shows you the exact searches and an upper cost bound before spending anything, then does the cleanup and the maths. The walkthrough after it explains each step if you want to understand or adapt it.

Benchmark a salary from live job adsInteractive setupPaste into Claude

Claude asks what role and market you mean, confirms the searches and their maximum cost, then builds the pay band with its evidence.

  1. Asks for the job title, acceptable title variants, city, country and seniority, one question at a time
  2. Checks the Apify connector is available, and explains how to add it if not
  3. Shows the two searches and the maximum cost, and waits for your go-ahead
  4. Classifies every ad, extracts pay, and reports P25/P50/P75 with the evidence table
Help me benchmark the salary for a role using live job ads, through the Apify connector.
Work through these stages in order. Ask me ONE question at a time.

1. INTERVIEW. Ask me, one at a time:
- the job title, and which variants count as the same job
(e.g. "Data Analyst II" yes, "Business Analyst" no)
- the city, and the country
- the seniority I care about (entry, mid, senior, or all)
- what the number is for (an offer, a hiring budget, a raise), so you know
whether to emphasise the median or the upper quartile

2. CHECK THE CONNECTOR. Confirm you can use Apify tools (for example call-actor
and get-dataset-items). If you cannot, stop and tell me to add Apify as a
connector with this URL, then wait:
https://mcp.apify.com?tools=actors,valig/indeed-jobs-scraper,curious_coder/linkedin-jobs-scraper

3. CONFIRM BEFORE SPENDING. Show me the two runs below with my values filled in,
and the maximum cost (Indeed: about $0.0001 per ad; LinkedIn: about $0.002 per
job on the free plan). Wait for my "go".
- valig/indeed-jobs-scraper: title, location, country (two-letter code),
limit 150
- curious_coder/linkedin-jobs-scraper: keywords, location, datePosted
"pastMonth", limitPerSource 50, scrapeCompany false

4. CLEAN. For every ad:
- Classify the title as MATCH, CLOSE or EXCLUDE against my definition.
Show me the CLOSE list and ask whether to include it.
- Take pay from the structured salary field. If it is empty, look for a pay
range in the description text. If the range is stated for a different
location (e.g. "New York, NY Salary Range" on a Denver ad), drop it.
- Annualise: hourly x 2080, monthly x 12. Base pay only; ignore bonus and equity.
- Use the midpoint of each range. Flag ranges where max is more than 2x min.
- Drop duplicates (same employer and same title) and ads older than 90 days.

5. REPORT.
- P25, median and P75 of annual base pay, with n. If n is under 15, say the
band is indicative only.
- The same split by seniority, only for groups with n of 8 or more.
- A table of every ad used: employer, title, posted range, midpoint, link.
- A list of excluded ads with the reason.
- A short paragraph on what could bias this number.

Never invent a salary. If an ad does not state pay, it does not count.

Claude asks before each change it makes. Read each step before you approve it.

Compare two citiesPaste into Claude

Run this in the same chat after the first benchmark.

Run the same benchmark, with the same title definition and cleaning rules, for
[second city]. Put both bands side by side with n for each, and tell me whether the
difference is bigger than the spread inside each city.

A real run: Data Analyst in Denver​

We ran the two scrapers on 21 October 2026 for "Data Analyst" in Denver, CO: 137 ads from Indeed with no date filter, and a 25-job LinkedIn sample from the past month. Colorado has required pay ranges in job postings since 2021, which is why we picked it. The numbers below come from those runs, with the cleaning steps done by a script so they are reproducible. They describe one search on one day, not the Denver market.

Finding 1: title drift moves the median by $20,000​

Indeed's search for "Data Analyst" returns analyst jobs of every kind: business analysts, BI analysts, operations analysts, a "Principal Analyst, Identity Analytics". Of the 137 ads, 124 carried a usable pay range. Here is what that band looks like before and after keeping only the titles that contain "Data Analyst":

Sample (Indeed, Denver, 21 Oct 2026)Ads with payP25MedianP75
Everything the search returned124$79,341$104,800$124,738
Titles containing "Data Analyst"26$72,622$84,618$107,500

The unfiltered median is about $20,000 higher, because senior BI and analytics roles at large tech employers pull it up. This is the single biggest source of error in DIY salary benchmarks, and it is why the setup prompt makes Claude classify every title and show you the borderline ones. A strict "title contains" filter is crude in the other direction (it drops "Reporting Analyst", which may well be the same job), so the CLOSE list is a decision for you, not for the model.

Indeed also labels many ads with a seniority level. Across all 124 analyst-type ads with pay:

Indeed seniority labelAdsP25MedianP75
Mid-level67$75,229$85,300$108,200
Senior level52$105,825$120,250$139,012
Entry level5n too small

Finding 2: on LinkedIn, the pay is in the description​

The LinkedIn scraper returns a salary field, but it was filled for only 2 of 25 jobs. The description text of 23 of 25 contained a pay range, in a dozen different formats:

Annual Salary Range: $99,000 - $124,000. This position is also eligible for a target annual bonus...
US: $124000 - $177000 (USD) + 15% bonus target + equity + benefits
Hiring Salary for this position is $67,074- $83,842 annually.
USD $70,586.88 - USD $85,000.00 /Yr.
New York, NY Salary Range: $92,100 USD - $115,000 USD
- State of California: $116,000 - $237,000

Real excerpts from the test run on 2026-10-21, trimmed.

A regex gets you most of the way, and then the last two lines break it: both appear on ads listed for the Denver area, but the ranges are stated for New York and California. A model reading the sentence sees that; a pattern match does not. LinkedIn's seniorityLevel field was "Not Applicable" for 22 of the 25 jobs, so seniority has to come from the title and description too.

The LinkedIn sample also contained the same job three times (one employer, one title, three reposts), which is why deduplication is in the cleaning rules.

Finding 3: "no date filter" includes old ads​

Indeed returned ads published between January 2025 and October 2026. 99 of 137 were from September or October 2026, but a handful were over a year old. Pay bands move, so the prompt drops anything older than 90 days. If you need a fresher sample, set Indeed's datePosted to "14" (last 14 days): in our test that cut the result to 45 ads, which is enough for an overall band but too few for a seniority split.

How the workflow fits together​

If you want to build or adapt this yourself, these are the parts.

Step 1: Connect the two job scrapers​

You need a free Apify account and the Apify connector in Claude. If you have not set that up, connecting Apify to Claude Cowork covers it; the same remote MCP server works in Claude Desktop. Limiting the connector to two Actors keeps Claude from browsing the Store for alternatives mid-task:

https://mcp.apify.com?tools=actors,valig/indeed-jobs-scraper,curious_coder/linkedin-jobs-scraper
ActorInputs used herePrice per ad, free plan
Indeed Jobs Scraper (valig)title, location, country, limit, optional datePosted ("1", "3", "7", "14")$0.0001 subject to change · verified 2026-10-21
LinkedIn Jobs Scraper (curious_coder)keywords, location, datePosted (pastMonth), limitPerSource, scrapeCompany$0.002 subject to change · verified 2026-10-21

Both charge less on paid Apify plans, plus a small per-run start fee. Indeed returns pay as a structured baseSalary object (min, max, unitOfWork, currencyCode), which is why it does most of the work here. LinkedIn adds employers that do not post on Indeed, at the cost of description parsing. Other job boards are listed in the Apify jobs category roundup.

Step 2: Define the job before you count it​

The first interview question is the important one. "Which variants count as the same job" is a compensation analyst's real job, and no scraper can answer it for you. Expect Claude to sort titles three ways:

  • MATCH: the title you named, with level suffixes (II, Senior) handled by the seniority split.
  • CLOSE: adjacent titles such as "Reporting Analyst" or "BI Analyst". Include them only if the work is the same.
  • EXCLUDE: shared keyword, different job ("Business Analyst", "Clinical Informatics Analyst").

Step 3: Normalise the pay​

The cleaning rules in the prompt are the standard ones, made explicit so you can audit them:

  • Base pay only. Bonus, equity and benefits are excluded, because few ads state them in comparable terms.
  • Annualise hourly pay at 2,080 hours (40 hours x 52 weeks) and monthly pay at 12 months.
  • Use the range midpoint. It is the simplest summary of a range. Very wide ranges (we saw $60,000 to $140,000 on one ad) are flagged rather than silently averaged in, because they say more about the employer's posting policy than about the role.
  • Drop, don't guess. An ad without pay does not count. The prompt says this twice, because "estimate the missing ones" is exactly where a language model will happily invent numbers.

Step 4: Read the band honestly​

Report percentiles with n next to every figure. A median from 26 ads is a useful signal; a P75 from 5 ads is a guess. The setup prompt marks anything under 15 ads as indicative, and only splits by seniority where a group has at least 8.

Where this works, and where it does not​

The method depends on employers posting pay, which depends on the law where the job is.

  • United States. As of mid-2026, state laws requiring a pay range in job postings were in effect in California, Colorado, Hawaii, Illinois, Maryland, Massachusetts, Minnesota, New Jersey, New York, Vermont and Washington, plus Washington, D.C. (Clockspot, fact-checked May 2026). Virginia (1 July 2026), Maine (29 July 2026) and Connecticut (1 October 2026) followed, and Delaware is due in September 2027 (Cooley, July 2026). Thresholds and details differ by state, and several cities have their own rules. Outside those states, expect far fewer ads with pay.
  • European Union. The Pay Transparency Directive (EU) 2023/970 gives job applicants the right to know the starting pay or its range before the interview, with a job ad as one way to provide it. Member states had until 7 June 2026 to transpose it, and national implementation is uneven, so check the country you benchmark.
  • Elsewhere, posted pay is the exception, and a job-ad benchmark will be thin. Say so in the report rather than stretching a small sample.

This is a summary for picking a market to benchmark, not legal advice.

What posted pay does not tell you​

  • Advertised is not paid. Ranges are what employers are willing to state; offers can land anywhere inside them, and occasionally outside.
  • The mix of employers matters. A city where three large tech companies are hiring will skew high that month. The evidence table lets you see this.
  • Job ads over-represent hard-to-fill roles. Positions that stay open get reposted, so they can be over-counted even after deduplication.
  • Totals are missing. Bonus, equity and benefits can be a large part of compensation, and they are excluded here.

For a negotiation, treat the band as one input next to the people you can ask. For a hiring budget, rerun it monthly, since the cost is negligible.

What it costs​

Scraping costs are tiny next to everything else in this workflow. Our test runs, on a paid Apify plan:

RunResultsCharged
Indeed, Data Analyst, Denver, no date filter137 ads$0.013
Indeed, same search, last 14 days45 ads$0.005
LinkedIn, Data Analyst, Denver, past month25 jobs$0.023

On the free plan the LinkedIn rate is twice that, and the whole benchmark still fits comfortably inside Apify's free monthly credit. The Claude side uses your normal Claude plan.

Frequently Asked Questions

Claude can describe typical pay from its training data, but that data is not current and cannot be checked against named ads. Pulling live ads gives you a dated sample with an evidence table, so the number can be audited.

In our Denver test Indeed returned a structured pay range on most ads, so it carries the benchmark. LinkedIn adds employers that do not post on Indeed, but its pay is usually in the description text, which Claude has to read.

There is no hard rule. The setup prompt treats fewer than 15 ads as indicative only and splits by seniority only where a group has at least 8. Widen the date range or add close title variants if you are below that.

It works wherever employers post pay. That is common in US states with pay transparency laws and becoming more common in the EU after the Pay Transparency Directive's June 2026 transposition deadline, but thin in most other markets.

Next step​

Run the setup prompt for the role you care about, read the exclusions list before the headline number, and keep the evidence table with the band. If you are on the job-seeker side, the same two scrapers power the daily job search digest ranked against your CV. Recruiters can feed the band straight into a candidate shortlist built in Claude, and Claude prompts that use live web data has more HR prompts.

Advertise on this page