Technical writing

NLRB Unfair Labor Practice Data: 300,000 Cases of Worker-Management Conflict

· 14 min read· AI Analytics
Federal DataNLRBLabor LawEnforcement

Since 1935, the National Labor Relations Board has recorded every unfair labor practice charge filed under the National Labor Relations Act. Over 300,000 cases are publicly searchable through the NLRB's case management system. Each record contains the docket number, charge party, respondent name and address, regional office, charge date, and disposition — whether the charge was dismissed, withdrawn, settled, or prosecuted to a full Board decision. The dataset is free, queryable via API, and underused by researchers who would benefit most from it.

This article covers what constitutes an unfair labor practice under the NLRA, the full case lifecycle from charge filing to court enforcement, the data structure and access methods available through the NLRB case search system, key statistics on filing volumes and disposition rates, the 2022–2024 Starbucks and Amazon organizing surge visible in the data, a Python approach for downloading and analyzing ULP disposition rates by industry, and the significance of this dataset for journalists covering labor organizing and corporate accountability.

What constitutes an unfair labor practice

The National Labor Relations Act defines the universe of conduct that the NLRB can investigate and remedy. The key provisions are Sections 7 and 8.

Section 7 establishes the fundamental rights the NLRA protects: the right of employees to self-organize, to form, join, or assist labor organizations, to bargain collectively through representatives of their own choosing, and to engage in other concerted activities for the purpose of collective bargaining or other mutual aid or protection. Critically, Section 7 also protects the right of employees to refrain from any or all of these activities. The practical scope of Section 7 is broader than formal union organizing — it covers any two or more employees acting together to improve their working conditions, including employees who complain collectively about pay, discuss wages with coworkers, or walk off the job together over a safety dispute, whether or not a union is involved.

Section 8(a) prohibits unfair labor practices by employers. The five subsections cover the core employer violations:

Section 8(a)(1) is the broadest: it bars employers from interfering with, restraining, or coercing employees in the exercise of their Section 7 rights. Every other Section 8(a) violation is also a derivative 8(a)(1) violation. Standalone 8(a)(1) charges cover employer interrogation of employees about union sympathies, surveillance of union meetings, threats of plant closure if workers unionize, promises of benefits contingent on rejecting a union, and maintenance of overly broad no-solicitation or confidentiality policies that chill protected communication.

Section 8(a)(2) prohibits employer domination of or interference with the formation or administration of a labor organization. This provision targets company unions — employer-created or employer-controlled worker groups that give the appearance of collective representation without the independence of genuine collective bargaining. It also reaches employer assistance to a union competing with a rival union seeking to represent the same employees.

Section 8(a)(3) bars discrimination in hire, tenure, or any term of employment to encourage or discourage union membership. This is the anti-retaliation provision most frequently cited alongside discharge cases: the employer who fires the union organizer or the employee who filed an OSHA complaint through concerted action faces an 8(a)(3) charge. Constructive discharge — making conditions intolerable until a protected employee quits — is also covered.

Section 8(a)(4) prohibits discharge or discrimination against employees who have filed charges or given testimony under the NLRA. It is the retaliation-for-NLRB-participation provision: once a worker files a ULP charge, firing that worker generates an independent 8(a)(4) violation layered on top of whatever underlying conduct generated the initial charge.

Section 8(a)(5) requires employers to bargain in good faith with the certified or recognized representative of their employees. Bargaining-in-bad-faith charges cover surface bargaining (going through the motions without genuine intent to reach agreement), unilateral changes to terms and conditions of employment without bargaining, bypassing the union to deal directly with represented employees, and withdrawal of recognition from a certified union.

Section 8(b) covers unfair labor practices by labor organizations. The most significant provision for research purposes is Section 8(b)(1)(A), which prohibits unions from restraining or coercing employees in the exercise of their Section 7 rights — the union-side counterpart to the employer prohibition. More practically relevant for data analysis is the duty of fair representation, a doctrine derived from the union's status as exclusive bargaining representative: a union that handles a member's grievance in an arbitrary, discriminatory, or bad-faith manner commits an unfair labor practice. Duty of fair representation charges account for a substantial share of the 8(b) case volume in the NLRB's database.

The case lifecycle

Every ULP charge follows a defined procedural path from initial filing through potential federal court enforcement. Understanding the lifecycle is essential for interpreting what the disposition codes in the database actually mean.

A charge is filed by any person — the charging party — with the NLRB regional office covering the geographic area where the alleged violation occurred. There are 26 regional offices plus several sub-regional and resident offices. The charge identifies the respondent (the employer or union accused of the violation), the approximate dates of the alleged conduct, and the NLRA section allegedly violated. Filing is free; there is no administrative fee. The regional office logs the charge and assigns a docket number in a format encoding the region, case type, and fiscal year: for example, 32-CA-320914 identifies region 32 (Oakland), a charge against an employer (CA = Charge Against Employer), in the fiscal year the sequence falls within.

