Technical writing

BLS Current Employment Statistics: The Federal Database Behind the Monthly Jobs Report

· 17 min read· AI Analytics
BLSCESJobs ReportEmploymentFederal Data

On the first Friday of every month, at precisely 8:30 a.m. Eastern Time, the Bureau of Labor Statistics releases a single document that moves Treasury yields, equity futures, currency markets, and Federal Reserve expectations simultaneously. The Employment Situation summary — universally called the Jobs Report — distills a payroll survey of roughly 140,000 businesses and government agencies covering 440,000 individual worksites into a headline number: total nonfarm payrolls added or lost in the prior month. That one number, and whether it beats or misses a Bloomberg consensus of roughly seventy economists, can shift the ten-year Treasury yield by fifteen basis points in sixty seconds.

This article covers the structure and design of the BLS Current Employment Statistics program, the distinction between the establishment survey and the household survey, the mechanics of the Jobs Friday release and the Wall Street consensus-tracking infrastructure that surrounds it, the NAICS supersector classification system that organizes the industry breakdown, average hourly earnings as a wage inflation indicator, the March benchmark revision process that realigns CES estimates to QCEW administrative records, the BLS public data API and its series ID naming conventions, and a Python script that pulls five years of monthly payroll data across all major supersectors and computes the labor market's recovery trajectory from the COVID-19 April 2020 trough.

What the Jobs Report is

The Employment Situation, published jointly by the BLS, combines output from two methodologically distinct surveys conducted simultaneously each month. The first is the Current Employment Statistics program, commonly called the establishment survey or payroll survey — a monthly questionnaire sent to employers asking how many workers were on their payroll during the reference week and what the aggregate payroll dollar amount was. The second is the Current Population Survey (CPS), a monthly household survey of approximately 60,000 households conducted by the Census Bureau under contract to BLS, which produces the unemployment rate, labor force participation rate, and measures of employment status that cannot be obtained from employer records.

The two surveys frequently diverge on a month-to-month basis, and understanding why is essential for interpreting the report correctly. The CES counts jobs — it measures employer payrolls, so a person holding two jobs is counted twice. The CPS counts people — a person with two jobs is counted once as employed. The CES excludes agricultural employment, private household workers, the self-employed, and unpaid family workers; the CPS includes all of these. The CES is considered the more reliable headline measure for month-to-month tracking because its sample is larger and its sampling frame (the Quarterly Census of Employment and Wages, a near-universe census of employer payrolls) is well-defined. The CPS unemployment rate is the headline measure for labor market slack because job-counting surveys cannot measure people who are out of work.

The total nonfarm payroll figure is the most-watched single number in U.S. economic data. It represents all paid civilian employment outside of farms, private households, nonprofit organizations serving individuals, and the armed forces. In 2024 that figure stood at approximately 158 million — a count of jobs, not workers, held by roughly 140 million distinct employed persons. The headline number is expressed as a monthly change in thousands: “+256K” or “−18K.”

Survey design and methodology

The CES sample frame is the Quarterly Census of Employment and Wages (QCEW), a near-complete enumeration of employer payrolls derived from state unemployment insurance (UI) tax records. Every employer that pays UI taxes is required to file quarterly wage reports with the applicable state agency; those records are aggregated by BLS into the QCEW, which covers approximately 95% of all civilian wage and salary employment. The QCEW serves both as the sample frame from which CES draws its monthly survey respondents and as the benchmark universe to which CES estimates are aligned each March.

The CES monthly survey contacts approximately 140,000 businesses and government agencies covering 440,000 individual worksites. These worksites are stratified by industry (NAICS code) and state, then sampled within strata with probability proportional to employment size. Large employers — those with 250 or more employees — are sampled with certainty (100% inclusion). Smaller employers are sampled at rates that vary by industry and state stratum. The overall sample represents approximately 34% of total nonfarm payroll employment.

