Technical writing

BLS OEWS: The Occupational Employment and Wage Statistics Behind Every Salary Benchmark

· AI Analytics
Federal DataBLSWagesLabor Economics

When an HR department benchmarks salaries, when an immigration attorney determines the prevailing wage for an H-1B petition, or when a policy analyst asks how much a software developer earns in rural Ohio versus downtown Seattle, the answer almost always traces back to a single federal dataset: the Bureau of Labor Statistics Occupational Employment and Wage Statistics program. OEWS covers more than 800 occupations across every industry and every metropolitan area in the United States, making it the most comprehensive source of occupation-level wage data the federal government produces.

What OEWS Is

The Occupational Employment and Wage Statistics program — known until 2021 as the Occupational Employment Statistics (OES) program — is a joint federal-state effort administered by BLS with data collection carried out by State Workforce Agencies. It is a periodic survey of nonfarm business establishments, asking each respondent to report how many workers it employs in each occupation and what those workers earn per hour. The result is a set of annual national, state, and metropolitan-area wage estimates for every Standard Occupational Classification (SOC) code — from fast food cooks to cardiovascular surgeons.

Unlike QCEW, which is derived from unemployment insurance tax filings, OEWS is a voluntary survey. Unlike the Current Population Survey, which surveys households, OEWS surveys establishments. The establishment-based design means that every occupation count in OEWS is anchored to a specific worksite and industry, giving it a precision in occupational detail that household surveys cannot match at scale.

BLS publishes OEWS estimates annually, with a May reference month. The national release typically arrives in the following spring. Estimates reflect conditions as of the most recent May survey, though because the program pools three years of data to improve precision, any given release incorporates survey responses from the current and two prior reference periods.

Survey Methodology

OEWS uses a mail survey of approximately 200,000 nonfarm business establishments per reference period. The survey is conducted in two semi-annual waves of roughly 66,000 establishments each, and each establishment is contacted only once during the three-year panel. The three-year panel design is the critical methodological choice: by pooling data across six semi-annual collection periods, BLS accumulates a sample large enough to publish estimates for hundreds of occupations within individual metropolitan areas without requiring an enormous single-year survey.

The sample is stratified by state, metropolitan area, industry (at roughly the 3-digit NAICS level), and establishment size class. Stratification ensures that rare but analytically important employer types — large hospitals, aerospace manufacturers, financial holding companies — are represented in proportion to their economic weight rather than their numerical prevalence among all establishments. The response rate across the panel is approximately 70%, which BLS treats as adequate for producing estimates with acceptable standard errors at the national and state levels.

Two categories of workers are excluded by design. Self-employed individuals do not appear in OEWS because they are not establishment employees. Workers in private households are likewise excluded. Both exclusions are consistent with the QCEW and CES programs, so OEWS is directly comparable to those datasets in terms of population coverage, but analysts should remember that a self-employed attorney or independent software consultant contributes nothing to the OEWS wage distribution for their occupation.

Data Fields: What Each Record Contains

Each OEWS record identifies an occupation within a geographic area and industry cross- section. The key fields in the national and area files are:

The national file also includes cross-industry occupation estimates where the same SOC code appears in multiple NAICS sectors. Industry-specific files (one per 2-digit NAICS sector) break out wages for occupations within a single industry, enabling comparisons like the wage premium for registered nurses (SOC 29-1141) in hospitals (NAICS 622) versus home health care services (NAICS 6216).

Geographic Coverage: 600+ Areas

OEWS publishes estimates at four geographic levels. National estimates are the most precise and cover all 800+ SOC occupations without suppression. State-level estimates cover all states plus the District of Columbia and cover the large majority of occupations, with suppression only for rare occupations in small states.

Metropolitan Statistical Areas and Metropolitan Divisions are the most analytically useful geographic unit for labor market analysis. BLS publishes OEWS estimates for over 600 MSAs and metropolitan divisions, representing all but the smallest labor markets in the country. Non-metropolitan area estimates group the rural portions of each state into one or more non-metro regions, providing coverage for workers outside major cities.

