Real Wage Growth Calculator: Python Tutorial and Data Analysis
Calculate purchasing power change, compare pay with inflation, and reproduce BLS real average hourly earnings with a transparent Python workflow.
Real Wage Growth Calculation Tutorial With Calculator and Python
Real wage growth shows whether a wage measure grew faster or slower than consumer prices. The exact calculation is not simply wage growth minus inflation. For a growth rate calculator, divide one plus nominal wage growth by one plus inflation, then subtract one. If nominal wages rise 5 percent while prices rise 3 percent, exact real wage growth is about 1.94 percent. The simple subtraction gives 2.0 percent, which is close but not exact.
Real Wage Growth Calculator
Use the interactive calculator in the HTML version of this article. It has three modes.
-
Growth rate mode uses nominal wage growth and inflation.
-
Level mode uses a starting wage, ending wage, starting CPI, and ending CPI.
-
Dollar purchasing power mode converts a nominal hourly wage into constant 1982 to 1984 dollars when the CPI index uses that base.
Exact real wage growth formula
Exact real wage growth = ((1 + nominal wage growth) / (1 + inflation) - 1) × 100
Enter the growth rates as decimals inside the formula. A 5 percent wage increase is 0.05. A 3 percent inflation rate is 0.03.
Quick approximation
Approximate real wage growth = nominal wage growth - inflation
The shortcut is useful for intuition. It should not be presented as the exact BLS calculation.
Simple calculator example
Suppose nominal wages rise 5 percent and consumer prices rise 3 percent.
Exact result = ((1.05 / 1.03) - 1) × 100
= 1.94 percent
Approximation = 5.0 - 3.0
= 2.0 percent
The difference is small in this example. It can become larger when wage growth or inflation is high.
| Example | Nominal wage growth | Inflation | Exact real wage growth | Approximation |
|---|---|---|---|---|
| Synthetic example A | 5.0% | 3.0% | 1.94% | 2.00% |
| Synthetic example B | 3.0% | 5.0% | negative 1.90% | negative 2.00% |
| Synthetic example C | 4.0% | 4.0% | 0.00% | 0.00% |
These are calculator examples. They are not BLS observations.
What Is Real Wage Growth?
Nominal wages are the dollars paid for work.
Real wages adjust those dollars for the price level. The adjustment helps answer a different question: how much purchasing power does the wage represent?
If hourly pay rises from 20 dollars to 21 dollars, the nominal raise is 5 percent. If prices rise 3 percent over the same period, the wage gained purchasing power. The exact gain is about 1.94 percent.
If pay rises 3 percent while prices rise 5 percent, purchasing power falls for that wage measure.
Positive real wage growth means the selected wage measure rose faster than the selected price index.
Negative real wage growth means the selected price index rose faster than the selected wage measure.
That result does not tell us that every worker became better or worse off. It depends on the wage series, the price index, the period, and the group being measured.
How Do You Calculate Real Wage Growth?
There are three useful ways to calculate it.
Method 1: Use growth rates
When you already have nominal wage growth and inflation, use the exact growth rate formula.
def exact_real_wage_growth(nominal_growth_pct, inflation_pct):
nominal = nominal_growth_pct / 100
inflation = inflation_pct / 100
return ((1 + nominal) / (1 + inflation) - 1) * 100
This is the best method for a calculator that takes two growth rates as inputs.
Method 2: Use wage and CPI levels
For reproducible analysis, levels are better.
First calculate the real wage level.
Real hourly wage = nominal hourly wage / CPI × 100
The multiplication by 100 is useful when CPI uses a base of 1982 to 1984 equals 100. The result is then expressed in constant 1982 to 1984 dollars.
Next, compare two real wage levels.
Real wage growth = ((ending real wage / starting real wage) - 1) × 100
You can combine the two steps directly.
def real_wage_growth_from_levels(wage_start, wage_end, cpi_start, cpi_end):
real_start = wage_start / cpi_start
real_end = wage_end / cpi_end
return (real_end / real_start - 1) * 100
This method is ideal when you download the underlying wage and CPI series yourself.
Method 3: Subtract inflation from wage growth
This is an approximation.
It works well for a quick mental estimate when both rates are modest. It does not reproduce the exact ratio calculation.
A second problem is rounding.
A release can show nominal wage growth of 3.5 percent and CPI inflation of 3.5 percent after rounding. Subtracting the displayed values gives zero. The exact result from the underlying levels can still be slightly positive or negative.
For research, use levels or the published real earnings series.
Real Wages vs Nominal Wages
Nominal wages answer, "How many dollars were paid?"
Real wages answer, "What is the purchasing power of those dollars after adjusting for consumer prices?"
A raise can look strong in nominal terms and still be weak in real terms.
Suppose pay rises 4 percent while prices rise 6 percent. The worker has more dollars per hour, but the selected basket of consumer prices rose faster. Real wage growth is negative.
The reverse can happen when inflation slows. Nominal wage growth may also slow, but real wage growth can improve if prices slow even more.
This is why wage growth and inflation should be viewed together.
Real Wage Formula Guide
| Measure | Formula | Meaning | Best use |
|---|---|---|---|
| Nominal wage growth | Current wage divided by prior wage, minus one | Change in dollar pay | Wage trend |
| Inflation | Current CPI divided by prior CPI, minus one | Change in consumer prices | Price trend |
| Real wage level | Nominal wage divided by CPI, multiplied by 100 | Inflation-adjusted hourly earnings | Level comparison |
| Exact real wage growth | One plus wage growth divided by one plus inflation, minus one | Exact purchasing power change | Calculator |
| Approximation | Wage growth minus inflation | Quick estimate | Mental check only |
Which Wage and Inflation Data Should You Use?
This tutorial follows the BLS Real Earnings method for all employees on private nonfarm payrolls.
The main nominal wage series is average hourly earnings of all employees, total private, seasonally adjusted.
The main price deflator is CPI U, all items, U.S. city average, seasonally adjusted.
BLS uses CPI U to deflate earnings for the all employees series. For production and nonsupervisory employees, BLS uses CPI W instead.
Do not mix CPI U and CPI W without explaining the reason.
Main BLS series dictionary
| Series ID | Description | Seasonal status | Role |
|---|---|---|---|
CES0500000003 |
Average hourly earnings of all employees, total private | Seasonally adjusted | Main nominal wage series |
CES0500000013 |
Average hourly earnings of all employees, constant 1982 to 1984 dollars | Seasonally adjusted | Direct real wage validation |
CUSR0000SA0 |
CPI U, U.S. city average, all items | Seasonally adjusted | Main price deflator |
CES0500000002 |
Average weekly hours of all employees, total private | Seasonally adjusted | Optional weekly earnings extension |
CES0500000011 |
Average weekly earnings of all employees, total private | Seasonally adjusted | Optional nominal weekly earnings |
CES0500000012 |
Average weekly earnings of all employees, constant dollars | Seasonally adjusted | Optional real weekly earnings |
CES0500000008 |
Average hourly earnings of production and nonsupervisory employees | Seasonally adjusted | Optional worker group extension |
CES0500000032 |
Real average hourly earnings of production and nonsupervisory employees | Seasonally adjusted | Optional real validation |
CWSR0000SA0 |
CPI W, all items | Seasonally adjusted | Deflator for production and nonsupervisory employees |
Always verify current series descriptions before publication. The BLS API requires the series IDs, but the article should document what those IDs mean.
Download Wage and CPI Data With Python
The BLS Public Data API Version 2 can return multiple time series in one request.
The current BLS features page lists expanded registered access of up to 50 series, up to 20 years in one query, and up to 500 queries per day.
Store the registration key outside the notebook.
export BLS_API_KEY="YOUR_BLS_REGISTRATION_KEY"
Then read it in Python.
import os
BLS_API_KEY = os.getenv("BLS_API_KEY")
if not BLS_API_KEY:
raise RuntimeError("BLS_API_KEY is not set.")
Create a reusable API function with a timeout and clear error checks.
import requests
import pandas as pd
BLS_URL = "https://api.bls.gov/publicAPI/v2/timeseries/data/"
SERIES = {
"nominal_ahe": "CES0500000003",
"real_ahe_bls": "CES0500000013",
"cpi_u": "CUSR0000SA0",
}
def fetch_bls_block(start_year, end_year):
payload = {
"seriesid": list(SERIES.values()),
"startyear": str(start_year),
"endyear": str(end_year),
"registrationkey": BLS_API_KEY,
}
response = requests.post(
BLS_URL,
json=payload,
timeout=30,
)
response.raise_for_status()
data = response.json()
if data.get("status") != "REQUEST_SUCCEEDED":
raise RuntimeError(data.get("message"))
return data
For a long history, split the requests into blocks that stay within the 20 year limit.
Clean and Align the Monthly Data
BLS monthly series can include M13, which is an annual average. Do not treat it as a thirteenth month.
Each real wage calculation needs a wage and CPI observation from the same month.
The safest workflow is:
-
Keep monthly observations that begin with
M. -
Remove
M13. -
Convert the period code into a real calendar date.
-
Convert values to numeric.
-
Sort within each series.
-
Pivot or merge on the same monthly date.
-
Use an inner join for the main calculation.
-
Do not fill a missing wage or CPI value with zero.
def parse_bls_payload(payload):
reverse = {
series_id: name
for name, series_id in SERIES.items()
}
rows = []
for series in payload["Results"]["series"]:
name = reverse[series["seriesID"]]
for obs in series["data"]:
period = obs["period"]
if not period.startswith("M") or period == "M13":
continue
month = int(period[1:])
rows.append({
"series": name,
"series_id": series["seriesID"],
"date": pd.Timestamp(
year=int(obs["year"]),
month=month,
day=1,
),
"value": pd.to_numeric(
obs["value"],
errors="coerce",
),
"footnotes": obs.get("footnotes", []),
})
return pd.DataFrame(rows)
Pivot into one row per month.
wide = (
tidy
.pivot(
index="date",
columns="series",
values="value",
)
.sort_index()
.dropna(
subset=[
"nominal_ahe",
"real_ahe_bls",
"cpi_u",
]
)
)
That dropna step creates the common monthly panel. It prevents a newer wage month from being paired with an older CPI month.
Calculate Real Hourly Earnings in Python
The main calculation is direct.
wide["real_ahe_calc"] = (
wide["nominal_ahe"]
/ wide["cpi_u"]
* 100
)
Now calculate one month and 12 month changes from the real level.
wide["real_growth_1m"] = (
wide["real_ahe_calc"]
.pct_change(1, fill_method=None)
* 100
)
wide["real_growth_12m"] = (
wide["real_ahe_calc"]
.pct_change(12, fill_method=None)
* 100
)
Calculate nominal wage growth and inflation the same way.
wide["nominal_growth_12m"] = (
wide["nominal_ahe"]
.pct_change(12, fill_method=None)
* 100
)
wide["inflation_12m"] = (
wide["cpi_u"]
.pct_change(12, fill_method=None)
* 100
)
wide["approx_real_growth_12m"] = (
wide["nominal_growth_12m"]
- wide["inflation_12m"]
)
The approximation is useful as a check. The exact growth comes from the calculated real wage level.
Validate the Calculation Against BLS Real Earnings
A strong tutorial should not stop after producing a number.
It should check that the number matches an official real earnings series.
wide["validation_gap"] = (
wide["real_ahe_calc"]
- wide["real_ahe_bls"]
)
Small differences can appear because the displayed source series are rounded.
A difference of one or two cents can be normal after matching the same date and seasonal status. A larger difference should be investigated.
Do not solve a mismatch by simply increasing the tolerance.