The CES reference week is the pay period that includes the 12th day of the month. Employers report employment (the number of workers on the payroll who received pay for any part of that reference pay period) and total payroll dollars for that pay period. Reporting is by mail, fax, touch-tone phone, electronic data interchange, or BLS web interface. BLS has been migrating respondents toward electronic submission through its BLS Upload Survey Tool, which allows large employers to submit extracted payroll system data directly. Response rates average approximately 70% for the preliminary estimate, rising with follow-up contacts for the revised estimates.

CES estimates go through three publication rounds. The preliminary estimate is published in the Employment Situation for month t and is based on responses available at the time of tabulation — typically the Tuesday before the first Friday of the following month. The first revision is published one month later in the month t+1 Employment Situation, incorporating additional responses received after the preliminary tabulation. The second revision is published in the month t+2 report. Revisions between the preliminary and second revised estimates average approximately ±35,000 jobs but have been much larger during periods of rapid economic change. During the COVID-19 pandemic, preliminary estimates for March and April 2020 were revised by hundreds of thousands as response rates temporarily collapsed.

Key data series

The Employment Situation release contains several hundred time series; the following are the most analytically significant:

SeriesBLS Series IDWhy it matters
Total nonfarm payrollsCEU0000000001Headline jobs number; broadest measure of payroll employment
Private payrollsCEU0500000001Nonfarm ex-government; the business-cycle signal, less noise from census hiring
ManufacturingCEU3000000001Goods-sector bellwether; correlates with ISM manufacturing PMI
ConstructionCEU2000000001Housing activity proxy; sensitive to mortgage rates and residential starts
Retail tradeCEU4142000001Consumer demand indicator; highly seasonal around holidays
Professional & business servicesCEU6000000001Includes temp staffing; temp employment is a leading labor market indicator
Education & health servicesCEU6500000001Largest private supersector by employment; relatively recession-resistant
Leisure & hospitalityCEU7000000001Most volatile; shed 8.2M jobs in March–April 2020 alone
GovernmentCEU9000000001Federal, state, and local; census-driven hiring cycles distort headlines
Average hourly earnings (AHE), privateCEU0500000011Wage inflation gauge; Fed watches YoY growth closely in setting policy
Average weekly hours, privateCEU0500000003Hours adjustment precedes headcount changes; leading labor indicator
Avg weekly overtime hours, manufacturingCEU3000000007Very leading indicator; manufacturers cut overtime before laying off workers

Average weekly hours and average weekly overtime hours in manufacturing are component inputs to the Conference Board's Leading Economic Index (LEI), reflecting the well-established pattern that employers reduce scheduled hours and cut overtime before initiating layoffs when demand softens. A decline in average weekly hours for all private employees — the series tracked most closely by labor economists — is often the first CES signal of a hiring slowdown before the headline payroll count turns.

The Jobs Friday release

The Employment Situation is published on the first Friday of each month at 8:30 a.m. Eastern Time, with the data reflecting the prior calendar month. BLS operates a strict pre-publication embargo: a small number of White House Council of Economic Advisers staff and the BLS Commissioner receive the data approximately one hour before public release under a lock-up arrangement in which they may review the data but cannot communicate externally until the 8:30 a.m. release. The lock-up room physically lacks external telephone and internet connectivity until the embargo lifts.

Market reaction at 8:30 a.m. is one of the most observable examples of simultaneous price discovery in financial markets. In the milliseconds after release, electronic futures markets reprice based on whether the headline payroll number is above or below the Bloomberg/Refinitiv consensus survey of approximately sixty to seventy professional economists. A print that beats consensus by more than 75,000 jobs typically produces: a sell-off in ten-year Treasury futures (yield rises on expectations of less aggressive Fed rate cutting), a rise in S&P 500 futures (if the labor market strength is interpreted as economic resilience rather than inflationary), and a strengthening of the U.S. dollar index. When the print misses by more than 75,000 jobs, the opposite moves occur.

