Technical writing

FDIC Institution Database: The Federal Profile of Every FDIC-Insured Bank and Thrift

· AI Analytics
FDICBankingBank DataFinancial InstitutionsFederal Data

The Federal Deposit Insurance Corporation maintains a public registry of every institution it has ever insured—approximately 4,600 currently active banks and thrifts and more than 10,000 historical institutions going back to the program's founding in 1934. Through the BankFind Suite at banks.data.fdic.gov, this registry is available as a free, queryable API exposing each institution's charter type, regulatory supervisor, asset size, branch network, holding company affiliation, and establishment date. For researchers studying banking consolidation, community development finance, regulatory arbitrage across the dual banking system, or geographic access to financial services, the FDIC institution database is the authoritative starting point.

What the FDIC institution database is

The FDIC BankFind Suite (banks.data.fdic.gov) is distinct from the quarterly financial data reported by banks on the FFIEC Call Report (FFIEC 031 for banks with domestic and foreign offices, FFIEC 041 for domestic-only institutions). The Call Report is a balance-sheet and income-statement filing that generates highly detailed financial data on loans, deposits, capital adequacy, and asset quality. The institution database, by contrast, covers bank characteristics—the structural facts about each institution that change slowly or not at all: where it is chartered, who supervises it, when it was established, how many branches it operates, and what holding company controls it.

The institution database covers the full historical universe of FDIC-insured entities. When a bank fails, merges, or voluntarily surrenders its charter, its record is not deleted—it is marked inactive, with a recorded termination date, a reason code (voluntary liquidation, merger, failure), and in the case of failures, an acquiring institution. The history endpoint of the BankFind API exposes this complete transaction history: every failure, every assisted merger, every voluntary combination since 1934. This historical depth makes the FDIC database the definitive longitudinal record of the American banking system across nine decades of structural change, including the Great Depression bank runs that motivated deposit insurance, the savings-and-loan crisis of the 1980s, and the 2008 financial crisis.

The dataset covers commercial banks, savings banks, savings associations (thrifts), and cooperative banks. Credit unions are explicitly excluded: they are federally insured not by the FDIC but by the National Credit Union Administration (NCUA) under the National Credit Union Share Insurance Fund (NCUSIF) and are tracked in the NCUA's own call report system. Industrial loan companies (ILCs) chartered in Utah, Nevada, and a few other states are FDIC-insured and appear in the database, as do a small number of non-depository trust companies that hold FDIC insurance. The universe is effectively every institution in the United States that accepts federally insured deposits.

Institution characteristics: what each record contains

Each institution record in the BankFind API carries a standardized set of fields covering identity, geography, charter structure, and financial scale. The certificate number (field: CERT) is the FDIC's own unique numeric identifier for the institution, assigned when the institution first receives FDIC insurance and never reused. The certificate number is the primary join key when linking institution records to the Call Report financial data and to the Summary of Deposits branch-level data.

Geographic fields include the institution's main office city, state (both full name and two-letter abbreviation), county, zip code, and physical address. The BankFind API also returns latitude and longitude for the main office, enabling GIS analysis of banking access. For multi-state bank holding companies with branches across many states, the main office location reflects the legal domicile of the institution rather than where most of its business is conducted: JPMorgan Chase Bank is legally domiciled in Ohio, and its main office entry in the FDIC database reflects Columbus despite the bank operating coast to coast.

The established date (ESTYMD) records when the institution first opened for business—not when it received FDIC insurance, which may have come later. The FDIC insured date (INSDATE) records the specific date insurance coverage began. For the oldest continuously operating institutions, established dates reach into the nineteenth century: BNY Mellon traces its lineage to the Bank of New York, founded by Alexander Hamilton in 1784, though the current legal entity was established much later through mergers.

The class code (CLASSP) encodes the institution's charter type, which determines its primary federal supervisor. The main codes in use are: N for national banks, chartered by the Office of the Comptroller of the Currency (OCC) under the National Bank Act; SM for state member banks, chartered by state banking authorities but members of the Federal Reserve System and supervised by the Federal Reserve; NM for state nonmember banks, chartered by states and supervised directly by the FDIC; SB for savings banks, typically state-chartered mutual or stock institutions; SA for savings associations (federal thrifts) chartered by the OCC under the Home Owners' Loan Act; and OI for other insured institutions, a residual category covering cooperative banks and a handful of atypical charter forms.