The regional office investigation follows filing. A field examiner (or attorney) interviews the charging party, reviews documents, and interviews the respondent. The regional director then makes a merit determination. If the investigation finds insufficient evidence to support the charge, the regional director issues a dismissal letter. The charging party may appeal a dismissal to the NLRB's Office of Appeals in Washington. If the investigation finds reasonable cause to believe a violation occurred, the regional director attempts to settle the case before issuing a formal complaint.

A settlement at the regional level typically involves the respondent agreeing to remedy the violation — reinstatement of a discharged employee, payment of back pay, posting of a notice to employees — without the Board issuing a formal complaint. Settlements constitute the largest single disposition category in the database after dismissals and withdrawals.

If no settlement is reached, the regional director issues a formal complaint that functions as a charging document specifying the alleged violations in legal terms. The case then proceeds to a hearing before an Administrative Law Judge (ALJ). The ALJ presides over an evidentiary hearing, takes testimony from witnesses, admits documentary evidence, and issues a written decision with findings of fact, conclusions of law, and a recommended order. The ALJ decision is not the final word.

Either party may appeal the ALJ decision to the full National Labor Relations Board in Washington, a five-member panel of presidentially appointed, Senate-confirmed members. The Board reviews the record and issues its own decision, which may adopt, modify, or reverse the ALJ's recommended order. Board decisions are precedential and published in NLRB Reports. They constitute the authoritative interpretation of the NLRA for purposes of that charge.

Board orders are not self-executing. If a respondent refuses to comply, the Board petitions a federal circuit court of appeals for enforcement of its order. The court of appeals reviews the Board's legal conclusions de novo and its factual findings under the substantial evidence standard. The court may enforce, modify, or refuse to enforce the Board order. If the respondent seeks review and the Board seeks enforcement, both proceedings are consolidated in the same circuit.

Data structure and access

The NLRB makes its case management data available through two primary channels: the web-based case search interface at nlrb.gov/cases-decisions/cases and a programmatic API that returns structured JSON.

Each case record in the system contains the following core fields:

case_number        # docket number, e.g. 32-CA-320914
                   # format: [region]-[type]-[sequence]
                   # CA = Charge Against Employer
                   # CB = Charge Against Union (Labor Org)
                   # CD = Charge Against Both

case_type          # ULP, RC (representation), RD (decert), RM, AC
date_filed         # date charge was filed with regional office
date_closed        # date case was formally closed
region             # two-digit regional office code (01-32)
city               # respondent city
state              # respondent state
respondent         # respondent name (employer or union)
charge_party       # charging party name (often individual)
union              # union name if applicable
industry           # NAICS description (not always populated)
status             # open or closed
close_reason       # disposition code (see below)

# Disposition codes for closed cases:
# ADISM   - Dismissed after appeal
# ADJ     - Adjudicated (ALJ/Board decision issued)
# COMPLY  - Compliance with Board order
# SDISM   - Settled at regional level before complaint
# CDISM   - Complaint dismissed after hearing
# WITH    - Withdrawn by charging party
# RDISM   - Regional director dismissal (no merit)
# SETTLE  - Settled after complaint issued

The NLRB's public API endpoint for case search is accessible without authentication for read operations. The base URL is https://www.nlrb.gov/cases-decisions/cases/listwith query parameters controlling filtering. The API returns paginated JSON with aresults array and total count. Key parameters include case_type,date_start, date_end, region, andrespondent for name search. The system does not provide a true bulk export endpoint; downloading the full historical record requires paginating through results in date-bounded windows.

Key statistics: filing volumes and disposition rates

The NLRB receives between 20,000 and 25,000 ULP charges per fiscal year under normal conditions. This figure has fluctuated with economic cycles, organizing surges, and changes in NLRB enforcement posture. The post-2021 organizing environment pushed annual charge volumes toward the higher end of this range.

The disposition breakdown across the historical record reveals a funnel that narrows sharply at each stage:

Approximately 65 percent of charges are dismissed or withdrawn before a complaint issues. Dismissals (cases where the regional director finds insufficient evidence) and withdrawals (cases where the charging party drops the charge, often after informal resolution) together dominate the disposition statistics. This figure is not evidence of frivolous filing — withdrawals often reflect private settlement between the parties before the regional office completes its investigation. The NLRB's informal settlement rate, which captures resolutions before a formal complaint, is counted within this category.

Roughly 30 percent of charges result in a complaint being issued. Of those complaints, many settle before an ALJ hearing. Post-complaint settlements typically involve more comprehensive remedies than pre-complaint informal resolutions because the respondent faces the cost and uncertainty of a formal hearing.