The interpretation of any given print depends heavily on context. During the 2021–2023 inflation surge, a strong payroll print was initially interpreted as inflationary — a hot labor market driving wage growth — and paradoxically negative for equities because it implied more Federal Reserve rate increases. By 2024, as inflation receded, strong payrolls were again interpreted as growth-positive. The same 200,000-job print means something different when the Fed is hiking than when it is cutting.

The Wednesday before Jobs Friday, ADP (in partnership with the Stanford Digital Economy Lab) publishes the ADP National Employment Report — an estimate of private nonfarm payroll changes derived from ADP's payroll processing data covering approximately 25 million U.S. employees. ADP is widely watched as a directional preview of the Friday BLS number, though its month-to-month correlation with the CES private payroll figure is substantially weaker than it appears in retrospect because ADP and BLS measure slightly different things and use different methodologies. The ADP report was substantially redesigned in 2022 in a partnership with Stanford; its predictive value for the BLS print remains a subject of practitioner debate.

Industry breakdown: NAICS supersectors

The CES classifies employment using the North American Industry Classification System (NAICS), which replaced the Standard Industrial Classification (SIC) system beginning with the 2002 benchmark. At the highest level, CES reports eleven supersectors — groupings of related two-digit NAICS sectors that organize the industry detail in the Employment Situation tables.

SupersectorSeries prefixComponent industries
Mining & LoggingCEU10Oil & gas extraction, coal mining, metal ore mining, logging; highly sensitive to commodity prices
ConstructionCEU20Residential and nonresidential building, heavy civil, specialty trades (electrical, plumbing, HVAC)
ManufacturingCEU30Durable goods (autos, aerospace, machinery, computers) and nondurable goods (food, chemicals, textiles)
Trade, Transportation & UtilitiesCEU40Wholesale and retail trade, trucking, rail, air transport, couriers, electric utilities, natural gas
InformationCEU50Publishing, broadcasting, telecommunications, internet services, data processing; shed jobs in 2022–2023 tech layoffs
Financial ActivitiesCEU55Banks, insurance carriers, securities dealers, real estate, rental and leasing
Professional & Business ServicesCEU60Legal, accounting, management consulting, scientific R&D, advertising, temporary help agencies
Education & Health ServicesCEU65Private education (K-12, universities), hospitals, ambulatory health care, nursing and residential care
Leisure & HospitalityCEU70Arts, entertainment, recreation, accommodation, food services; most COVID-sensitive
Other ServicesCEU80Repair and maintenance, personal services (laundries, pet care), membership associations, religious organizations
GovernmentCEU90Federal, state, and local government civilian employment (military excluded); includes public schools and hospitals

Below the supersector level, CES publishes employment data at successively finer NAICS levels: sector (two-digit), subsector (three-digit), industry group (four-digit), and NAICS industry (five- or six-digit). At the national level, CES publishes approximately 500 distinct industry series. At the state and metropolitan area level, the State and Metro Area Employment, Hours, and Earnings (SAE) program publishes CES-equivalent data for all 50 states and nearly 400 metropolitan statistical areas, though at coarser industry granularity and on a slightly delayed release schedule.

Professional and Business Services is analytically important beyond its employment size because it includes Temporary Help Services (NAICS 561320), which has historically been one of the most reliable leading indicators in the payroll data. When businesses anticipate increasing demand, they often hire temp workers before converting to permanent headcount; when they anticipate a slowdown, they cut temp workers first. The temporary help subseries peaked in early 2022 and declined throughout 2022–2023, a development that labor economists cited as an early warning of slowing labor demand even as headline payrolls remained strong.

Average hourly earnings and wage inflation

Average hourly earnings (AHE) is computed from two CES data items collected in the same survey instrument: total payroll dollars paid during the reference week and total hours paid (including overtime) during that week. AHE equals total payroll divided by total hours. BLS publishes AHE for all private employees and for production and nonsupervisory employees separately; the production and nonsupervisory series has a longer history and is useful for historical comparisons.