The trade-off is precision. Metropolitan-area estimates are built from fewer survey responses than national estimates, so standard errors are higher. For common occupations — registered nurses, truck drivers, retail salespersons — metro-area estimates are generally reliable. For rare occupations — nuclear engineers, special effects artists, astronomers — the estimates in smaller metros may be suppressed or carry very high relative standard errors. Analysts should always check EMP_PRSE and interpret flagged values accordingly.

The SOC Classification System

OEWS is organized around the Standard Occupational Classification system, a hierarchical taxonomy that BLS maintains and revises periodically (current version: SOC 2018). The SOC has four levels:

OEWS publishes estimates at both the broad and detailed occupation levels for national data, and at the detailed level for most geographies. Long time-series analysis is complicated by the SOC revision history: the SOC 2018 taxonomy reclassified a number of occupations relative to SOC 2010, particularly in the computer and mathematical major group where “Computer Programmers” and “Software Developers” were split into more specific codes. BLS publishes crosswalk tables between SOC versions to assist in constructing consistent series.

The H-1B Prevailing Wage Connection

OEWS data has a direct legal consequence that makes it matter far beyond academic labor economics: it is the foundation of the prevailing wage system governing H-1B and permanent labor certification (PERM) immigration petitions. The Department of Labor's Office of Foreign Labor Certification (OFLC) uses OEWS wage percentiles to set the prevailing wage levels that employers must meet when sponsoring a foreign worker for an H-1B visa or a green card.

The OFLC prevailing wage levels are defined by four percentile thresholds, each drawn from the OEWS distribution for the relevant occupation and geographic area:

Employers sponsoring an H-1B worker must pay at or above the applicable prevailing wage level or the employer's actual wage for similarly situated employees, whichever is higher. This means that the annual OEWS release directly determines the minimum salary for hundreds of thousands of technology, engineering, and healthcare workers on H-1B status. When OEWS wages for Software Developers rise, the legal wage floor for sponsored software developers rises with them — a consequence that immigration attorneys monitor closely every spring.

Key Findings in the Current Data

The OEWS data reveals striking patterns in occupational wages and employment concentration across the US economy.

Within Computer and Mathematical occupations, Software Developers earn a median annual wage of approximately $127,000 at the national level, reflecting the sustained demand for engineering talent. Data Scientists and Mathematical Science Occupations cluster around $108,000 at the median, while Statisticians sit near $99,000. The 25th-to-75th percentile range for Software Developers spans roughly $95,000 to $164,000 — a $70,000 spread that reflects both geographic variation and firm-type heterogeneity within a nominally homogeneous occupation.

Healthcare occupations dominate the upper end of the wage distribution. Surgeons as a broad category report median annual wages exceeding $252,000, with Oral and Maxillofacial Surgeons and Orthodontists close behind at $208,000 and above. The surgical and specialist occupations occupy virtually every slot in the top ten highest-wage detailed occupations nationally, a reflection of the combination of training barriers to entry, licensing, and inelastic demand for specialized medical care.

At the employment volume end of the spectrum, the three largest detailed occupations by total employment are Fast Food and Counter Workers (approximately 3.8 million), Retail Salespersons (approximately 3.5 million), and Cashiers (approximately 3.3 million). All three occupy the lower end of the wage distribution and account for a substantial share of total service-sector employment. The contrast — the most numerous workers earn the least, the highest earners are comparatively rare — is a structural feature of the US labor market legible in the OEWS data without additional processing.

Location quotients in the OEWS data reveal geographic specialization that reflects both natural resources and economic history. Petroleum Engineers (SOC 17-2171) are concentrated in Texas, Oklahoma, and Wyoming, where the oil and gas industry defines the local labor market. Marine Engineers and Naval Architects (SOC 17-2121) are concentrated in Virginia, Maine, and the Gulf Coast states that host major shipyards and naval installations. These location quotients are not static: metro areas that host a surge of semiconductor fab investment, for example, will see elevated location quotients for Semiconductor Processing Technicians (SOC 51-9141) within a few OEWS release cycles of the facility opening.

OEWS vs. CPS vs. QCEW: Choosing the Right Wage Dataset

Three federal datasets all purport to measure wages, but they measure different things and should be matched to the analysis at hand.