The selected BLS release points show that the reconstructed value closely follows the published real average hourly earnings series.
Latest Common Month Snapshot
As of August 10, 2026, June 2026 is still the latest month with a published CPI U value and a published Real Earnings release.
The July 2026 Employment Situation has already published July nominal wages. The July CPI and July Real Earnings releases are scheduled for August 12, 2026. Do not combine the July wage with the June CPI and call it July real wage growth.
The June Real Earnings release, published July 14, used preliminary June wage data.
| June 2026 Real Earnings release item | Published value |
|---|---|
| Nominal average hourly earnings | $37.64 |
| CPI U, seasonally adjusted | 332.568 |
| Real average hourly earnings | $11.32 in constant 1982 to 1984 dollars |
| Monthly nominal hourly earnings change | 0.3% |
| Monthly CPI U change | negative 0.4% |
| Monthly real hourly earnings change | 0.8% |
| 12 month nominal hourly earnings change | 3.5% |
| 12 month CPI U change | 3.5% |
| 12 month real hourly earnings change | 0.1% |
The release shows why subtracting rounded percentages can mislead. Both the displayed 12 month nominal wage rate and CPI rate are 3.5 percent, but BLS published a 0.1 percent increase in real average hourly earnings because the calculation uses underlying levels.
Important revision note
The July Employment Situation, published August 7, later reported June average hourly earnings of $37.60 and July average hourly earnings of $37.62. The June figure in the July 14 Real Earnings table was marked preliminary.
This is exactly why a reproducible page should save the retrieval date and rerun after each release. The July Real Earnings release on August 12 will provide the next complete wage and CPI update.
Wages vs Prices, Rebased to 100
Comparing a dollar wage level directly with a CPI index is not useful because the scales are different.
Rebase each series to 100 at the same starting date instead.
base_date = "2010-01-01"
base = wide.loc[base_date]
wide["wage_index_100"] = (
wide["nominal_ahe"]
/ base["nominal_ahe"]
* 100
)
wide["cpi_index_100"] = (
wide["cpi_u"]
/ base["cpi_u"]
* 100
)

