

# How to Compare Salaries From Job Listings With Reduced Bias

Compare advertised pay only within like roles, seniority, currencies, and pay periods, and show how many eligible listings had usable pay.

When you compare pay across job listings, many posts do not show any salary numbers. To reduce comparison errors, keep missing pay out of your pay calculation and place it in your coverage count. A published range is an offer range in an advertisement, not actual earnings or verified employee income. The median is the middle value after sorting; with an even count, use the mean of the two middle values. A midpoint is the mean of one listing's lower and upper range bounds; it is not expected employee pay. Missing period, currency, and bounds stay unknown and remain coverage exclusions.

An actor is a hosted data collection program, and a task is a saved input preset. You can use the [Indeed Jobs Scraper Actor](https://apify.com/chronometrica/indeed-jobs-scraper) and its broad [Indeed Jobs Scraper Task](https://apify.com/chronometrica/indeed-jobs-scraper/examples/indeed-jobs-scraper) for discovery. Use one query and country, small cap2, salaryType equals any, minSalary equals 0, and saveOnlyUniqueItems equals true.

A high salary filter selects higher paying disclosures and cannot measure general pay coverage or a representative pay median. Pay filtering changes your coverage sample denominator. Retain the following raw and structured fields: salaryRaw, salaryMin, salaryMax, salaryCurrency, salaryPeriod, salarySource, jobId, jobUrl, title, seniority, employmentType, locationRaw, descriptionStatus, descriptionText, and scrapedAt. Preserve missing values and check structured units against the raw source. Current two accepted rows establish listing and detail identity, not a salary benchmark or employee earnings.

## Define the comparison group

1. Define the comparison group before you look at pay. Look at the role family, seniority, location, work pattern, employment type, and observation window. Keep duplicates and reposts from giving extra weight to one listing.
2. Save your full source rows and your filter notes. Keep the denominator of eligible unique listings before you remove missing pay. A pay filter changes that denominator, and you must report it.
3. Inspect the raw salary text and the structured bounds and units. Distinguish annual, monthly, hourly, contract day rate, bonus, and total compensation words. Keep unknown periods and currencies separate. Do not guess that a number is annual just because it looks large.
4. Group matching currencies and periods. Use separate panels for unlike units. Convert units only when the work schedule and assumptions are known. For this guide, keep hourly and annual pay separate. Do not multiply hourly pay by 2,080 as a universal employee income fact.
5. Keep listings with comparable two ended ranges in a range summary group. One ended values need a separate group unless you define another summary. Never turn missing bounds into zero or guess a midpoint.
6. Report total eligible listings, pay disclosed, comparable ranges, exclusions by reason, and collection limits. Then show range bounds and midpoint summaries with their exact meaning. The median of midpoints is a summary of advertised ranges, not a median actual salary.
7. Show the small distribution, not only one number. A broad range may overlap another range. A slightly higher midpoint does not mean a better offer. Small samples cannot support national labor market claims. Recheck pay on the current source before you make a personal choice.

## Fictional example of salary comparison

All listings and pay in this example are invented. Imagine six eligible unique mid level analyst listings in the same region and time window.

## Eligibility and pay unit ledger

| Job ID | Title | Seniority | Pay Raw | Currency | Period | Min | Max | Status |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| J1 | Analyst | Mid | 80000 to 100000 | USD | annual | 80000 | 100000 | Comparable |
| J2 | Analyst | Mid | 90000 to 110000 | USD | annual | 90000 | 110000 | Comparable |
| J3 | Analyst | Mid | 100000 to 120000 | USD | annual | 100000 | 120000 | Comparable |
| J4 | Analyst | Mid | Missing | Unknown | Unknown | Blank | Blank | Excluded missing |
| J5 | Analyst | Mid | 40 to 50 | USD | hourly | 40 | 50 | Excluded period |
| J6 | Analyst | Mid | 90000 to 100000 | Unknown | annual | 90000 | 100000 | Excluded currency |

Pay disclosed is five of six listings, which equals roughly 83.3 percent. Comparable USD annual two ended ranges appear in three of six listings, which equals 50 percent. J4 missing pay, J5 different period, and J6 unknown currency are three separate exclusions. Do not drop them and claim 100 percent pay coverage.

## Comparable range and midpoint summary

| Job ID | Lower Bound (USD per year) | Upper Bound (USD per year) | Midpoint (USD per year) |
| --- | --- | --- | --- |
| J1 | 80000 | 100000 | 90000 |
| J2 | 90000 | 110000 | 100000 |
| J3 | 100000 | 120000 | 110000 |

For the three comparable rows, the midpoint values are 90000, 100000, and 110000 USD per year. The median midpoint is 100000 USD per year. The median lower bound is 90000, and the median upper bound is 110000 USD per year. Those two medians describe the observed range endpoints and are not guaranteed to form any one employer's offer.

The J5 hourly range stays at 40 to 50 USD per hour in a separate one row panel with no annual conversion. J6 retains its raw range but has no comparable USD label. If a high salary filter removes J1 and J4 from discovery, your visible sample denominator changes. Do not recalculate 83.3 percent as if the same six source sample remained. Show the effect as selection, not real pay growth.

## Next action

Open the broad [Indeed Jobs Scraper Task](https://apify.com/chronometrica/indeed-jobs-scraper/examples/indeed-jobs-scraper) and inspect the [Indeed Jobs Scraper Actor](https://apify.com/chronometrica/indeed-jobs-scraper), retain every eligible listing in your coverage ledger, and compare only verified like unit ranges.

## Sources

- [Indeed Jobs Scraper Actor](https://apify.com/chronometrica/indeed-jobs-scraper)
- [Recruiting Hell Salary Tracking Discussion](https://www.reddit.com/r/recruitinghell/comments/1r3a8jj/i_tracked_63336_us_job_postings_last_week_feb_26/)
- [CS Career Questions Job Postings Discussion](https://www.reddit.com/r/cscareerquestions/comments/1red1yc/found_5000_cs_jobs_in_440_000_jobs_posted_on/)