By 2024, AHE for all private employees stood at approximately $35 per hour, up from roughly $29 in early 2020 — a cumulative increase of approximately 20% in nominal terms over four years. Whether that represents real wage growth depends on the inflation comparison: during 2021–2022, AHE growth of 5–6% annually was consistently below CPI inflation of 7–9%, meaning most workers experienced real wage losses despite nominal raises. By mid-2023, as CPI inflation declined toward 3–4% and AHE growth remained near 4%, real wages turned positive again for the first time since the inflation surge began.

The Federal Reserve watches AHE closely in the context of the wage-price spiral concern: the theory that sustained high wage growth becomes self-reinforcing as businesses raise prices to cover higher labor costs, which then drives further wage demands. The Fed's preferred wage measure is the Employment Cost Index (ECI), a quarterly survey of wage and benefit costs published by BLS that controls for compositional shifts in the workforce (AHE can rise mechanically when low-wage workers are laid off and the average worker remaining is higher-paid). During the 2021–2023 tightening cycle, FOMC members regularly cited both AHE and ECI growth as factors in their rate decisions, and the pace of AHE deceleration was treated as a key condition for pausing and eventually cutting the federal funds rate.

AHE has a well-known compositional bias: it is not a true wage index for any individual worker but rather a cross-sectional average that shifts when the composition of the employed population changes. During COVID-19, AHE paradoxically surged in March and April 2020 even as the economy was collapsing — because most of the 22 million jobs lost were in low-wage sectors like leisure and hospitality, the average hourly earnings of those who remained employed rose mechanically. For that reason, analysts supplement AHE with the Atlanta Fed Wage Growth Tracker, a median wage growth measure that tracks the same individuals over time and avoids composition effects.

Benchmark revisions

The largest and most consequential CES data revision happens once per year in March, when BLS publishes benchmark-revised employment estimates. The benchmark revision realigns the CES monthly estimates to the QCEW administrative records for the twelve-month period ending the prior March. Because the QCEW is itself nearly a census of employer payrolls (rather than a sample), it serves as the gold standard to which the survey-based CES estimates are corrected.

The practical mechanism: each March, BLS publishes revised employment levels for the prior twelve months (April of the previous year through March of the current year) that incorporate complete QCEW records rather than estimated trends. The difference between the final survey-based estimate and the benchmark is the benchmark revision. Benchmarks are typically small in absolute terms relative to total employment — usually within 0.3% of the March level, or roughly 400,000 to 500,000 jobs on a base of 158 million. In extraordinary circumstances they can be much larger: the preliminary March 2024 benchmark showed a downward revision of approximately 818,000 jobs for the twelve months ending March 2023, the largest benchmark revision in fifteen years, raising concerns that the CES survey had overestimated job creation during the post-COVID hiring surge.

Benchmark revisions affect the historical series back to the prior March, not the entire history. Earlier periods are revised only in the annual comprehensive revisions to seasonal adjustment factors, which BLS also publishes each February when it updates the seasonal factors using an additional year of data. Seasonal adjustment is applied to CES employment series to remove predictable patterns — the surge in retail employment every November and December, the drop in construction in January in cold-weather states, the school-year rhythm of education employment — so that month-to-month comparisons reflect genuine labor market changes rather than calendar effects.

Historical context: recession troughs and COVID-19

Total nonfarm payrolls peaked in January 2008 at approximately 138.4 million and declined through the Great Recession to a trough in February 2010 at approximately 129.7 million — a loss of 8.7 million jobs over 25 months. The recovery took more than six years: payrolls did not return to their pre-recession peak until May 2014, and the unemployment rate did not return to its pre-recession level until 2017. The Great Recession jobs loss was concentrated in construction (which lost approximately 2.2 million jobs, reflecting the collapse of the housing bubble) and manufacturing (1.2 million), with professional and business services (including temp staffing) also sharply affected.