The Current Population Survey (CPS) is a monthly household survey of approximately 60,000 households, conducted jointly by the Census Bureau and BLS. The CPS is residence-based — it captures where workers live, not where they work — and it includes self-employed individuals, who are absent from OEWS. The CPS is the source of the official monthly unemployment rate and is rich in demographic detail: earnings by race, gender, educational attainment, and union status are all available. What the CPS lacks is occupational and geographic granularity at the level OEWS provides. With 60,000 households nationwide, the CPS cannot reliably estimate wages for specific occupations within metropolitan areas; the standard errors become too large to be useful below the national or large-region level.

The Quarterly Census of Employment and Wages (QCEW) is derived from UI tax filings rather than a voluntary survey, giving it near-census coverage of nonfarm employment. QCEW reports wages by establishment, industry, and geography down to the county level — a geographic depth that OEWS cannot match. However, QCEW reports only aggregate wages and average weekly wages by industry; it has no occupational breakdown at all. You cannot use QCEW to learn what a registered nurse earns in a particular county's hospitals; you can only learn the average wage of all workers in the hospital industry in that county. For occupation-level wage benchmarking, OEWS is the appropriate tool.

OEWS occupies the middle ground: richer occupational and percentile detail than QCEW, better geographic coverage and occupational granularity than the CPS, but no demographic breakdowns and no county-level data. For HR benchmarking, immigration compliance, workforce development planning, and occupational wage research, OEWS is generally the correct primary source.

How to Access OEWS Data

BLS distributes OEWS data through several channels suited to different use cases:

Python: Top Tech Occupations by Median Annual Wage

The following script downloads the national OEWS XLS file for the most recent May reference year, filters to Computer and Mathematical occupations (SOC 15-xxxx), sorts by annual median wage, and plots a horizontal bar chart of the top 20 highest-paid tech occupations with 25th–75th percentile ranges shown as error bars.

import requests
import pandas as pd
import matplotlib.pyplot as plt
import io

# Download the national OEWS XLS for the most recent May reference year
# BLS publishes national estimates at:
# https://www.bls.gov/oes/special.requests/oesnat.zip (current year)
YEAR = "2023"
url = "https://www.bls.gov/oes/special.requests/oesnat.zip"

import zipfile

resp = requests.get(url, timeout=180)
resp.raise_for_status()

# The ZIP contains one XLS per ownership type; "national_M" is the main file
with zipfile.ZipFile(io.BytesIO(resp.content)) as zf:
    names = zf.namelist()
    # Find the national all-industries file
    target = [n for n in names if n.lower().startswith("national_m") and n.endswith(".xlsx")]
    if not target:
        target = [n for n in names if n.endswith(".xlsx")]
    with zf.open(target[0]) as f:
        df = pd.read_excel(f, dtype=str)

# Standardise column names to lowercase
df.columns = [c.strip().lower() for c in df.columns]

# Filter to Computer and Mathematical occupations (SOC major group 15-0000)
# occ_code starts with "15-"
tech = df[df["occ_code"].str.startswith("15-", na=False)].copy()

# Keep only detailed occupations (6-digit SOC, e.g. "15-1252")
# Detailed codes match the pattern NN-NNNN (7 chars with hyphen)
tech = tech[tech["occ_code"].str.match(r"^d{2}-d{4}$", na=False)].copy()

# Convert annual median and percentile wage columns to numeric
wage_cols = ["a_median", "a_pct25", "a_pct75", "tot_emp"]
for col in wage_cols:
    if col in tech.columns:
        tech[col] = pd.to_numeric(tech[col], errors="coerce")

# Drop rows with no annual median (often indicates data not released)
tech = tech.dropna(subset=["a_median"]).copy()

# Sort by annual median wage descending, take top 20
top20 = tech.sort_values("a_median", ascending=False).head(20).copy()

# Build labels: occupation title
top20["label"] = top20["occ_title"].str.strip()

# Plot horizontal bar chart with 25th-75th percentile range as error bars
fig, ax = plt.subplots(figsize=(12, 9))

y_pos = range(len(top20))
medians = top20["a_median"].values / 1000  # convert to $K
p25 = top20["a_pct25"].fillna(top20["a_median"]).values / 1000
p75 = top20["a_pct75"].fillna(top20["a_median"]).values / 1000