The static chart above uses selected values from the June 2026 BLS Real Earnings table for validation. The notebook code generates the full monthly history and rebases it at January 2010.
If the wage index rises faster than the CPI index over a period, the selected wage measure gained purchasing power over that period. If prices rise faster, real wage growth is negative.
Nominal Wage Growth vs CPI Inflation
The next chart compares the two rates that drive real wage growth.

The selected release points show a simple pattern. When nominal wage growth is above CPI inflation, real wage growth tends to be positive. When CPI inflation is above nominal wage growth, real wage growth tends to be negative.
The exact calculation still comes from the ratio of the two growth factors or from real wage levels.
For the full history, use the same monthly panel and calculate 12 month changes for both series.
Real Wage Growth Over Time
The clearest real wage chart places exact 12 month real wage growth around a zero line.

Values above zero mean the selected average hourly earnings measure gained purchasing power over the previous 12 months.
Values below zero mean CPI U rose faster than nominal average hourly earnings over the same period.
The chart does not say why wage growth or inflation changed. It is descriptive.
Paycheck vs Prices, Real Wage Growth Pulse
The main animation compares three 12 month rates:
-
Nominal average hourly earnings growth.
-
CPI U inflation.
-
Exact real wage growth.
The full notebook should create one frame per month from January 2020 through the latest common month.
Use fixed axis limits across every frame. Keep a visible zero line. Do not let the chart rescale each month.