The COVID-19 collapse in March–April 2020 was unlike any prior labor market event in the CES record, which extends back to 1939. Total nonfarm payrolls declined by approximately 1.4 million in March 2020 and then by an additional 20.5 million in April 2020 alone — the largest single-month decline ever recorded, representing roughly 13% of the entire nonfarm payroll base in one month. The April 2020 unemployment rate reached 14.7%, the highest since the Great Depression. Leisure and hospitality lost 8.2 million jobs in April 2020; government lost 980,000. The recovery began almost immediately as state governments re-opened, with 2.7 million jobs added in May 2020 and large monthly gains continuing through summer and fall.

The COVID recovery illustrated fundamental labor market dynamics that will influence policy debates for years. Labor force participation fell sharply in 2020 as millions of workers, particularly older workers and those with caregiving responsibilities, exited the workforce entirely. Many did not return: the labor force participation rate for workers aged 55 and over remained below its pre-pandemic level through 2023, reflecting what researchers have called a pandemic-induced retirement wave. Among prime-age workers (aged 25–54), participation recovered more fully, eventually exceeding pre-pandemic levels by 2023. The composition of the workforce also shifted: remote-capable professional services employment expanded, leisure and hospitality faced persistent staffing shortages even after total job counts recovered, and the relationship between the unemployment rate and wage growth (the Phillips Curve) appeared to shift as employers raised wages aggressively to attract workers into positions they perceived as risky or undesirable.

BLS API access and data download

BLS provides public access to CES data through the BLS Public Data API v2, available at https://api.bls.gov/publicAPI/v2/timeseries/data/. The API accepts JSON POST requests specifying a list of series IDs, a start year, an end year, and an optional registration key. Without a registration key, requests are limited to 25 series per query and 3 years of history; with a free registration key (obtained at data.bls.gov), limits expand to 500 series per query and 10 years of data, with up to 50 requests per day.

CES series IDs follow a structured naming convention: the prefixCEU (Current Employment, Unadjusted) or CES(Current Employment, Seasonally Adjusted) is followed by an eight-digit industry code and a two-digit data type code. The industry code structure mirrors the NAICS hierarchy: the first two digits identify the supersector, subsequent digits identify finer industry detail, and trailing zeros pad the code to eight digits. Data type codes govern what is measured: 01is all employees (thousands), 03 is average weekly hours,06 is production/nonsupervisory employees, 07 is average weekly overtime hours (manufacturing only), 11 is average hourly earnings, and 30 is average weekly earnings.

Seasonally adjusted series use the CES prefix; unadjusted series use CEU. For most analytical purposes, the seasonally adjusted series is appropriate because it removes calendar effects that would otherwise dominate month-to-month comparisons. The Employment Situation release itself leads with seasonally adjusted figures. The BLS also provides the full CES database for bulk download at bls.gov/ces/data.htm, in flat text files organized by supersector that include all available series from the 1939 start of the establishment survey through the current month.

Beyond the CES API, BLS also exposes QCEW data, Local Area Unemployment Statistics (LAUS), the Occupational Employment and Wage Statistics (OEWS) program, and the Mass Layoffs Statistics through the same API endpoint using different series ID prefixes. For QCEW: series IDs use the prefixENU followed by the FIPS state code, ownership code, NAICS industry code, and data type. LAUS uses the prefix LA. The BLS series ID structure documentation is available at bls.gov/help/hlpforma.htm and is the essential reference for programmatic API access.

Python: pulling CES data from the BLS API

The following script uses only the Python standard library plusrequests to query the BLS API for five years of monthly employment data across all major CES supersectors. It prints a formatted table showing the latest employment level and year-over-year change for each industry, reports average hourly earnings and average weekly hours for the private sector and manufacturing, and computes the labor market's recovery percentage from the COVID-19 April 2020 trough. Register a free API key at data.bls.gov/registrationEngine/ and substitute it for the placeholder in the script.