Fewer than 5 percent of filed charges reach an ALJ hearing. ALJ decisions are further appealed to the Board in a fraction of those cases. Approximately 1 percent of all charges filed in a given fiscal year eventually result in a published Board decision — the authoritative resolution on the merits. This means the Board's published decisions, while legally significant, represent a highly selected subset of the full conflict landscape captured in the charge database.

The 2022–2024 organizing surge in NLRB data

The most significant recent signal in the NLRB ULP database is the organizing surge associated with Starbucks and Amazon beginning in late 2021 and running through 2024. Both campaigns generated unusually high ratios of ULP charges to election petitions, reflecting aggressive employer response to organizing activity.

Starbucks Workers United filed its first election petition in August 2021 at the Buffalo, New York Elmwood Avenue store. Within eighteen months, charges against Starbucks had been filed in nearly every NLRB region. By mid-2023, the NLRB had issued complaints against Starbucks in more than 80 cases, alleging violations including discharge of union organizers, surveillance of union activity, unilateral changes to wages and benefits at non-union stores to discourage organizing, and refusal to bargain in good faith with newly certified units. The concentration of CA charges — charges against an employer — in the retail food and beverage NAICS codes from late 2021 onward is directly visible in the NLRB case data as a spike in case filings involving a single respondent name across multiple regions.

The Amazon Labor Union's successful election at the JFK8 fulfillment center on Staten Island in April 2022 generated its own ULP charge stream. Amazon's challenges to the election result, and subsequent charges alleging that Amazon refused to bargain with the certified union, created a cluster of CB and CA charges in Region 29 (Brooklyn) that can be isolated by region and respondent name. The JFK8 bargaining refusal charge became one of the higher-profile CB/CA combinations in the database during this period.

For researchers, the practical significance is that case volume alone is informative. A sudden increase in CA charges against a specific employer across multiple regions — identifiable by filtering on respondent name and date range — signals an organized opposition campaign by that employer, regardless of whether any individual charge results in a complaint. The NLRB data captured the Starbucks campaign in near-real-time; an analyst monitoring new CA charge filings by respondent name could have detected the national scope of the campaign from regional filings months before it was widely covered in the press.

Downloading and analyzing ULP disposition rates by industry

The following Python approach downloads ULP charges from the NLRB API using date-bounded pagination and computes disposition rates by case type and region. Because the NLRB API does not support a single full-history bulk export, the approach pages through fiscal year windows.

import requests
import pandas as pd
import time

BASE_URL = "https://www.nlrb.gov/cases-decisions/cases/list"

def fetch_ulp_cases(date_start: str, date_end: str, page: int = 1) -> dict:
    """
    Fetch one page of ULP cases from the NLRB case search API.
    date_start / date_end: 'YYYY-MM-DD' strings
    """
    params = {
        "case_type": "CA,CB,CD",   # CA=vs employer, CB=vs union, CD=vs both
        "date_start": date_start,
        "date_end": date_end,
        "page": page,
        "per_page": 100,
    }
    resp = requests.get(BASE_URL, params=params, timeout=30)
    resp.raise_for_status()
    return resp.json()

def download_year(fiscal_year: int) -> pd.DataFrame:
    """
    Download all ULP cases for a DOL fiscal year (Oct 1 - Sep 30).
    NLRB fiscal year matches the federal fiscal year.
    """
    start = str(fiscal_year - 1) + "-10-01"
    end   = str(fiscal_year) + "-09-30"

    page = 1
    records = []
    while True:
        data = fetch_ulp_cases(start, end, page)
        batch = data.get("results", [])
        if not batch:
            break
        records.extend(batch)
        total = data.get("count", 0)
        print("FY" + str(fiscal_year) + " page " + str(page) + ": " + str(len(records)) + " / " + str(total))
        if len(records) >= total:
            break
        page += 1
        time.sleep(0.5)   # be polite to the public API

    return pd.DataFrame(records)

# Download five fiscal years
frames = []
for fy in range(2020, 2025):
    df_year = download_year(fy)
    df_year["fiscal_year"] = fy
    frames.append(df_year)

df = pd.concat(frames, ignore_index=True)
print("Total cases downloaded:", len(df))

# --- Disposition rate analysis ---

# Normalize close_reason into broad outcome buckets
def bucket_disposition(reason: str) -> str:
    if pd.isna(reason):
        return "open"
    reason = str(reason).upper()
    if reason in ("RDISM", "ADISM", "CDISM"):
        return "dismissed"
    if reason in ("WITH",):
        return "withdrawn"
    if reason in ("SDISM", "SETTLE"):
        return "settled"
    if reason in ("ADJ", "COMPLY"):
        return "adjudicated"
    return "other"

df["outcome"] = df["close_reason"].apply(bucket_disposition)

