# 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.*

- Canonical URL: https://www.refolk.ai/guides/pay-band-from-posted-ranges
- Pillar: Market and talent intelligence
- Format: Playbook
- Published: 2026-09-17
- Last reviewed: 2026-09-17
- Reading time: 15 min
- Keywords: build pay band from job postings, salary benchmark from posted pay ranges, compensation benchmarking public data, normalize job titles to levels for pay, market pay range for a role and location

## 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.

**53% - US postings that now disclose pay, per the NY Fed**

Up from roughly 15% before 2018, driven by state pay-transparency mandates rather than voluntary disclosure.

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.

> **Watch out:** A mandate-state band is not a national band
>
> Building from covered states only and labelling the result "national" is the most common way this method misleads a leadership audience. Bands skew toward California, Colorado, New York, Washington, and Illinois and toward larger employers. Say so explicitly.

## 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

1. **Scope the target** - Fix one role, one level, one location, and pick the base market. Write a one-line definition plus the level criteria you will slot against.
2. **Pull covered postings** - Collect postings only from in-posting mandate jurisdictions for the role, and record the pull date. Note that coverage skews to mandate states.
3. **Normalize titles to your level** - Slot 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.
4. **Filter junk ranges** - Drop 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.
5. **Check sample size** - Count clean observations for the slice, not the pool. If thin, widen the window or report with an explicit caveat, and state n per slice.
6. **Apply geo and remote adjustment** - Adjust ranges from other markets using a cost-of-labor differential, and treat remote ranges as the strictest-market rate with a flag.
7. **Compute the band** - Report median, 25th and 75th percentiles, n, pull date, and decay cadence.
8. **Set refresh cadence** - Quarterly 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

| Stage | Figure | Note |
| --- | --- | --- |
| Postings pulled from mandate states | 100% | raw set with pull date |
| Retained after leveling | fewer | tagged to one level, straddlers flagged |
| Retained after junk-range filter | fewer still | min and max present, span within threshold |
| In-slice observations | the band | counted per level-location cell, not pooled |

*Coverage, leveling, and the junk filter each remove observations, so the cell you compute on is smaller than the pull.*

## 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.

> **Rule:** Level before pay, always
>
> Slot every posting to a level by scope and autonomy before you look at its range. Benchmarking first anchors the band to what people already earn rather than to what the role is worth, and formalizes existing inconsistency.

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.

I ran this search: `Senior software engineers in New York with Python who currently work at companies that post salary ranges.` - [see the full result list](https://www.refolk.ai/s/0n2dpg08b0).

*Returns named senior engineers in a mandate market at employers that disclose pay, so you can ground your level definition in real incumbents before you attach a range.*

## 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.

**Junk-range filter log**

```
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.*

> **Tip:** Report the interquartile range, not just the median
>
> Even after filtering, publish the 25th and 75th percentiles alongside the median. A single number invites false confidence; the spread shows the reviewer how tight or loose the real market is.

## 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.

**65 - Observations for plus or minus 10% at 90% confidence**

A practical floor for a single benchmark; relax toward 20 to 30 only when sampling is hard, and always publish n.

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

1. **Identify source market** - read the posting's stated location, or flag it remote
2. **Look up differential** - cost of labor, target market versus source market
3. **Apply percentage** - shift the range by the differential, not by cost of living
4. **Flag remote as strictest-market** - mark remote observations and name the governing state

*Every non-target observation gets adjusted by a cost-of-labor differential before it enters the band.*

## 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.

> **Watch out:** The headline "flat pay" number will mislead you
>
> Overall tech pay of 0.8% to 1.6% masks role-level drift wide enough to break a band. Set the refresh cadence on the specific role family's movement, not the aggregate, or a January band will be materially off by Q3.

## 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.

## Frequently asked questions

### 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.

---

*From the Refolk guide library. I revise these guides rather than replacing them, so the current version is always at https://www.refolk.ai/guides/pay-band-from-posted-ranges*