import requests
import json
import time

# ---------------------------------------------------------------------------
# BLS Current Employment Statistics (CES) — Jobs Report Data Pull
# ---------------------------------------------------------------------------
# BLS Public Data API v2: https://api.bls.gov/publicAPI/v2/timeseries/data/
#
# CES series ID format: CEU + supersector_code + industry_code + data_type
#   Data type 01 = All employees (thousands)
#   Data type 03 = Average weekly hours, all employees
#   Data type 11 = Average hourly earnings, all employees
#
# Registration (free): https://data.bls.gov/registrationEngine/
# Registered key: 500 series/query, 10 years of data, 50 req/day
# Unregistered:    25 series/query,  3 years of data, 25 req/day

BLS_API_URL = "https://api.bls.gov/publicAPI/v2/timeseries/data/"
REGISTRATION_KEY = "YOUR_BLS_API_KEY_HERE"   # register free at data.bls.gov

# ---------------------------------------------------------------------------
# Key CES series — Total nonfarm employment (data type 01 = all employees)
# ---------------------------------------------------------------------------
SERIES = {
    "CEU0000000001": "Total Nonfarm",
    "CEU0500000001": "Total Private",
    "CEU0600000001": "Goods-Producing",
    "CEU2000000001": "Construction",
    "CEU3000000001": "Manufacturing",
    "CEU3132900001": "Durable Goods Mfg",
    "CEU3133400001": "Nondurable Goods Mfg",
    "CEU4000000001": "Trade / Transp / Utilities",
    "CEU4142000001": "Retail Trade",
    "CEU5000000001": "Information",
    "CEU5500000001": "Financial Activities",
    "CEU6000000001": "Professional & Business Svcs",
    "CEU6500000001": "Education & Health Svcs",
    "CEU7000000001": "Leisure & Hospitality",
    "CEU8000000001": "Other Services",
    "CEU9000000001": "Government",
}

# Also pull average hourly earnings and average weekly hours for all private
AHE_SERIES = {
    "CEU0500000011": "Avg Hourly Earnings (All Private, $)",
    "CEU0500000003": "Avg Weekly Hours (All Private)",
    "CEU3000000011": "Avg Hourly Earnings (Manufacturing, $)",
    "CEU3000000007": "Avg Weekly Overtime Hours (Manufacturing)",
}

ALL_SERIES = {**SERIES, **AHE_SERIES}

# BLS API accepts up to 50 series per request (registered key)
series_ids = list(ALL_SERIES.keys())

import datetime
end_year   = datetime.date.today().year
start_year = end_year - 5    # 5 years of monthly data

print(f"Fetching {len(series_ids)} CES series from BLS API ({start_year}-{end_year})...")

payload = {
    "seriesid":        series_ids,
    "startyear":       str(start_year),
    "endyear":         str(end_year),
    "registrationkey": REGISTRATION_KEY,
    "catalog":         False,
    "calculations":    False,
    "annualaverage":   False,
}

resp = requests.post(BLS_API_URL, json=payload, timeout=60)
resp.raise_for_status()
data = resp.json()

if data.get("status") != "REQUEST_SUCCEEDED":
    print(f"BLS API error: {data.get('message', data)}")
    raise SystemExit(1)

# ---------------------------------------------------------------------------
# Parse results into a dict: series_id -> list of (year, period, value)
# period is 'M01'..'M12'
# ---------------------------------------------------------------------------
series_data = {}
for series_obj in data.get("Results", {}).get("series", []):
    sid   = series_obj["seriesID"]
    rows  = series_obj.get("data", [])
    # Sort ascending by year, then period
    rows_sorted = sorted(rows, key=lambda r: (int(r["year"]), r["period"]))
    series_data[sid] = rows_sorted

