Building a Defensible Pay Band From Posted Salary Ranges
You can turn a set of public job postings into a defensible role-level-location pay band with a stated sample size, filtered ranges, geo adjustment, and a freshness date.
Key takeaways
- Roughly half of US postings now carry pay - 50.4% on Indeed in July 2026, and about 53% nationally per the NY Fed - but a quarter of covered listings still omit salary, so the visible market skews larger and more compliant.
- No regulator publishes a numeric maximum-width rule, so the junk-range filter is the analyst's own defensibility lever: documented junk bands run $50,000-$180,000 and $140,000-$450,000 in New York.
- Slot every posting to a level by scope and autonomy before attaching pay; reversing that order anchors bands to what people already earn rather than to role worth.
- Report n per slice, never pooled - a band that looks solid at n=200 pooled can hold n=8 in the target level-location cell.
- Headline 'flat' tech pay of 0.8% to 1.6% hides mid-level AI engineers up 9.2%, DevOps up 12%, and senior developers down 10%, so one decay cadence across a role family is wrong.
- In Refolk's index, entry-level Python engineers (54,956) outnumber seniors (36,669) by 1.50x, so an equal posting pull over-samples junior bands and starves senior cells.
This is a repeatable method for producing one market pay band, for one role at one level in one location, built from the salary ranges companies now post in job ads. It is written for strategy and research teams, talent-intelligence analysts, and operators sizing a market who need a band they can defend in a leadership review. By the end you will have a band with a stated sample size, filtered ranges, a geographic adjustment, and a freshness date, plus a written account of what the sample can and cannot see.
Posted salary ranges barely existed as a benchmarking source before pay-transparency mandates. The generic salary-benchmarking article pitches a vendor tool and hides the judgment calls inside it. This guide does the opposite: it shows you where the judgment sits, so the number you present is yours to defend and not a black box.
Why posted ranges are a usable source now
Posted ranges are usable because roughly half the US market now discloses pay in the ad itself, a share that barely existed a few years ago. That is enough to build a band from - but only if you understand where the visible half comes from.
Explicit pay hit 50.4% of US postings on Indeed in July 2026, the highest since the series began in 2020. The New York Fed puts the national share at about 53% since January 2024, up from roughly 15% before 2018. The mechanism behind the jump is regulatory: when a mandate takes effect, the share of postings with pay rises by about 20 points in the implementation month.
The catch is that the visible half is not a random half. Roughly a quarter of covered listings still omit salary, and the ones that omit it are not random either - small-employer exemptions and plain noncompliance mean the postings you can see skew toward larger, more compliant employers. So "no range on this posting" carries information, and treating the disclosed set as representative of the whole market overstates the pay of the roles you cannot see.
Where the coverage bias lives
You can only benchmark from-posting in jurisdictions that require the range in the posting itself, so every band you build tilts toward those labor markets. Name the tilt before anyone in the review asks.
As of mid-2026 there is no federal law, and counts vary by how sources scope "in-posting" versus "on request." One tracker states 13 jurisdictions require the range in the posting itself and names California, Colorado, Hawaii, Illinois, Maryland, Massachusetts, Minnesota, New Jersey, New York, Vermont, Washington, plus Virginia and DC, with Maine and Delaware following. Another counts 27 enacted jurisdictions once localities are included. The disagreement is real, and it matters: your pull is only legitimate from the in-posting set.
Size thresholds compound the tilt. They range from 4 employees in New York City to 50 in Hawaii, so even inside a covered state you under-sample small employers. A band built only from mandate states and presented as national is a specific, common failure. State which jurisdictions and size thresholds your sample can and cannot see.
Supply density tells you which cells will be thin
Before you pull a single posting, know that equal effort yields unequal samples, because supply is not uniform across levels or countries. The same pull over-samples junior roles and starves senior ones, and silently turns a "global" band into a US band.
In Refolk's index of professional profiles, the shape of supply is easy to see for one concrete role. Entry-level supply outnumbers senior supply by half again, and US supply dwarfs Germany's for the identical role and level.
Table A - Supply density by seniority band (US, Software Engineer + Python)
| Level | Profiles indexed | Ratio vs Senior |
|---|---|---|
| Entry level | 54,956 | 1.50x |
| Senior | 36,669 | 1.00x |
Table B - Same role and level, two countries (Senior Software Engineer + Python)
| Country | Profiles indexed | Share of US |
|---|---|---|
| United States | 36,669 | 100% |
| Germany | 2,336 | 6.4% |
Read Table B as a warning about pooling. US senior supply is 15.7 times Germany's, so a "global" band silently becomes a US band the moment you pool them. Keep the slices separate and report each on its own.
Concentration inside a thin market is worse still. In Refolk's index, the German senior sample was about 44% Berlin, against a US sample spread across New York, Los Angeles, San Francisco, Boston, and Chattanooga where the top metro was only about 16% of the sample.
Table C - Geographic concentration of the senior sample
| Market | Top metro | Approx share of sampled seniors |
|---|---|---|
| United States | New York | ~16% |
| Germany | Berlin | ~44% |
Table C is a caution, not a national rate: it says that in a thin market your "band" may rest on near-zero local observations clustered in one city. If you are benchmarking a role where local supply is thin, expect the from-posting method to give you a Berlin band dressed up as a Germany band.
In a thin market the from-posting method hands you a one-city band wearing a whole country's name.
The procedure, start to finish
Here is the full method in order. It runs about a day and a half of analyst time for one role-level-location cell. The one rule that overrides everything else: slot to level before you attach pay.
Building the pay band
- Scope the targetFix one role, one level, one location, and pick the base market. Write a one-line definition plus the level criteria you will slot against.
- Pull covered postingsCollect postings only from in-posting mandate jurisdictions for the role, and record the pull date. Note that coverage skews to mandate states.
- Normalize titles to your levelSlot each posting's title against level criteria by scope and autonomy, not tenure or the posted title. Tag every retained posting to one level and flag straddlers.
- Filter junk rangesDrop ranges too wide to be good faith, such as six-figure spreads or bands wider than about 40 to 50 percent, and any missing a min or max. Keep a filter log.
- Check sample sizeCount clean observations for the slice, not the pool. If thin, widen the window or report with an explicit caveat, and state n per slice.
- Apply geo and remote adjustmentAdjust ranges from other markets using a cost-of-labor differential, and treat remote ranges as the strictest-market rate with a flag.
- Compute the bandReport median, 25th and 75th percentiles, n, pull date, and decay cadence.
- Set refresh cadenceQuarterly for high-surge families, semi-annual or annual otherwise, on a calendared review date.
The order dispute worth knowing about is leveling-first versus pay-first. Primary job-leveling sources insist level comes before pay, which is why step 3 sits ahead of steps 4 and 7. Reverse it and you anchor the band to incumbent pay, formalizing whatever inconsistency already exists.
What survives from raw pull to computed band
- 100%Postings pulled from mandate states
raw set with pull date
- fewerRetained after leveling
tagged to one level, straddlers flagged
- fewer stillRetained after junk-range filter
min and max present, span within threshold
- the bandIn-slice observations
counted per level-location cell, not pooled
Normalizing titles to levels
Slot each posting to a level by scope, complexity, autonomy, and judgment - not by the posted title and not by tenure. This is the step that decides whether your pooling means anything.
The documented order is: inventory the real distinct jobs rather than HRIS titles, define levels by scope and autonomy, slot each role against the criteria not the person, and benchmark pay last. Title inflation is the reason. "Senior" at one firm is "Engineer II" at another, so pooling on the posted title mixes two different jobs into one band. Some roles legitimately straddle two levels; when they do, say so explicitly rather than forcing a choice.
Vendor taxonomies do not solve this for you. They require a clean role taxonomy as input; they do not fix title chaos upstream. That work is yours.
Finding the incumbents who actually hold a level - so you can sanity-check what "senior" means in this market rather than trusting the ad copy - is where a plain-language search earns its keep. Instead of scraping and de-duplicating title strings by hand, Refolk lets you ask for the population directly.
Filtering junk ranges
The junk-range filter is the single real defensibility lever you control, and no law sets it for you. Regulators require a "good faith" range but publish no numeric width cap, so your threshold, not the statute, determines the result.
Documented junk ranges show how far this goes: $50,000 to $180,000 and $140,000 to $450,000 in New York, and $40,000 to $400,000 posted against a real target of $90,000 to $110,000. A single band that wide, left in the sample, drags the median and produces a wide, meaningless result that still looks well-sampled. A workable rule of thumb is to drop any range wider than about 40 to 50 percent of its own minimum, plus any posting missing either a min or a max. Sources disagree on where the cutoff sits, so document the one you chose.
posting_id :: employer :: posted_min :: posted_max :: span_pct :: rule_triggered :: kept_or_dropped JR-0142 :: (employer) :: 50000 :: 180000 :: 260% :: span > 50% :: dropped JR-0187 :: (employer) :: 145000 :: 175000 :: 21% :: within threshold :: kept JR-0203 :: (employer) :: 130000 :: (missing) :: n/a :: missing max :: dropped Filter rule applied: drop span > 50% of min OR missing bound. Dropped: __ of __.
One row per dropped posting. The log is your defense when someone asks why the band looks the way it does.
Sizing the sample and adjusting for geography
Two things separate a defensible band from a plausible-looking one: an honest sample count per slice, and a geographic adjustment that uses cost of labor rather than cost of living.
On sample size, no published standard sets a minimum number of posting observations for a role-level-location band specifically - treat that as not established publicly. The borrowable anchors come from survey statistics. Cochran's formula yields n=384 for plus or minus 5% at 95% confidence on a large population, and 278 for a population of 1,000. A standalone benchmark reaches plus or minus 10% at 90% confidence at n=65. When sampling is genuinely hard, practitioners relax to a 20 to 30 floor, noting that data at n=18 to 19 is just a bit less precise. The non-negotiable rule: each geographic and level slice must clear its own minimum, not the pooled total.
On geography, the documented method compares the cost of labor in the target location to the base location and applies the percentage differential to the range. This is cost of labor, not cost of living: 89% of companies use salary surveys for differentials, and only 11% use cost of living as the primary input. One published blend weights 70% market data, 20% cost-of-labor index, and 10% cost-of-living. For remote postings, if a role can be performed from a pay-transparency state, the strictest applicable law governs, so a remote range reflects the highest-cost covered market. Flag remote observations and identify the governing market; never fold them in as a national rate.
From an out-of-market range to a target-location number
- Identify source marketread the posting's stated location, or flag it remote
- Look up differentialcost of labor, target market versus source market
- Apply percentageshift the range by the differential, not by cost of living
- Flag remote as strictest-marketmark remote observations and name the governing state
How this goes wrong
The failure modes below are where a band that looks solid quietly lies. Each one has a false positive that survives a casual review, so build the check into the method rather than hoping to catch it later.
| Failure mode | What the false positive looks like | The check |
|---|---|---|
| Pooled n hides thin slices | A confident median at n=200 pooled, when the target cell holds n=8 | Report n per slice, never pooled |
| Junk ranges survive the filter | A wide, meaningless band that looks well-sampled | Log every dropped range and its span |
| Title taken at face value | Inflated titles pooled as one level | Slot to level by scope and autonomy |
| Remote range read as national | A strictest-state number priced as mid-market | Flag remote and name the governing market |
| Coverage bias unstated | A mandate-state band presented as national | State the jurisdictions and thresholds seen |
| Stale band on a fast role | A DevOps or AI band off by 9 to 12% by Q3 | Stamp a freshness date and quarterly cadence |
The two that catch experienced analysts most often are the pooled-n trap and the stale-band trap. The pooled-n trap is dangerous because the pooled total is genuinely large; only when you break it into level-location cells does the emptiness show. The stale-band trap is dangerous because the headline hides it: overall tech pay moved just 0.8% to 1.6% year over year, which invites an annual refresh, while mid-level AI engineers moved up 9.2%, DevOps up 12%, and senior software developers down 10%. A single decay cadence across a role family is simply wrong, because demand concentrates in narrow skill pockets.
Setting the refresh cadence and keeping the band current
Stamp a freshness date on every band and set a refresh cadence tied to how fast that specific role family moves. Traditional salary surveys already lag 6 to 12 months; posted ranges let you refresh faster, but only if you calendar it.
The documented dating rule is quarterly refresh for high-surge role families and semi-annual or annual for slow movers, always with a freshness date. A band set in January can be out of step by Q3, so the freshness date is not decoration - it tells the reviewer exactly how much to trust the number today. When you re-pull, do not compare against the prior number; recompute from scratch, because the compliant set and the metro mix both shift underneath you.
The mechanism to re-check, rather than a value to memorize: pull the current year-over-year movement for your exact role family from a recruitment salary guide, and if it exceeds a few points, drop to quarterly. If it is flat, annual is defensible. The point is to re-check the movement, not to trust a cadence you set once.
Before you call the band defensible
- The band names one role, one level, one location, with the base market stated
- Every posting was slotted to a level by scope and autonomy before pay was attached
- The junk-range filter log records what was dropped, the span, and the rule used
- n is reported for the target slice, not the pooled total
- Median plus 25th and 75th percentiles are published, not the median alone
- Out-of-market ranges are adjusted by cost of labor, and remote ranges are flagged
- The jurisdictions and size thresholds the sample can and cannot see are stated
- A pull date and a calendared refresh cadence are stamped on the band
A band that carries all of these is one you can put in front of a leadership review and defend line by line. It states its own limits, which is exactly what makes it stronger than a vendor number nobody in the room can trace. When the next refresh comes due, run the same eight steps again, recompute from the raw pull, and update the freshness date. The method holds; only the numbers move.
Questions practitioners ask
How many job postings do I need for a defensible pay band?
No published standard sets a minimum for a role-level-location band specifically, so treat that as not established. Borrow survey anchors instead: Cochran's formula gives n=384 for plus or minus 5% at 95% confidence, and a standalone benchmark reaches plus or minus 10% at 90% confidence at n=65. Practitioners relax to a 20 to 30 floor when sampling is hard. Whatever you land on, each geographic and level slice must clear its own minimum, not the pooled total.
What counts as a junk salary range I should filter out?
A junk range is one too wide to be a good-faith estimate or missing a minimum or maximum. No law fixes a numeric cap, so this is a judgment threshold; a common working rule drops spreads wider than about 40 to 50 percent. Documented real-world junk bands include $50,000 to $180,000 and $140,000 to $450,000 in New York, and $40,000 to $400,000 against a real $90,000 to $110,000 target. Log every range you drop and the rule you used.
Can I build a national pay band from posted ranges?
Not cleanly. You can only benchmark from-posting in jurisdictions that mandate ranges in the ad, so your sample skews toward California, Colorado, New York, Washington, and Illinois markets and toward employers above each size threshold. About a quarter of covered listings still omit pay, so the visible half skews larger and more compliant. State which jurisdictions and thresholds your sample can and cannot see rather than presenting it as national.
How do I adjust a posted range from another city to my target location?
Compare the cost of labor in the target location to your base market and apply the percentage differential to the range. That is cost of labor, not cost of living; 89% of companies use salary surveys for this. One published blend weights 70% market data, 20% cost-of-labor index, and 10% cost-of-living. Treat remote postings as the strictest applicable market rate and flag them separately rather than as a national number.
How often should I refresh a pay band?
It depends on the role family. Overall US tech pay moved only 0.8% to 1.6% year over year, but specialized roles moved far faster: mid-level AI engineers up 9.2%, DevOps up 12%, senior software developers down 10%. Refresh high-surge families quarterly and slow movers semi-annually or annually, and always stamp a freshness date, because a band set in January can be off by 9 to 12 percent by Q3.
Should I map titles to levels before or after looking at pay?
Before. Primary job-leveling sources insist you slot each role to a level by scope, complexity, autonomy, and judgment first, then benchmark pay last. Reversing that order anchors bands to what incumbents already earn and formalizes existing inconsistency. Title inflation means a 'Senior' at one firm equals an 'Engineer II' at another, so taking the posted title at face value corrupts any pooling you do downstream.
Try it on the search you came here for
Stop building boolean strings. Just describe the person.
Type one sentence. I plan the search, read GitHub, public LinkedIn and Crunchbase records, and the open web as it is right now, and hand back a ranked list with the reason next to every name.
01Describe them
One plain sentence. Role, city, stack, stage, whatever matters to you.
02I read the web live
GitHub, public LinkedIn and Crunchbase records, the open web. Not a database that went stale last quarter.
03You read the shortlist
Ranked, with the reasoning under every name. Open a profile, ask a follow-up, narrow it down.
- Staff backend engineers in NYC who shipped Rust in production
- Series A fintechs in SF under 50 people, growing headcount this year
- Maintainers of fast-growing Rust web frameworks on GitHub
- No boolean, no filters, no seat to buy. One box.
- Read at search time, so a profile updated yesterday counts today.
- Every step visible as it runs, every name with its reason.
500 free credits on sign-up. No card, no demo call. See real searches.