The GIF above is a publication safe preview built only from selected official BLS validation points in the June 2026 Real Earnings table. The notebook code below produces the full monthly animation after retrieving the complete API history.
import plotly.express as px
animation_frame = wide.reset_index().copy()
animation_frame = animation_frame[
animation_frame["date"] >= "2020-01-01"
].copy()
long = animation_frame.melt(
id_vars=["date"],
value_vars=[
"nominal_growth_12m",
"inflation_12m",
"real_growth_12m",
],
var_name="measure",
value_name="percent",
)
long["month"] = long["date"].dt.strftime("%b %Y")
x_min = long["percent"].min() - 1
x_max = long["percent"].max() + 1
fig = px.scatter(
long,
x="percent",
y="measure",
color="measure",
animation_frame="month",
animation_group="measure",
range_x=[x_min, x_max],
title="Paycheck vs Prices, Real Wage Growth Pulse",
)
fig.add_vline(x=0, line_width=1)
fig.write_html(
"real-wage-growth-pulse.html",
include_plotlyjs="cdn",
)
fig.show()
The animation is a comparison tool, not a causal model.
Selected Period Real Wage Results
The following table uses selected points from the June 2026 BLS Real Earnings table. It is useful for validating the code, not for replacing the full monthly dataset.
| Period | Nominal AHE | CPI U | Calculated real AHE | BLS real AHE | BLS 12-month real wage growth |
|---|---|---|---|---|---|
| June 2025 | $36.36 | 321.435 | $11.31 | $11.31 | 1.2% |
| April 2026 | $37.41 | 332.407 | $11.25 | $11.25 | negative 0.3% |
| May 2026 | $37.51 | 333.979 | $11.23 | $11.23 | negative 0.8% |
| June 2026 preliminary | $37.64 | 332.568 | $11.32 | $11.32 | 0.1% |
The calculated constant dollar values are rounded to two decimals for the table.
What Does the Latest Real Wage Reading Mean?
The June 2026 Real Earnings release reported a strong monthly improvement in real average hourly earnings.
Nominal hourly earnings rose 0.3 percent from May to June, while CPI U fell 0.4 percent. BLS reported a 0.8 percent increase in real average hourly earnings for the month.
Over 12 months, the release reported 3.5 percent nominal wage growth and 3.5 percent CPI inflation after rounding. Real average hourly earnings still increased 0.1 percent in the release because the exact underlying levels do not cancel perfectly.
The result is close to flat purchasing power over the year for this average hourly earnings measure.
It does not mean every worker had the same result.
Real Weekly Earnings Are a Useful Extension
Hourly purchasing power and weekly purchasing power can move differently because hours can change.
For all employees, BLS also publishes average weekly hours, nominal weekly earnings, and real weekly earnings.
A simple extension is:
Weekly earnings = average hourly earnings × average weekly hours
Then deflate the weekly earnings series with CPI U and compare the result with CES0500000012.
This is useful when you want to know whether changes in hours changed weekly purchasing power.
What Real Wage Growth Can and Cannot Tell You
Real wage growth is useful, but it has limits.
Average hourly earnings are an average across jobs
BLS explains that average earnings are arithmetic averages for jobs in the private nonfarm sector.
The mix of employment can change.
If low-wage jobs fall more than high-wage jobs, average hourly earnings can rise even without a broad pay increase for individual workers. The reverse can also happen.
Average hourly earnings are not median wages
The series does not describe the wage of a typical worker.
It is an average across jobs.
A median wage measure answers a different question.
Benefits are not included
Average hourly earnings do not measure the full value of employee benefits or total compensation.
Health insurance, retirement benefits, employer taxes, and some irregular payments are outside the measure.
CPI is a national average price index
Your household may spend a different share on rent, food, transport, medical care, or energy.
Your personal inflation experience can be different from CPI U.
Distribution is hidden
A national average real wage rate does not show how gains are distributed by income, occupation, industry, region, age, or demographic group.
The analysis is descriptive
A rise in real wages can happen because nominal wages rise, inflation falls, or both.
The chart does not prove that real wage growth caused consumer spending, employment, or economic growth.
Validation Checks Before Publishing
A reproducible article should fail loudly when the data are not aligned.
required = {
"nominal_ahe",
"real_ahe_bls",
"cpi_u",
}
missing = required - set(wide.columns)
if missing:
raise ValueError(f"Missing series: {missing}")
assert wide.index.is_unique
assert (wide["nominal_ahe"] > 0).all()
assert (wide["cpi_u"] > 0).all()
wide = wide.sort_index()
wide["real_ahe_calc"] = (
wide["nominal_ahe"]
/ wide["cpi_u"]
* 100
)
wide["validation_gap"] = (
wide["real_ahe_calc"]
- wide["real_ahe_bls"]
)
Also check the latest common date explicitly.
latest_common = wide.index.max()
print("Latest common month:", latest_common)
Do not take the maximum date from the wage series alone.
Reproducibility Record
| Field | Value |
|---|---|
| Data provider | U.S. Bureau of Labor Statistics |
| Nominal wage series | CES0500000003 |
| Main CPI series | CUSR0000SA0 |
| Validation real wage series | CES0500000013 |
| Frequency | Monthly |
| Seasonal status | Seasonally adjusted for the main BLS Real Earnings replication |
| Real wage level formula | Nominal AHE divided by CPI U, multiplied by 100 |
| Growth formula | Percent change in calculated real AHE levels |
| Approximation | Nominal wage growth minus CPI inflation, labeled approximate |
| Latest common published release month | June 2026 as of August 10, 2026 |
| Retrieval date | August 10, 2026 |
| Missing value policy | Do not fill missing main series values with zero |
| Revision note | CES and seasonally adjusted CPI can be revised |
| Validation | Compare with CES0500000013 and the BLS Real Earnings release |
Frequently Asked Questions
What is real wage growth?
Real wage growth is the change in a wage measure after adjusting for consumer price changes. Positive real wage growth means that wage measure gained purchasing power relative to the selected price index.
How do you calculate real wage growth?
Use the exact ratio formula or calculate real wage levels first. Divide nominal wage by CPI, then compare the real levels across two dates.
What is the exact real wage growth formula?
Divide one plus nominal wage growth by one plus inflation, then subtract one. Multiply the result by 100 to express it as a percent.
Can I just subtract inflation from wage growth?
You can use subtraction as a quick approximation. It is not the exact formula, and rounded release values can hide a small positive or negative exact result.
What does positive real wage growth mean?
It means the selected wage measure rose faster than the selected price index over that period.
What does negative real wage growth mean?
It means the selected price index rose faster than the selected wage measure.
What is the difference between nominal wages and real wages?
Nominal wages are measured in current dollars. Real wages adjust those dollars for the price level.
Which CPI should I use to calculate real wages?
To reproduce the BLS all employees Real Earnings series, use seasonally adjusted CPI U, series CUSR0000SA0.
Why does BLS use CPI U for all employees and CPI W for production and nonsupervisory employees?
That is the deflator method documented in the BLS Real Earnings technical note. Match the CPI series to the worker group when reproducing BLS results.
What is real average hourly earnings?
It is average hourly earnings adjusted for consumer prices. BLS publishes the all employees series in constant 1982 to 1984 dollars.
How can I calculate real wage growth in Python?
Download nominal AHE and CPI U from the BLS API, align them by month, calculate nominal_ahe / cpi_u * 100, then calculate percent changes from that real level.
Why can my calculation differ slightly from the BLS release?
The source series and displayed release values are rounded. Revisions can also occur. Match the exact month, seasonal status, worker group, and current data vintage before deciding that there is an error.
Does real wage growth mean every worker is better off?
No. Average hourly earnings are an average across private nonfarm payroll jobs. Individual wages, hours, benefits, household inflation, and job changes can differ widely.
Can real wages rise even when nominal wage growth slows?
Yes. Real wage growth can improve when inflation slows more than nominal wage growth slows.
How often should a real wage analysis be updated?
Monthly updates make sense because BLS publishes wages, CPI, and Real Earnings each month. The update should wait until the wage and CPI data for the same reference month are both available.
Methodology and Data Notes
This tutorial uses official U.S. Bureau of Labor Statistics data.
The main wage series is CES0500000003, average hourly earnings of all employees on private nonfarm payrolls, total private, seasonally adjusted.
The main CPI series is CUSR0000SA0, CPI U, all items, seasonally adjusted.
The validation series is CES0500000013, real average hourly earnings for all employees in constant 1982 to 1984 dollars, seasonally adjusted.
The main real wage level is calculated as nominal average hourly earnings divided by CPI U, multiplied by 100.
Monthly and 12 month real wage growth are calculated from the real wage level.
The main panel uses an inner join by month. Missing wage or CPI observations are not filled with zero and are not forward filled.
M13 annual averages are removed before monthly calculations.
The June 2026 BLS Real Earnings release is used as the visible validation snapshot because July 2026 CPI and Real Earnings are scheduled for August 12, 2026.
The June values in the July 14 Real Earnings release were preliminary. The July Employment Situation later revised June nominal average hourly earnings. This article therefore records the publication date and explains why a later rerun can differ from the original release table.
Official Sources
Final Takeaway
Real wage growth compares pay with prices.
For a quick estimate, nominal wage growth minus inflation is useful. For an exact calculation, use the ratio formula or calculate real wage levels from the underlying wage and CPI data.
The best research workflow goes one step further. It validates the result against the BLS real earnings series, keeps wage and CPI months aligned, records revisions, and saves the retrieval date.
Use the calculator for a quick answer. Use the Python workflow when you need a result that can be checked and reproduced.
Last updated: August 10, 2026