def latest_value(sid):
    """Return (year, month_label, value_float) for the most recent data point."""
    rows = series_data.get(sid, [])
    if not rows:
        return None, None, None
    r     = rows[-1]
    year  = r["year"]
    month = r["period"]   # 'M01' .. 'M12'
    val   = float(r["value"])
    month_num = int(month.replace("M", ""))
    month_name = datetime.date(int(year), month_num, 1).strftime("%b %Y")
    return year, month_name, val

def value_one_year_ago(sid):
    """Return the value from 12 months before the latest available data point."""
    rows = series_data.get(sid, [])
    if len(rows) < 13:
        return None
    # Latest is rows[-1], 12 months ago is rows[-13]
    return float(rows[-13]["value"])

# ---------------------------------------------------------------------------
# Print employment table: latest month + YoY change
# ---------------------------------------------------------------------------
print()
print("=" * 78)
print("  BLS CURRENT EMPLOYMENT STATISTICS &mdash; LATEST MONTH SNAPSHOT")
print("=" * 78)

# Get a reference date from the total nonfarm series
_, ref_date, _ = latest_value("CEU0000000001")
print(f"  Reference period: {ref_date}  |  Employment in thousands of jobs")
print()
print(f"  {'Supersector':<38}  {'Level (000s)':>13}  {'YoY Chg (000s)':>15}  {'YoY %':>7}")
print("  " + "-" * 78)

for sid, label in SERIES.items():
    _, _, val = latest_value(sid)
    ago       = value_one_year_ago(sid)
    if val is None:
        print(f"  {label:<38}  {'N/A':>13}  {'N/A':>15}  {'N/A':>7}")
        continue
    if ago is not None:
        chg  = val - ago
        pct  = (chg / ago) * 100
        chg_str = f"{chg:>+,.1f}"
        pct_str = f"{pct:>+.2f}%"
    else:
        chg_str = "N/A"
        pct_str = "N/A"
    indent = "    " if label not in ("Total Nonfarm", "Total Private", "Government") else ""
    print(f"  {indent}{label:<38}  {val:>13,.1f}  {chg_str:>15}  {pct_str:>7}")

# ---------------------------------------------------------------------------
# Average hourly earnings and average weekly hours
# ---------------------------------------------------------------------------
print()
print("=" * 78)
print("  AVERAGE HOURLY EARNINGS & AVERAGE WEEKLY HOURS")
print("=" * 78)
print()
print(f"  {'Series':<45}  {'Latest':>10}  {'1yr Ago':>10}  {'YoY Chg':>10}")
print("  " + "-" * 78)

for sid, label in AHE_SERIES.items():
    _, _, val = latest_value(sid)
    ago       = value_one_year_ago(sid)
    if val is None:
        print(f"  {label:<45}  {'N/A':>10}")
        continue
    val_str = f"{val:.2f}"
    if ago is not None:
        ago_str = f"{ago:.2f}"
        chg     = val - ago
        chg_str = f"{chg:+.2f}"
    else:
        ago_str = "N/A"
        chg_str = "N/A"
    print(f"  {label:<45}  {val_str:>10}  {ago_str:>10}  {chg_str:>10}")

# ---------------------------------------------------------------------------
# COVID-19 recovery analysis
# Peak nonfarm payrolls: Feb 2020 = ~152,504k
# Trough:               Apr 2020 = ~130,160k  (lost ~22,344k jobs)
# ---------------------------------------------------------------------------
COVID_PEAK_FEB2020   = 152_504.0   # thousands (BLS historical)
COVID_TROUGH_APR2020 = 130_160.0   # thousands

_, _, current_nonfarm = latest_value("CEU0000000001")

