Tracking wage growth using the salary ranges in job postings gives you a dated reading of what employers offer new hires, by role and place. A median of advertised pay can rise when the roles advertised shift towards better-paid work, with no employer offering more for any job. This post sets out what a posting-based measure captures, how the official US measures treat the same problem, and a method that holds role, level and place fixed, with a worked example and a query to run on your own data.
What tracking wage growth from salary ranges in job postings measures
A wage growth measure built on salary ranges in job postings tracks the pay employers offer for new openings, in the postings that state pay, dated when each posting appears. It follows offers, not people in work. Unless role, level and place are held fixed, it also moves with the mix of jobs advertised.
- It is a flow of offers. Each posting is one opening on one date. The pay of people already in post, and the rises they receive, are outside it.
- It covers postings that state pay. That set can change between periods, so count the postings with pay in every period and publish the count beside the growth rate.
- A range becomes one figure by a rule. The midpoint moves when either end moves. Track the growth of the minimums, the midpoints and the maximums as three series: when they part, ranges are changing shape, which one series hides.
- It carries the mix of jobs advertised. A wave of engineering openings lifts the median and a wave of entry-level openings lowers it. The rest of this post is about taking that out.
Wage growth measurement methodology: advertised salaries in job postings beside the BLS measures, JOLTS and the Atlanta Fed Wage Growth Tracker
The BLS Employment Cost Index follows pay in the same jobs at fixed employment weights. BLS average hourly earnings divide payroll by hours, so they shift with the mix of work. The Atlanta Fed Wage Growth Tracker follows the same people a year apart. JOLTS counts openings, hires and separations. Advertised salaries measure offers on new postings.
The table sets the measures side by side. BLS is the US Bureau of Labor Statistics.
| Measure | Publisher | Pay it covers | What it follows between periods | A changing mix of jobs |
|---|---|---|---|---|
| Employment Cost Index | BLS | Wages and salaries, and employer costs for benefits, in private industry and state and local government | The same jobs within sampled establishments, each calendar quarter | Held out: employment is fixed at a reference year's estimates |
| Average hourly earnings | BLS | Aggregate weekly payroll divided by aggregate weekly hours, in the pay period that includes the 12th of the month | Nothing is matched: the payroll and hours of each period | Included: the average shifts as employees move between higher-paid and lower-paid work |
| Wage Growth Tracker | Federal Reserve Bank of Atlanta | Hourly wages of people employed in the current month and a year earlier, from the Current Population Survey | The same individuals, a year apart | Held out by matching; the matched sample is older and more educated than the employed population |
| Job Openings and Labor Turnover Survey (JOLTS) | BLS | Counts of employment, job openings, hires and separations | Counts for each month | Read beside a pay measure, for the openings behind it |
| Indeed Wage Tracker | Indeed Hiring Lab, a company's product | Wages and salaries advertised in job postings on Indeed | Combinations of job title, region and salary type, year on year | Controlled for by job title: growth is taken within each combination, then the median across them |
Employment Cost Index. The calculation chapter of the BLS Handbook of Methods describes the index as a modified Laspeyres index, computed for each calendar quarter with employment held constant at a reference year's estimates, so that it measures the change in labour costs independent of employment shifts among occupations and industries. The sample tracks pay within establishment jobs, not for individual workers. The concepts chapter counts commissions, production bonuses and cost-of-living allowances as wages and salaries, and leaves out overtime premiums, shift differentials and nonproduction bonuses.
Average hourly earnings. The handbook of the bureau's payroll survey defines them as aggregate weekly payroll divided by aggregate weekly hours. It says that averages of hourly earnings differ from wage rates: they move when employees shift between relatively high-paid and low-paid work, as well as when earnings change within an establishment.
Wage Growth Tracker. The Atlanta Fed takes the other route and matches people. In a post of 5 February 2024, John C. Robertson, senior policy adviser and economist emeritus at the bank, describes the tracker as the typical wage growth, measured by the median, of workers employed in the current month and a year earlier, matched through the short panel of the Current Population Survey. He gives the reason for the design: in the recession of 2008 and 2009 layoffs were concentrated among lower-paying jobs, so aggregate measures of wage growth rose while labour demand was very weak. The cost of matching, he notes, is a sample that is older and more educated than the employed population, which one weighted version adjusts for.
JOLTS. The survey's overview describes a monthly survey of job openings, hires and separations, and none of the data elements it lists is a pay figure. It gives the openings; a posting-based measure adds the pay advertised on them.
A tracker built on postings. Indeed Hiring Lab documents its Wage Tracker as an approach similar to the Atlanta Fed's that tracks job titles, not individuals. It takes the median posted wage for each country, month, job title, region and salary type (hourly, monthly or annual), computes year-on-year growth within each combination, and takes the median of that distribution as the country's measure for the month.
Three designs come out of the comparison. Fix the weights, as the Employment Cost Index does. Match the unit with itself, as the Atlanta Fed does with people and Indeed with job titles. Or do neither, and read the average with its mix, as with average hourly earnings. A posting is seen once, so it cannot be matched with itself a year later. The unit to match is a cell: one role, at one level, in one place.
A methodology for a wage growth index from job posting salary data
Build a wage growth index from job posting salary data in six steps: reduce each range to one figure, assign each posting to a cell of role, level and place, take each cell's median pay per period, keep cells with enough postings in both periods, compute growth within each cell, and combine cells with base-period weights.
The method is this post's advice, not an official standard. Its statistics come from the BLS handbook above and from the NIST/SEMATECH e-Handbook of Statistical Methods.
- One figure for each posting. Choose the midpoint, the minimum or the maximum of the stated range once, and apply the choice to every period.
- A cell for each posting. A cell is one job function, one seniority level and one country. Add the stated currency and pay period to the cell, as Indeed does with salary type. Growth is a ratio within a cell, so stated pay is compared with stated pay and no exchange rate or hours convention enters the result.
- A median for each cell and period. The handbook's page on measures of location notes that extreme values in the tails distort the mean and leave the median, which is based on ranks, in place.
- A minimum cell size, in both periods. The handbook's confidence limits for a mean narrow with the square root of the number of observations, so it takes about four times the postings to halve the width. The interval is for a mean, so take it as a guide. Set one floor for every cell, 20 postings is a workable start, and drop a cell that falls under it in either period.
- Growth within each matched cell. Divide the cell's median in the later period by its median in the base period.
- Fixed weights. Weight each matched cell by its share of base-period postings, multiply each weight by the cell's ratio, sum, and multiply by 100. This is a fixed-weight Laspeyres form, as in the modified Laspeyres index the BLS handbook describes for the Employment Cost Index, with postings in the place of employment. The median of the cell ratios, Indeed's convention, is the alternative: every cell counts once, so one large cell cannot move the measure.
Report three numbers with every reading: the fixed-mix index, the growth of the raw median over all postings, and coverage, the share of each period's postings that sit in matched cells. Raw growth less fixed-mix growth is the mix effect.
Five tests for the finished series.
| Test | How to run it | What a failure tells you |
|---|---|---|
| Range rule | Rebuild the index on the minimums and on the maximums | Ranges are changing width: report all three indexes |
| One employer | Drop the largest employer from each cell | One company's pay scale sets a cell median: require several employers in a cell |
| Weights | Recompute with the later period's weights | The answer depends on the mix chosen: publish both indexes |
| Coverage | Share of postings in matched cells, for each period | New roles are entering unmatched: widen the cells or shorten the gap between periods |
| Labels | Check that both periods were classified under one version | A changed definition moves postings between cells and reads as a change in pay |
Worked example: a raw median that rises while the fixed-mix index stays level
The example is illustrative. Acme Robotics is an invented company and every figure is an assumption made to show the arithmetic.
Assumptions:
- Acme Robotics advertises software engineering roles in the United States, each with a yearly pay range in US dollars. Each posting is reduced to the midpoint of its range, in thousands of dollars.
- The two periods are the same 30 days, one year apart.
- A cell is a seniority level, since function, country, currency and pay period are the same for every posting.
- The minimum cell size is two postings in each period, set low so that the arithmetic can be done by hand.
- Weights are each matched cell's share of base-period postings.
| Level | Base period midpoints | Base median | Later period midpoints | Later median |
|---|---|---|---|---|
| Entry | 76, 78, 82, 84 | 80 | 78, 82 | 80 |
| Mid | 96, 98, 102, 104 | 100 | 96, 98, 102, 104 | 100 |
| Senior | 126, 134 | 130 | 124, 128, 132, 136 | 130 |
| Lead or principal | No postings | No median | 180 | 180 |
The working:
- Raw median, base period. The ten midpoints in order are 76, 78, 82, 84, 96, 98, 102, 104, 126 and 134. The median is the mean of the fifth and sixth: (96 + 98) / 2 = 97.
- Raw median, later period. The eleven midpoints in order are 78, 82, 96, 98, 102, 104, 124, 128, 132, 136 and 180. The median is the sixth: 104.
- Raw growth. 104 / 97 = 1.072, a rise of 7.2 per cent.
- Matching. Entry, mid and senior have at least two postings in both periods. Lead or principal has none in the base period, so it is left out of the index. Coverage is 10 of 10 postings in the base period and 10 of 11, or 90.9 per cent, in the later one.
- Growth within cells. 80 / 80, 100 / 100 and 130 / 130 are each 1.000.
- Weights. Of the ten base-period postings, four are entry, four mid and two senior: 0.4, 0.4 and 0.2.
- Fixed-mix index. 100 × (0.4 × 1.000 + 0.4 × 1.000 + 0.2 × 1.000) = 100.0. Growth is 0.0 per cent, and the median of the three cell ratios gives the same answer.
The whole 7.2 per cent is mix. Senior and lead roles were 2 of 10 postings in the base period, 20 per cent, and 5 of 11 in the later period, 45.5 per cent. Entry roles fell from 4 of 10, 40 per cent, to 2 of 11, 18.2 per cent. No level's median moved.
The index still responds when pay moves. Had the senior median risen by 5 per cent, to 136.5, the index would read 100 × (0.4 + 0.4 + 0.2 × 1.05) = 101.0, a rise of 1.0 per cent however many senior roles were advertised.
Advertised pay and the weekly pay series in Fokals data
Fokals collects the roles companies publish on their own careers pages, as why we read job postings from the company's own board explains, and delivers them in Job Postings, part of the hiring product. Each posting carries its job function, its seniority, its location and country, and pay as the posting states it: minimum, maximum, currency and period, with the same pay on one annual US-dollar scale. Each also carries the dates it was first seen, last seen and closed. Those are the cell, the figure and the date of the method above.
The query builds the fixed-mix index between two periods. Every date and the minimum cell size are parameters, and the base period ends before the later one starts. The SQL is PostgreSQL.
with posted as (
select
first_seen_at >= :later_start as later,
nullif(labels ->> 'job_function', '') as job_function,
nullif(labels ->> 'seniority', '') as seniority,
country, salary_currency, salary_period,
(salary_min + salary_max) / 2.0 as pay_mid
from job_postings
where not found_on_first_read
and salary_min is not null
and salary_max is not null
and ( (first_seen_at >= :base_start and first_seen_at < :base_end)
or (first_seen_at >= :later_start and first_seen_at < :later_end))
),
cells as (
select
later, job_function, seniority, country, salary_currency, salary_period,
count(*) as postings,
percentile_cont(0.5) within group (order by pay_mid) as median_pay
from posted
group by 1, 2, 3, 4, 5, 6
),
matched as (
select
b.postings as base_postings,
l.postings as later_postings,
l.median_pay / b.median_pay as ratio
from cells b
join cells l
using (job_function, seniority, country, salary_currency, salary_period)
where not b.later
and l.later
and b.postings >= :min_postings
and l.postings >= :min_postings
)
select
100.0 * sum(base_postings * ratio) / sum(base_postings) as fixed_mix_index,
100.0 * percentile_cont(0.5) within group (order by ratio) as median_cell_index,
count(*) as matched_cells,
sum(base_postings) as base_postings_matched,
sum(later_postings) as later_postings_matched
from matched;The query dates each posting by its first-seen date and leaves out postings that carry the baseline flag, which were already published when the company's postings were first observed. It keeps postings that state both ends of a range and matches cells on job function, seniority, country, currency and period; a posting with an empty label or no currency falls out at the join. It returns the fixed-weight index, the median of the cell ratios, and the matched cells and postings for the coverage figure.
Market Series, the market series product, carries the weekly median of advertised pay in yearly US dollars, as of each Sunday, over 7, 30 and 180 days, for all companies and by industry, country and company size band. Each row gives the median, the number of postings it is over, the median of the window just before, the growth between the two and an index. Every row rests on at least 20 postings and is written once, so the series is point-in-time.
The weekly median is the market's advertised pay level with its mix of roles, dated to the week. The fixed-mix index from Job Postings says how much of a move in that median is pay for like roles. Market Series applies the same principle to companies, with rates taken over same-store cohorts.
The data dictionary defines every field and the methodology sets out how pay is read from a posting. For pay levels by role, see benchmarking pay and talent demand.
Frequently asked questions
Does JOLTS measure wages or salaries?
The overview of the Job Openings and Labor Turnover Survey, published by the US Bureau of Labor Statistics, describes a monthly survey of job openings, hires and separations. The data elements it lists are total employment, job openings, hires, quits, layoffs and discharges, and other separations, and none is a pay figure. For pay, the bureau publishes the Employment Cost Index and average hourly earnings, and a measure built on job postings adds the pay advertised on openings.
What is the composition effect in wage growth?
It is the part of a change in average or median pay that comes from a change in who or what is counted, with no change in the pay of any one job. The BLS handbook says average hourly earnings move when employees shift between relatively high-paid and low-paid work. In a posting-based measure the same effect appears when the mix of roles, levels and places advertised changes between two periods. A fixed-mix index takes it out by comparing each cell with itself.
Is wage growth in job postings the same as wage growth for people in work?
No. Postings measure the pay employers offer for new openings, in postings that state pay. The Atlanta Fed's Wage Growth Tracker measures the median wage growth of people employed in the current month and a year earlier, and the Employment Cost Index follows pay in the same establishment jobs each quarter. The three populations differ, so read a posting-based measure as the direction of offers to new hires, beside the official measures.
How many job postings does each cell need?
Set a floor yourself and publish it with the series. The interval around a mean narrows with the square root of the number of observations, as the NIST statistics handbook shows, so about four times the postings halve its width. A floor of 20 postings in each period is a workable start, with several employers in every cell. In Fokals data, each row of the weekly pay series in Market Series rests on at least 20 postings.
Should a wage growth index from job postings use the midpoint of the salary range?
Use the midpoint for the headline series and build the same index on the minimums and on the maximums. When the three grow together, the choice makes no difference to growth. When they part, employers are widening or narrowing their ranges, and the midpoint alone would report that as a change in pay. Whichever figure you choose, apply the same rule to every period.
How does the Atlanta Fed Wage Growth Tracker differ from average hourly earnings?
Average hourly earnings divide aggregate weekly payroll by aggregate weekly hours, so they move when employees shift between higher-paid and lower-paid work. The Atlanta Fed's tracker takes the median wage growth of people employed in the current month and a year earlier, which holds the people fixed. Its matched sample is older and more educated than the employed population, as John C. Robertson of the Atlanta Fed wrote in a post of 5 February 2024.
The queries and code on this page are examples to adapt. Test them in your own environment before you rely on them.
What this page says about the products it names was checked against their public documentation on 7 October 2026. Product and company names are trademarks of their owners. Fokals is not affiliated with them or endorsed by them.