Financial scale fields pulled from the most recent Call Report include total assets (ASSET, in thousands of dollars), total deposits (DEP), total loans (LNLSNET), equity capital (EQ), return on assets (ROA), return on equity (ROE), net interest margin (NETINC), nonperforming loan ratio, and tier 1 capital ratio. These enable quick bank screening without pulling the full Call Report data—useful when the goal is to identify institutions above or below a given asset threshold or those with elevated nonperforming loan ratios suggesting credit stress. The RSSD ID field records the Federal Reserve's own unique identifier for the institution, used across Federal Reserve data systems including the National Information Center (NIC) holding company database, enabling cross-system joins. CAMELS ratings—the composite supervisory assessment of Capital, Assets, Management, Earnings, Liquidity, and Sensitivity to market risk—are confidential and not included in any public FDIC data.

The dual banking system: charter types and regulatory arbitrage

The United States operates a dual banking system in which commercial banks may be chartered either by the federal government (as national banks, through the OCC) or by state banking authorities (as state banks, through each state's department of financial institutions or equivalent). This system dates to the National Bank Act of 1863, passed during the Civil War to create a nationally uniform currency backed by federal bonds and to finance the Union war effort. State banks predated the National Bank Act and continued operating under state charters, setting up the competing federal and state jurisdictions that persist today.

National banks (class code N) receive their charter from the OCC and must join the Federal Reserve System. They are supervised by the OCC for safety and soundness and by the Federal Reserve for their holding company relationships. National bank charters carry the statutory preemption authority of the National Bank Act, meaning that in many cases federal law preempts conflicting state consumer protection regulations—a longstanding source of tension between federal banking regulators and state attorneys general.

State member banks (SM) are chartered by states and have voluntarily joined the Federal Reserve System, making them subject to Fed supervision. State nonmember banks (NM) are chartered by states but have not joined the Federal Reserve System; they are supervised directly by the FDIC for safety and soundness. State banks must comply with both state banking law and federal requirements, and they cannot rely on the same preemption authority available to national banks, though the Supreme Court has moderated the preemption gap in recent decisions.

The thrift charter (savings associations and savings banks) has its own parallel dual structure. Federal savings associations (SA) are chartered by the OCC under the Home Owners' Loan Act of 1933 (HOLA), originally designed to fund residential mortgage lending during the Depression. State-chartered savings banks (SB) are supervised by state authorities and the FDIC. The Office of Thrift Supervision (OTS), which formerly supervised federal thrifts, was abolished by the Dodd-Frank Act of 2010 and its functions transferred to the OCC (for federal thrifts) and the Federal Reserve and FDIC (for state thrifts).

The dual banking system creates regulatory arbitrage opportunities: banks can choose their chartering authority based on which regulatory framework is more favorable for their business model. Historically, banks have converted between federal and state charters in response to changes in supervisory policy, capital requirements, or preemption rules. A bank uncomfortable with OCC supervisory priorities can convert to a state charter; a state bank seeking preemption of state usury limits can convert to a national charter. The FDIC database's history endpoint records these charter conversions, enabling researchers to trace the regulatory migration patterns of individual institutions over time.

Banking concentration: from 14,000 banks in 1984 to 4,600 today

The most dramatic trend visible in the FDIC institution database is the consolidation of the American banking system over the past four decades. In 1984, the FDIC database shows approximately 14,000 FDIC-insured commercial banks operating in the United States. By 2024, that count had fallen to approximately 4,600—a 67 percent decline driven by two overlapping forces: merger and acquisition activity during periods of deregulation and economic expansion, and waves of bank failures during financial crises.

The savings-and-loan crisis of the late 1980s and early 1990s was the first major failure wave. More than 1,000 savings institutions failed between 1986 and 1995, resolved at a cost exceeding $130 billion to taxpayers through the Resolution Trust Corporation (RTC). The commercial banking sector experienced its own significant failure wave during the same period, with more than 1,600 commercial bank failures between 1980 and 1994 according to FDIC historical data. The FDIC history endpoint records each failure with the acquiring institution, the estimated resolution cost, and the transaction type (purchase-and-assumption vs. deposit payoff), making the full record of each crisis episode available for quantitative analysis.

The 2008–2012 failure wave produced approximately 500 bank failures, concentrated heavily in states with significant residential real estate exposure—Georgia, Florida, California, Illinois, and Minnesota. Washington Mutual, seized by the Office of Thrift Supervision and sold to JPMorgan Chase in September 2008, was the largest bank failure in American history by assets at the time, with approximately $307 billion in assets. IndyMac, seized in July 2008, was the second largest. The FDIC managed the resolutions through purchase-and-assumption transactions in which surviving banks acquired the deposits and selected assets of failed institutions, limiting direct depositor losses.

Between crises, consolidation proceeded through mergers driven by interstate banking deregulation. The McFadden Act of 1927 had prohibited national banks from branching across state lines, confining them to the branching rules of their home state. The Riegle-Neal Interstate Banking and Branching Efficiency Act of 1994 repealed these restrictions, and the subsequent wave of interstate mergers transformed regional banks into national megabanks. BankAmerica merged with NationsBank to form Bank of America in 1998. First Union acquired Wachovia, which was itself later acquired by Wells Fargo during the 2008 crisis. Chemical Bank merged with Chase Manhattan, which merged with J.P. Morgan to form JPMorgan Chase. Each of these transactions removed multiple large institutions from the FDIC database as separate certificates and consolidated their certificate numbers under the surviving charter.

The result of this four-decade consolidation is extraordinary asset concentration. The four largest US banks by assets—JPMorgan Chase, Bank of America, Wells Fargo, and Citigroup—together hold approximately 45 percent of all US banking assets. Community banks, defined by the FDIC as institutions with under $1 billion in total assets, still account for approximately 4,000 of the 4,600 active institutions— roughly 87 percent of all institutions by count—but hold only about 12 to 14 percent of all US banking assets. The gap between institution count and asset share is the statistical signature of banking consolidation: many small institutions, a handful of dominant ones.

The Summary of Deposits: branch-level geographic data

The institution-level database covers each bank's main office, but banks operate branch networks extending across counties, metro areas, and in the case of the largest institutions, the entire country. The FDIC Summary of Deposits (SOD) is the companion dataset that provides branch-level deposit and location data for every FDIC-insured institution. The SOD is an annual survey conducted as of June 30 of each year, published approximately six months later, covering every branch office of every FDIC-insured institution in the United States and its territories.

Each SOD record captures: the branch's physical address, county, state, and zip code; the branch's deposit total (the deposits held at that specific branch location); the FDIC certificate number of the institution operating the branch; whether the branch is the institution's main office or a satellite branch; and summary data on whether the branch office was opened, closed, or relocated during the prior year. The branch-level deposit data enables geographic analysis of where bank deposits are held in relation to where bank lending activity occurs— a central question for Community Reinvestment Act compliance analysis.

The BankFind API exposes SOD data through its /branches endpoint, supporting filtering by state, county, FDIC certificate number, and year. The full historical SOD going back to 1994 is available in bulk download format, enabling longitudinal analysis of branch network expansion and contraction. Between 2009 and 2022, the number of FDIC-insured bank branches in the United States fell from approximately 99,000 to approximately 77,000, a decline of roughly 22 percent, as digital banking reduced the economic justification for dense branch networks and consolidation eliminated overlapping branches in merger markets.

Banking deserts and the CRA framework

The geographic distribution of bank branches visible in the SOD data directly informs the analysis of banking deserts—areas where residents lack reasonable physical access to a bank branch or other mainstream financial institution. The FDIC defines a banking desert in rural areas as a census tract where the nearest bank branch is more than 10 miles from the population centroid, and in urban areas as a census tract where the nearest branch is more than one mile away. Approximately 4.5 percent of US census tracts qualify as banking deserts under one or both definitions, with higher concentrations in rural Great Plains states, the Mississippi Delta, and tribal lands in the Southwest and Northern Plains.

The FDIC's annual national survey of unbanked and underbanked households—most recently finding approximately 4.5 percent of US households, or about 5.9 million households, entirely unbanked as of its most recent edition—documents the population-level consequences of banking deserts. Unbanked households rely on check-cashing services, payday lenders, and money orders that collectively impose fees substantially higher than the cost of maintaining a bank account, a dynamic researchers have termed a “poverty premium” on financial services.

The Community Reinvestment Act of 1977 (CRA) requires FDIC-insured depository institutions to meet the credit needs of their entire assessment area, including low- and moderate-income communities, consistent with safe and sound operations. Banks are evaluated on their CRA performance by their primary federal supervisor (OCC for national banks, Federal Reserve for state member banks, FDIC for state nonmember banks) on a four-point scale: Outstanding, Satisfactory, Needs-to-Improve, and Substantial Noncompliance. CRA ratings are public records. A Needs-to-Improve or Substantial Noncompliance rating can delay or block regulatory approval of mergers and acquisitions, giving the CRA compliance framework practical leverage over bank growth strategies.

CRA assessment areas are defined by the bank itself, based on where its branches are located and where it makes most of its loans. Community advocates and researchers use SOD branch data together with HMDA (Home Mortgage Disclosure Act) loan origination data to evaluate whether banks' CRA assessment area definitions fairly reflect their actual lending footprints and whether their CRA lending activity is proportionate to the credit needs of lower-income communities within those areas. The 2023 CRA reform rules issued jointly by the OCC, FDIC, and Federal Reserve substantially revised the assessment area definition framework to account for digital lending, which increasingly allows banks to originate loans in communities far from any branch.

The BankFind Suite API

The FDIC BankFind Suite API at banks.data.fdic.gov/api is a free, no-authentication-required REST API organized around six primary endpoints. The /institutions endpoint serves the institution characteristics database described throughout this article: it accepts filter expressions in an RSQLITE-style syntax (filters=ACTIVE:1 AND STALP:TX), field selection (fields=CERT,NAME,ASSET,CLASSP), sort parameters (sort_by=ASSET&sort_order=DESC), and pagination via limit and offset. The API returns up to 10,000 records per request, sufficient to retrieve the full active institution universe in a single call with a high limit, though the typical maximum allowed by the API is 10,000 records.

The /history endpoint covers failed and acquired institutions: it returns records for every FDIC-insured institution that has undergone a class event—failure, assisted merger, charter conversion, voluntary liquidation—with the date of the event, the transaction type code, and the acquiring institution's certificate number where applicable. Filtering by FAILDATE date range enables retrieval of all failures within a specific period, such as the 2008–2012 crisis window.

The /branches endpoint exposes the Summary of Deposits branch-level data, supporting filtering by institution certificate, state, county, and year. The /financials endpoint returns quarterly financial metrics pulled from Call Reports, including the balance-sheet and income-statement ratios described earlier. The/summary endpoint serves aggregated financial statistics across multiple institutions—useful for computing aggregate asset totals by state or charter class without downloading every individual record. The /failures endpoint provides a dedicated view of failed institutions with resolution cost estimates.

The API supports content negotiation via the output parameter, returning data in JSON (default), CSV, or YAML. JSON responses wrap each record in a data object: {"data": [{"data": {...}}]}. Callers should account for this nesting when parsing responses. The API does not enforce rate limits in its documentation, but polite usage with small sleep intervals between paginated requests is advisable.

Financial metrics exposed via BankFind

Although the institution database is primarily about structural characteristics rather than financial performance, the BankFind API surfaces a set of key financial metrics from the most recent available Call Report data. These metrics enable rapid screening of the institution universe without pulling the full quarterly Call Report.

Total assets and total deposits are the foundational scale measures. The asset-size tiers used in regulatory practice reflect both supervisory burden allocation and product eligibility: community banks under $1 billion in assets qualify for simplified Call Report formats and reduced regulatory burden under the Economic Growth, Regulatory Relief, and Consumer Protection Act of 2018 (S.2155). Mid-size banks between $100 billion and $250 billion in assets are subject to enhanced prudential standards under the Federal Reserve's Category III and IV framework adopted after 2018. Banks exceeding $250 billion in assets face the most stringent Category I and II requirements, including annual stress testing, resolution planning, and enhanced liquidity standards.

The net interest margin (NIM), return on assets (ROA), and return on equity (ROE) available through BankFind provide a quick read on profitability trends across the institution universe. The 2022–2024 rate cycle, in which the Federal Reserve raised the federal funds rate from near zero to over five percent, had complex and asymmetric effects on NIM across institution sizes: larger banks with significant variable-rate loan portfolios and low-cost deposit franchises saw NIM expansion, while smaller institutions with fixed-rate mortgage portfolios saw compressed margins as funding costs rose faster than asset yields. The nonperforming loan ratio and tier 1 capital ratio provide credit quality and capital adequacy indicators, enabling identification of institutions with elevated stress signals for more detailed Call Report analysis.

Python example: analyzing active institutions by state and asset tier

The following script uses the FDIC BankFind API to retrieve all currently active FDIC-insured institutions with their asset size, state, and charter class. It classifies each institution into one of three asset tiers (community under $1 billion, regional $1–50 billion, large over $50 billion), computes the distribution by state and tier, identifies states with the highest community-bank concentration, summarizes the charter class distribution, and plots a stacked bar chart of the top-20 states by total institution count. The script requires onlyrequests, pandas, and matplotlib. No API key is required.

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

# ---------------------------------------------------------------------------
# FDIC BankFind Suite API
# Pull all currently active FDIC-insured institutions, classify by asset tier
# and state, and plot the top-20 states by institution count.
# Docs: https://banks.data.fdic.gov/docs/
# No API key required.
# ---------------------------------------------------------------------------

BASE_URL = "https://banks.data.fdic.gov/api/institutions"

FIELDS = [
    "CERT",        # FDIC certificate number (unique ID)
    "NAME",        # Institution name
    "STNAME",      # State name (full)
    "STALP",       # State abbreviation
    "CITY",        # City
    "CLASSP",      # Charter class code (N, SM, NM, SB, SA, OI ...)
    "ASSET",       # Total assets (thousands of dollars)
    "ESTYMD",      # Established date (YYYY-MM-DD)
    "ACTIVE",      # 1 = currently active
    "SPECGRP",     # Asset concentration specialization group
    "HCTMULT",     # Holding company indicator
]


def fetch_all_active_institutions() -> pd.DataFrame:
    """
    Page through the FDIC /institutions endpoint to retrieve all active
    institutions. The API caps results at 10,000 per request; use offset
    pagination to handle the full universe (~4,600 as of 2024).
    """
    params = {
        "filters": "ACTIVE:1",
        "fields": ",".join(FIELDS),
        "limit": 5000,
        "offset": 0,
        "sort_by": "ASSET",
        "sort_order": "DESC",
        "output": "json",
    }

    all_rows: list[dict] = []
    page = 0
    while True:
        params["offset"] = page * params["limit"]
        resp = requests.get(BASE_URL, params=params, timeout=30)
        resp.raise_for_status()
        payload = resp.json()
        data = payload.get("data", [])
        if not data:
            break
        # Each record is wrapped in {"data": {...}}
        rows = [rec["data"] for rec in data]
        all_rows.extend(rows)
        print(f"  Page {page + 1}: fetched {len(rows):,} records "
              f"(total so far: {len(all_rows):,})")
        if len(rows) < params["limit"]:
            break   # last page
        page += 1
        time.sleep(0.2)  # be polite

    df = pd.DataFrame(all_rows)
    return df


def classify_asset_tier(asset_thousands: float) -> str:
    """
    Classify an institution into one of three asset tiers.
      Community  : total assets < $1 billion  (<$1,000,000 thousand)
      Regional   : $1B – $50B
      Large      : > $50 billion
    """
    if pd.isna(asset_thousands):
        return "Unknown"
    b = asset_thousands / 1_000  # convert thousands → millions for readability
    if b < 1_000:        # < $1B
        return "Community (<$1B)"
    elif b < 50_000:     # $1B–$50B
        return "Regional ($1–50B)"
    else:                # > $50B
        return "Large (>$50B)"


def main() -> None:
    print("Fetching all active FDIC-insured institutions...")
    df = fetch_all_active_institutions()
    print(f"\nTotal active institutions retrieved: {len(df):,}\n")

    # Coerce numeric columns
    df["ASSET"] = pd.to_numeric(df["ASSET"], errors="coerce")

    # Classify by asset tier
    df["tier"] = df["ASSET"].apply(classify_asset_tier)

    # ------------------------------------------------------------------
    # 1. National summary by asset tier
    # ------------------------------------------------------------------
    tier_summary = (
        df.groupby("tier")
        .agg(
            institutions=("CERT", "count"),
            total_assets_B=("ASSET", lambda x: x.sum() / 1_000_000),  # → billions
        )
        .reindex(["Community (<$1B)", "Regional ($1–50B)", "Large (>$50B)", "Unknown"])
        .dropna(how="all")
    )
    total = tier_summary["institutions"].sum()
    tier_summary["pct_count"] = tier_summary["institutions"] / total * 100
    tier_summary["pct_assets"] = (
        tier_summary["total_assets_B"] / tier_summary["total_assets_B"].sum() * 100
    )

    print("National breakdown by asset tier:")
    print(f"  {'Tier':<22} {'Institutions':>14}  {'% Count':>8}  {'Assets ($B)':>13}  {'% Assets':>9}")
    print("  " + "-" * 76)
    for tier, row in tier_summary.iterrows():
        print(
            f"  {tier:<22} {int(row['institutions']):>14,}  "
            f"{row['pct_count']:>7.1f}%  "
            f"{row['total_assets_B']:>12.1f}  "
            f"{row['pct_assets']:>8.1f}%"
        )
    print()

    # ------------------------------------------------------------------
    # 2. Active institutions by state (all tiers)
    # ------------------------------------------------------------------
    by_state = (
        df.groupby("STNAME")
        .agg(
            total=("CERT", "count"),
            community=("tier", lambda x: (x == "Community (<$1B)").sum()),
            regional=("tier", lambda x: (x == "Regional ($1–50B)").sum()),
            large=("tier", lambda x: (x == "Large (>$50B)").sum()),
        )
        .sort_values("total", ascending=False)
        .reset_index()
    )
    by_state["community_pct"] = by_state["community"] / by_state["total"] * 100

    print("Top-20 states by total FDIC-insured institutions:")
    print(f"  {'State':<26} {'Total':>7}  {'Community':>10}  {'Regional':>9}  {'Large':>7}  {'Comm%':>7}")
    print("  " + "-" * 74)
    for _, row in by_state.head(20).iterrows():
        print(
            f"  {row['STNAME']:<26} {int(row['total']):>7,}  "
            f"{int(row['community']):>10,}  "
            f"{int(row['regional']):>9,}  "
            f"{int(row['large']):>7,}  "
            f"{row['community_pct']:>6.1f}%"
        )
    print()

    # ------------------------------------------------------------------
    # 3. States with highest community-bank concentration (>= 20 institutions)
    # ------------------------------------------------------------------
    top_community = (
        by_state[by_state["total"] >= 20]
        .sort_values("community_pct", ascending=False)
        .head(10)
    )
    print("States with highest community-bank share (min 20 institutions):")
    print(f"  {'State':<26} {'Community%':>11}  {'Total':>7}  {'Community':>10}")
    print("  " + "-" * 60)
    for _, row in top_community.iterrows():
        print(
            f"  {row['STNAME']:<26} {row['community_pct']:>10.1f}%  "
            f"{int(row['total']):>7,}  {int(row['community']):>10,}"
        )
    print()

    # ------------------------------------------------------------------
    # 4. Charter class distribution
    # ------------------------------------------------------------------
    class_labels = {
        "N":  "National bank (OCC)",
        "SM": "State member bank (Fed)",
        "NM": "State nonmember bank (FDIC)",
        "SB": "Savings bank (state)",
        "SA": "Savings association (federal/OCC)",
        "OI": "Other insured",
    }
    charter_counts = (
        df["CLASSP"]
        .map(lambda c: class_labels.get(str(c).strip(), str(c).strip()) if pd.notna(c) else "Unknown")
        .value_counts()
        .reset_index()
    )
    charter_counts.columns = ["charter_type", "count"]
    charter_counts["pct"] = charter_counts["count"] / charter_counts["count"].sum() * 100
    print("Active institutions by charter class:")
    print(f"  {'Charter Type':<40} {'Count':>7}  {'Pct':>7}")
    print("  " + "-" * 60)
    for _, row in charter_counts.iterrows():
        print(f"  {row['charter_type']:<40} {int(row['count']):>7,}  {row['pct']:>6.1f}%")
    print()

    # ------------------------------------------------------------------
    # 5. Bar chart: top-20 states by total institution count
    # ------------------------------------------------------------------
    top20 = by_state.head(20)
    fig, ax = plt.subplots(figsize=(13, 6))

    x = range(len(top20))
    width = 0.65
    community_vals = top20["community"].values
    regional_vals  = top20["regional"].values
    large_vals     = top20["large"].values

    bars_c = ax.bar(x, community_vals, width, label="Community (<$1B)", color="#4878d0")
    bars_r = ax.bar(x, regional_vals,  width, bottom=community_vals,
                    label="Regional ($1–50B)", color="#ee854a")
    bars_l = ax.bar(x, large_vals,     width,
                    bottom=community_vals + regional_vals,
                    label="Large (>$50B)", color="#6acc65")

    ax.set_xticks(list(x))
    ax.set_xticklabels(top20["STNAME"], rotation=45, ha="right", fontsize=9)
    ax.set_ylabel("Number of FDIC-Insured Institutions")
    ax.set_title(
        "Top-20 States by Total FDIC-Insured Institutions\n"
        "(Active institutions, stacked by asset-size tier)",
        fontsize=12,
        pad=14,
    )
    ax.legend(loc="upper right")
    ax.yaxis.grid(True, linestyle="--", alpha=0.5)
    ax.set_axisbelow(True)

    plt.tight_layout()
    plt.savefig("fdic_institutions_by_state.png", dpi=150)
    print("Bar chart saved to fdic_institutions_by_state.png")


if __name__ == "__main__":
    main()

Running this script against the live FDIC API will return approximately 4,500 to 4,700 active institutions depending on the date of execution. Texas typically leads the national count with 400 to 500 active institutions, reflecting both its large population and its historical unit-banking culture that maintained many independent community banks even after interstate branching deregulation. Illinois, Missouri, Minnesota, and Kansas typically follow, states with similarly deep traditions of independent community banking rooted in their agricultural economies. The community-bank concentration analysis will show agricultural Midwest states—North Dakota, Nebraska, Iowa— with community banks representing upwards of 95 to 98 percent of all active institutions, a structural pattern that reflects the persistence of farm-oriented local banks in communities too small to attract regional or national competitors. The charter class breakdown will typically show state nonmember banks (NM) as the most numerous class, followed by national banks (N), with savings associations (SA) and savings banks (SB) representing a declining share as the thrift sector continues its post–S&L-crisis contraction.

For the EPA facility-level emissions database that tracks environmental compliance across heavy industry—including the same Subpart W petroleum and natural gas methodology controversies that affect banks' energy-sector loan portfolios under climate transition risk frameworks—see EPA Greenhouse Gas Reporting Program: The Facility-Level Emissions Database Behind US Climate Accountability, published 2026-11-22, covering FLIGHT, ECHO bulk download, the methane satellite validation controversy, and Python analysis of facility-level GHG data.

For the DOJ Antitrust Division's public enforcement database covering merger review, HSR pre-merger notifications, and the HHI concentration thresholds that govern whether banking mergers receive Phase 1 or Phase 2 review from the Justice Department—including the specific analytical framework applied to bank merger competitive effects analysis—see DOJ Antitrust Division: The Federal Merger Review and Cartel Enforcement Database, published 2026-11-21, covering the HSR process, leniency program, and Python scraping of DOJ enforcement press releases.