# Disposition rate by fiscal year
pivot = (
    df.groupby(["fiscal_year", "outcome"])
    .size()
    .unstack(fill_value=0)
)
pivot["total"] = pivot.sum(axis=1)
for col in ["dismissed", "withdrawn", "settled", "adjudicated"]:
    if col in pivot.columns:
        pivot[col + "_pct"] = (pivot[col] / pivot["total"] * 100).round(1)

print(pivot[["total", "dismissed_pct", "withdrawn_pct", "settled_pct", "adjudicated_pct"]])

# Starbucks charge concentration: CA charges with 'STARBUCKS' in respondent name
starbucks = df[
    (df["case_type"] == "CA") &
    (df["respondent"].str.upper().str.contains("STARBUCKS", na=False))
]
print("\nStarbucks CA charges by fiscal year:")
print(starbucks.groupby("fiscal_year").size())

# Regional concentration: which regions received the most charges?
top_regions = (
    df.groupby("region")
    .size()
    .sort_values(ascending=False)
    .head(10)
)
print("\nTop 10 regions by ULP charge volume:")
print(top_regions)

A few notes on this approach. The NLRB API's respondent field is not normalized across records: a single employer may appear under its legal name, a doing-business-as name, or a location-specific variant. Starbucks alone appears under “Starbucks Corporation,” “Starbucks Coffee Company,” and store-specific names in different records. A production pipeline should normalize respondent names before aggregating. The NAICS industry field is inconsistently populated in NLRB data — a meaningful share of records lack it — so industry-level analysis requires supplementing with external employer data matched on name and geography.

Using the NLRB case search API directly

For targeted research — tracking a specific employer or monitoring new charges in a specific region — the NLRB case search web interface at nlrb.gov is often faster than API pagination. The interface supports filtering by respondent name, charge party, union name, region, case type, date range, and case status (open or closed). Results are exportable to CSV directly from the interface for result sets up to several thousand records.

For programmatic monitoring, the API supports an ordering parameter on date_filed that enables a “new filings since last check” pattern: store the most recent date_filed seen in the previous run, then query for cases filed after that date. This approach works reliably for employer-specific monitoring because the charge filing date is publicly available and the API does not require authentication. Journalist teams covering major organizing campaigns have used this pattern to receive near-real-time alerts when new charges are filed against a specific employer.

One structural limitation: the NLRB case search system does not expose ALJ decision text or Board decision text through the case search API. Decisions are published as PDFs in the Board Decisions section of nlrb.gov and are separately searchable by case number. The case metadata in the API indicates whether a case reached the ALJ or Board stage through the close_reason field, but the substantive legal analysis requires accessing the decision documents separately.

Significance for journalism and accountability research

The NLRB ULP database offers three capabilities that are not available in any other federal enforcement dataset covering labor relations.

First, it documents employer response to organizingin near-real-time. An employer who fires workers who sign union authorization cards will typically face an 8(a)(3) charge within days or weeks of the discharge. That charge appears in the public NLRB database as an open case long before any investigation concludes, any complaint issues, or any remedy is imposed. The charge data is therefore a leading indicator of labor conflict that precedes and predicts later enforcement outcomes.

Second, the database captures the geographic spreadof employer anti-union conduct across multiple locations. A national employer with dozens or hundreds of locations will face ULP charges in multiple regions simultaneously during a major organizing drive. The multi-region charge pattern is visible in the data as a distribution of charges across regional offices within a short date window — a signal that no single regional investigation can capture but that the aggregate database makes apparent.

Third, cross-referencing NLRB ULP data against other federal enforcement records reveals coordinated labor conflict at specific employers. An employer appearing simultaneously in NLRB ULP charges, DOL Wage and Hour Division enforcement records, and OSHA inspection citations is facing overlapping regulatory pressure across multiple labor law domains — a pattern that may reflect systemic labor relations failures rather than isolated violations. The employer name and address fields common to all three datasets enable approximate matching without any non-public data.

For journalists, the most actionable use of the database is the combination of charge volume monitoring and cross-referencing. A company that is simultaneously the subject of 50 ULP charges across 15 regions, has active WHD enforcement cases for wage violations, and has received OSHA citations at multiple locations represents a story that no single agency dataset reveals on its own — but that the combination of public federal enforcement records makes fully documentable.


For DOL Wage and Hour Division enforcement data that cross-references with NLRB retaliation charges: Wage theft by employer: using DOL Wage and Hour Division enforcement data to find labor violations →

For OSHA inspection and citation data covering workplace safety at the same employer sites: Workplace safety violations: using OSHA inspection and citation data to find dangerous employers →

For EPA enforcement data and cross-agency accountability research methods: EPA enforcement data: how to track pollution violations and penalty collection →