xerr_lo = medians - p25
xerr_hi = p75 - medians

ax.barh(list(y_pos), medians, color="#0b4a8f", alpha=0.85, label="Median annual wage")
ax.errorbar(
    medians,
    list(y_pos),
    xerr=[xerr_lo, xerr_hi],
    fmt="none",
    color="#555",
    capsize=4,
    linewidth=1.2,
    label="25th-75th percentile range",
)

ax.set_yticks(list(y_pos))
ax.set_yticklabels(top20["label"].tolist(), fontsize=9)
ax.invert_yaxis()
ax.set_xlabel("Annual wage ($000s)")
ax.set_title(
    "Top 20 Computer & Mathematical Occupations by Median Annual Wage (BLS OEWS " + YEAR + ")",
    fontsize=11,
    pad=12,
)
ax.legend(loc="lower right")
ax.xaxis.grid(True, linestyle="--", alpha=0.5)
ax.set_axisbelow(True)

plt.tight_layout()
plt.savefig("oews_tech_wages.png", dpi=150)
plt.show()

print("Top 20 Computer & Mathematical occupations by median annual wage:")
print(
    top20[["occ_code", "occ_title", "tot_emp", "a_pct25", "a_median", "a_pct75"]]
    .rename(columns={
        "occ_code": "SOC",
        "occ_title": "Occupation",
        "tot_emp": "Employment",
        "a_pct25": "P25 ($)",
        "a_median": "Median ($)",
        "a_pct75": "P75 ($)",
    })
    .to_string(index=False)
)

A few notes on the implementation. The BLS OEWS bulk files are ZIP archives containing one or more Excel workbooks. Column names vary slightly between annual releases; the script normalizes them to lowercase. Wage values that BLS suppresses or cannot estimate appear as special flag codes (“#” or “*”) in the raw file; these become NaN after pd.to_numeric(..., errors='coerce') and are removed by the dropna call. The location quotient field is not used in this chart but is present in the file and can be used to annotate which occupations are geographically concentrated in specific metros.

Extending this pattern to geographic comparison requires downloading individual metropolitan-area files and concatenating them with an area identifier column before filtering and sorting. The SOC codes and column names are identical across national and metro files, so the filtering logic transfers without modification.

Limitations and Practical Cautions

The three-year rolling panel design improves precision but introduces a temporal lag that matters when occupational wages are moving quickly. The most recent OEWS estimate for software developers blends survey responses from three consecutive May surveys. During periods of rapid wage growth — as occurred in software from 2020 to 2023 — the published median will lag the current market by roughly 18 months on average. Employers using OEWS for real-time salary benchmarking should supplement with private-sector compensation surveys that have shorter reference periods.

Occupation titles and codes change between SOC revisions. Users building multi-year wage trend series must apply the SOC crosswalk tables BLS publishes alongside each revision. Failing to account for reclassifications can produce spurious wage jumps or drops when an occupation's boundary shifts.

The 2,080-hour annual wage conversion (hourly × 40 hours × 52 weeks) is a BLS convention that may not reflect actual hours worked in occupations with significant overtime, part-time prevalence, or seasonal variation. Healthcare occupations with substantial shift differentials, or retail occupations dominated by part-time schedules, may have annual wage equivalents that differ from actual annual earnings. The CPS earnings data, which captures actual annual earnings, is a better source for comparisons that require total compensation accuracy over a calendar year.

Self-employment exclusion is an increasingly important caveat for occupations like software consulting, legal services, financial advising, and healthcare. If a significant fraction of practitioners in an occupation work independently, OEWS will undercount employment and may misrepresent the wage distribution to the extent that self-employed workers earn systematically more or less than their employed counterparts.


For industry-level wages and employment at the county level — the establishment-based complement to OEWS's occupational focus — see BLS QCEW: The County-Level Employment and Wages Dataset Behind Every Local Economic Analysis.

For macroeconomic context on the GDP and output flows that underlie occupational wage levels, see BEA GDP Accounts: Measuring the Size and Composition of the US Economy.

For prevailing wage determinations in the context of H-2A and H-2B temporary worker programs — which use a parallel OEWS-based methodology — see DOL H-2 Visa Disclosures: Tracking Temporary Foreign Worker Certifications.