if current_nonfarm is not None:
    jobs_lost     = COVID_PEAK_FEB2020 - COVID_TROUGH_APR2020
    jobs_recovered = current_nonfarm - COVID_TROUGH_APR2020
    recovery_pct  = (jobs_recovered / jobs_lost) * 100

    print()
    print("=" * 78)
    print("  COVID-19 LABOR MARKET RECOVERY TRACKER")
    print("=" * 78)
    print(f"  Pre-pandemic peak  (Feb 2020):  {COVID_PEAK_FEB2020:>10,.0f}k nonfarm payroll jobs")
    print(f"  Pandemic trough    (Apr 2020):  {COVID_TROUGH_APR2020:>10,.0f}k  ({COVID_PEAK_FEB2020 - COVID_TROUGH_APR2020:,.0f}k jobs lost)")
    print(f"  Current payrolls   ({ref_date}): {current_nonfarm:>10,.1f}k")
    net_vs_peak = current_nonfarm - COVID_PEAK_FEB2020
    sign        = "+" if net_vs_peak >= 0 else ""
    print(f"  Net vs. pre-pandemic peak:      {sign}{net_vs_peak:,.1f}k")
    print(f"  Recovery from trough:           {recovery_pct:.1f}% of pandemic job loss recovered")

    bar_len = int(min(recovery_pct, 100) / 2)
    bar     = "#" * bar_len + "-" * (50 - bar_len)
    print(f"  Progress: [{bar}] {min(recovery_pct,100):.1f}%")

print()
print("Source: U.S. Bureau of Labor Statistics Current Employment Statistics (CES)")
print("        https://www.bls.gov/ces/  |  API: https://api.bls.gov/publicAPI/v2/")

The script handles BLS API pagination by keeping all series in a single request (the API supports up to 500 series per call with a registered key). The year-over-year change is computed by comparing the most recent data point to the value thirteen months prior (i.e., 12 months back in the sorted array), which aligns months correctly even when the latest data is preliminary and the prior year's data for the same month is revised. For production use, supplement this with a check on the footnotes field returned by the API, which BLS uses to flag preliminary estimates and seasonal adjustment revisions. The bulk flat-file download from bls.gov/ces/data.htm is more appropriate for analyses requiring the full historical series back to 1939 or for local caching to avoid API rate limits.

Data limitations and research notes

CES payroll employment is a count of jobs, not workers. An individual who holds two paid jobs counts twice in the CES tally. The distinction matters most during tight labor markets when multi-job holding rises: if 100,000 workers each take a second job, CES reports 100,000 jobs added while the CPS records zero change in the number of employed persons. The CPS does collect multi-job holding statistics but does not publish them as a headline monthly series.

The birth-death model is a frequently misunderstood component of CES estimation. Because new businesses are not in the QCEW sample frame until they file their first UI wage report (typically one to two quarters after opening), CES cannot directly survey them. BLS estimates employment at new businesses using a net birth-death adjustment model — a time-series model that estimates the net job creation at businesses too new to be in the sample, based on historical patterns of business entry and exit. The birth-death model is derived from QCEW data for prior periods and is applied as a monthly additive factor to the survey-based estimate. During periods of unusual economic conditions, the birth-death model can introduce systematic errors: it likely overestimated job creation in 2022–2023 (when new business formations were high but may not have translated to actual hiring as robustly as the historical model predicted), contributing to the large downward benchmark revision published in 2024.

CES excludes self-employment, which represents a growing share of the workforce as platform-based gig work expands. Uber drivers, DoorDash couriers, and independent contractors working through online platforms are not in the CES count if they are classified as self-employed. The CPS asks about self-employment status and captures this population. BLS publishes supplementary data on contingent and alternative employment arrangements from the Contingent Worker Supplement to the CPS, conducted periodically rather than monthly. The definitional distinction between employees and independent contractors is also the subject of ongoing legal and regulatory debate under state and federal labor law, with implications for which workers appear in CES payroll counts and which do not.

Related writing

BOP Federal Prison Population: The Federal Database Behind 148,000 US Federal Inmates — federal criminal justice data from the Bureau of Prisons, offense types, demographics, First Step Act reforms.

BLS QCEW: The Federal Database Behind US Payroll Data for Every Industry and County — quarterly payroll data from UI administrative records, 40M+ records by NAICS industry and county.