FDIC Bank Branch Openings, Closings & Market Exit Rollup
Pricing
from $8.25 / 1,000 results
FDIC Bank Branch Openings, Closings & Market Exit Rollup
Net FDIC branch churn by bank, county and quarter: openings, closings, purchases, acquisitions, divestitures and relocations, with metro area and branch-desert flags, plus the verified standing branch count today from FDIC's own branch list, so a market exit is confirmed rather than inferred.
Pricing
from $8.25 / 1,000 results
Rating
0.0
(0)
Developer
Kyle Maloney
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
FDIC Bank Branch Network Churn & Market Exit Rollup
Every branch a US bank opened, closed, bought, sold, inherited in a merger or relocated, rolled into a bank x county x quarter table — with net change, the verified standing branch count today, confirmed market entry and exit, metro/micropolitan classification, and a branch-desert signal for counties that have been thinning for several quarters running.
Source: the FDIC public API (api.fdic.gov) — /banks/history, /banks/locations and
/banks/institutions. No key, no scraping, no login. FDIC refreshes the data daily; the actor
refuses to run against a stale index.
Who this is for
Branch strategy and network planning. You need to know where the incumbent footprint is
shrinking before you commit a lease. Filter to is_branch_desert_signal = true and sort by
county_offices_current to find counties where deposits are stranded but the competition has left.
net_change_incl_relocations is the honest county figure — a bank that "closed" a branch and opened
one two miles away across the county line did not remove a branch from the market.
CRA and fair-banking analysts. Branch closures in low- and moderate-income counties are an
examination topic. county_fips joins straight to Census, HMDA and FFIEC data;
is_market_exit_confirmed tells you which banks genuinely left a county rather than merely posting
a bookkeeping event. cbsa_type splits metropolitan from micropolitan and rural.
Commercial real estate. Bank branches are prime retail corners on long leases. A confirmed
market exit is a pipeline of second-generation retail space with parking, a drive-through and a
vault. is_market_exit_confirmed = true plus closed >= 1 is the shortlist.
Fintech and de-novo market entry. is_market_entry_confirmed shows which charters are actively
building in a market and which are harvesting it. county_bank_count against
county_offices_current shows concentration, and holding_company_name collapses subsidiaries up
to the parent so three brands owned by one company do not read as three competitors.
Bank M&A and competitive intelligence. acquired_in_merger and purchased show inorganic
growth by county; successor_cert and inst_active follow a charter through its absorption; and in
events mode acquirer_name / seller_name name both sides of a branch transfer.
What makes the numbers trustworthy
The office-count correction (read this before using offices_end)
FDIC's branch-event stream is not a closed ledger, so a running balance over it is not a standing office count. When a charter is absorbed the acquirer books a "Branch Acquired in Merger" event for each branch — but the disappearing charter books nothing. Change code 713 is 202,213 of the 366,989 branch events in the dataset (55%), so that omission dominates:
Re-measured live on 2026-07-28 (FDIC index locations_20260724090007) by running this actor:
| Net-event balance | FDIC standing branches, same series | Series reconciling | ||
|---|---|---|---|---|
| Texas | 14,816 | 5,982 | overstated 148% | 2,285 of 5,804 (39.4%) |
| Vermont | 353 | 205 | overstated 72% | 87 of 188 (46.3%) |
The right-hand branch count is the matched-series figure — the standing branches that sit in a
bank x county series the event stream also covers, which is the only apples-to-apples denominator
for a replay. Do not read it as the size of the state. Texas as a whole is 6,414 offices on
/banks/locations (347 main + 6,067 branches); Vermont is 219 (12 main + 207 branches).
Concretely: Chittenden Trust Company (CERT 14149) ceased to exist on 2009-01-01, and its net-event balance still reads 32 offices in Vermont.
This actor therefore ships both numbers and tells you which is which:
offices_start/offices_end— the cumulative net-event balance, unchanged from v1.0 so existing pipelines keep working.office_balance_basisrecords how it was built.offices_current— the authoritative standing branch count for that bank in that county today, straight from FDIC's own branch list, excluding the main office. This is the number to act on.reconciles_to_locations/reconciliation_delta— the gap between the two, on every row.is_market_exit_confirmed— asserted only when the bank's last event quarter in the county coincides with zero standing branches there today.is_market_exitalone can fire on a bookkeeping artefact.
Relocations are counted, on both endpoints
Change code 520 is the only relocation code in the entire FDIC dataset (verified by an exhaustive
sweep of codes 0-999) and it is 100% branch-level. A single 520 row carries both the origin and
the destination county. Because the natural state filter is the destination, a branch relocating
out of your state is invisible unless the origin field is queried too — this actor queries both.
In Texas 101 relocations crossed a county line and 9 crossed a state line; those are real branch
losses for the origin county that a destination-only query never sees. Relocations never touch
net_change (they are neither an opening nor a closing); they land in relocated_in,
relocated_out, relocated_within_county and net_change_incl_relocations.
The branch state, not the charter state
FDIC publishes both PSTALP (where the bank is chartered) and OFF_PSTALP (where the branch
physically is). For Texas branch closings they differ 4,592 vs 2,412, and 19.7% of the
charter-state rows sit outside Texas — a real Wells Fargo row is chartered in SD with a branch in
TN. This actor uses the branch state everywhere geography matters, and asserts it at runtime.
Nothing is silently discarded
3.09% of FDIC's national branch-event stream (6.3% in Vermont) carries no CERT. Those rows used to
be dropped on the floor. They are the per-branch legs of a purchase, and they cannot be folded into
a bank's balance without double-counting — FDIC publishes a CERT-bearing row for the same branch on
the same day. So they are surfaced instead: reported in events mode with cert: null and
cert_attributed: false, resolved to acquirer_name and seller_name, and counted on every row as
county_events_unattributed.
Five live assertions, checked on every run
An offline test cannot see a silently degraded upstream, because the fixture was shaped from the degraded data. These run against the real response and fail the run rather than bill you for a confident wrong answer:
- The filter FDIC echoes back must exactly equal the filter that was sent. (FDIC has silently dropped an unparseable filter and returned the entire 583,491-row national dataset.)
- Every returned record must actually be in the requested states and change codes — a data-level check that does not take FDIC's word for it.
- A one-state query reporting national-scale row counts is refused.
- The FDIC search index must have been rebuilt within 45 days.
- The reconciliation between the event replay and the standing branch list must stay inside its
measured band. A rate of zero means the join key broke and every
offices_currentwould be wrong.
And a gate that does not run says so on the row. Every one of the five can legitimately skip —
FDIC serving a response with no index metadata, a page with no records, a 51-state sweep,
reconcileWithLocations turned off, a state too small for the reconciliation rate to mean
anything. A skip is not a bug; a silent skip is, because a check that quietly no-ops is not a
check. So each gate stamps gate_<name>_status (ok / skipped / not_run) on every row,
alongside drift_gates_ok, drift_gates_skipped and drift_gates_skipped_reason, and logs a
DRIFT GATE SKIPPED warning naming the reason. If none of the five could run, the actor fails
and emits nothing — rows nothing verified are not worth billing for.
Per-source honesty
/banks/history is required — if it cannot answer the run fails and nothing is emitted or
billed. The other three sources are enrichment: if one is down the run continues, every field
derived from it is null (never false, never 0), and the row says so in locations_status,
institutions_status, relocations_status and source_notes.
offices_current: 0 means FDIC lists no branch. offices_current: null means we did not check.
Example input
{"mode": "churn","states": ["TX"],"sinceDate": "2024-01-01","maxResults": 500}
A branch-desert sweep across a whole region:
{"mode": "county","states": ["MS", "AL", "AR"],"quarters": 12,"thinningQuarters": 3,"maxResults": 2000}
Every branch event and relocation in two counties, with acquirer attribution:
{"mode": "events","states": ["TX"],"counties": ["HARRIS", "DALLAS"],"sinceDate": "2023-01-01","maxResults": 5000}
Input fields
| Field | Type | Notes |
|---|---|---|
states | array | Two-letter USPS codes, max 10 per run. Filters on the branch state. Defaults to ["TX"], so a bare {} is a valid Texas run rather than an error; an explicitly empty list is rejected. |
mode | string | churn (bank x county x quarter, the default), county, bank, or events. |
sinceDate / untilDate | string | YYYY-MM-DD. Bounds the output window; the full history is still replayed behind it. |
quarters | integer | Alternative to sinceDate — the N most recent quarters. |
counties | array | Optional county-name filter, e.g. ["HARRIS","DALLAS"]. |
thinningQuarters | integer | Consecutive negative county quarters required for is_branch_desert_signal. Default 3. |
maxResults | integer | Row cap, and the thing you are billed for. Default 500, the same as the prefill. Raise it deliberately — a full-state sweep runs to tens of thousands of rows. |
replayFullHistory | boolean | Default true. Off = a faster window-only run with a window-relative balance. |
reconcileWithLocations | boolean | Default true. Off = offices_current, MSA and reconciliation fields come back null, and gate_reconciliation_status reads skipped. |
includeRelocations | boolean | Default true. Off = relocation fields come back null. |
On states and required: states deliberately carries a default and is deliberately not
in the schema's required list. Apify applies a property default before it validates required,
so listing it there could never actually fire — a bare {} silently became a Texas run while the
schema advertised a 400. Two honest options existed: drop the default so the guard bites, or drop
the guard. The default stays, because a well-formed Texas answer is a legitimate result and Apify's
automated quality checks re-run this actor on a {}-shaped input every few days and expect it to
succeed. A genuinely empty "states": [] is still rejected loudly at runtime, before anything is
fetched or billed.
Output fields (churn mode)
| Field | Type | Meaning |
|---|---|---|
period, period_start, period_end | string | Calendar quarter and its bounds. |
state, county, county_fips | string | Branch geography. county_fips is the 5-digit Census FIPS. |
cbsa_code, cbsa_name, cbsa_type, is_metro | mixed | Metro / micropolitan area for the county. cbsa_type is metropolitan, micropolitan or none. |
cert, inst_name | mixed | FDIC certificate number and institution name. |
inst_active, inst_end_date, successor_cert | mixed | Charter status. inst_active: false with a successor_cert means the bank was absorbed. |
inst_total_offices | integer | The institution's nationwide office count. |
holding_company_rssd, holding_company_name | string | Top holding company (Federal Reserve RSSD). |
opened, purchased, acquired_in_merger | integer | Inbound events (FDIC codes 711, 712, 713). |
closed, sold, divested | integer | Outbound events (721, 722, 724). |
inbound_total, outbound_total, net_change | integer | Exact event arithmetic. net_change is the headline metric. |
offices_start, offices_end | integer | Cumulative net-event balance — see the correction above. |
office_balance_basis | string | How that balance was built. |
offices_current | integer | Verified standing branches today, excluding the main office. |
offices_current_incl_main, offices_current_asof | mixed | Inclusive count and the FDIC snapshot date. |
reconciles_to_locations, reconciliation_delta | mixed | Balance vs the standing list. |
is_market_entry, is_market_exit | boolean | Balance-based flags, kept from v1.0. |
is_market_entry_confirmed, is_market_exit_confirmed | boolean | Cross-checked against the standing branch list. Use these. |
is_first_event_quarter, is_final_event_quarter | boolean | Position of this quarter in the bank's activity in this county. |
office_count_estimated | boolean | The balance was clamped at zero. net_change is still exact. |
relocated_in, relocated_out, relocated_within_county | integer | Change-code-520 moves, both endpoints. |
net_change_incl_relocations | integer | The true county-level change. |
churn_rate_pct | number | Quarter events as a share of the bank's standing branches in the county. |
county_total_net, county_bank_count | integer | County-wide net change and active banks that quarter. |
county_offices_current, county_net_change_rate_pct, county_churn_events | mixed | Size and intensity of the county banking market. |
county_events_unattributed | integer | Events FDIC published without a CERT. |
county_consecutive_negative_quarters, county_is_thinning, is_branch_desert_signal | mixed | Thinning streak and the desert flag. |
history_status, locations_status, institutions_status, relocations_status | string | ok / partial / unavailable / skipped per source. |
gate_filter_echo_status | string | Did the "FDIC echoed back exactly the filter we sent" gate run? ok / skipped / not_run. |
gate_record_postcondition_status | string | Did the "every record really is in the requested states and change codes" gate run? |
gate_national_scale_status | string | Did the national-scale row-count band run? Skips on an unscoped or 51-state query. |
gate_index_freshness_status | string | Did the index-age check run? Skips when FDIC serves no meta.index.createTimestamp. |
gate_reconciliation_status | string | Did the replay-vs-standing-branches reconciliation run? Skips when /banks/locations was not consulted or the state is too small. |
drift_gates_ok, drift_gates_skipped | integer | How many of the five gates ran, and how many did not. |
drift_gates_skipped_reason | string | Named reason per skipped gate. Null when all five ran. |
source_notes | string | Populated only when a source degraded. Null on a healthy run. |
data_asof_history, data_asof_locations | string | Vintage of each upstream snapshot. |
source_url, retrieved_at | string | The exact FDIC query and the fetch timestamp. |
county mode aggregates the same quarter across all banks and adds market_entries,
market_exits, county_banks_with_offices and county_banks_current. bank mode gives one row
per bank per quarter across the whole requested geography, with counties_touched,
county_market_exits_confirmed and the bank's offices_current. events mode is the raw typed
stream — one row per branch event and per relocation, with transnum, org_role_code,
acq_uninum / out_uninum and the resolved acquirer_cert / acquirer_name / seller_cert /
seller_name.
Use as an MCP tool
This actor is available to AI agents through mcp.apify.com. Every input and
output field carries a description, so an agent can pick and chain it without extra prompting.
Typical agent question: "Which Mississippi counties have lost bank branches for three straight
quarters, and which banks have fully exited them?" — one call with mode: "county", then filter on
is_branch_desert_signal.
Pricing
$15 per 1,000 results, billed on the automatic result event — you pay for rows, not for compute.
Graduated discounts apply on paid Apify plans (Bronze 20% through Diamond 70%). Use maxResults to
cap a large sweep. A run that fails a source-integrity assertion emits zero rows and bills
nothing.
Related actors
- FDIC Bank Structure Change & M&A Delta Monitor — the institution-level companion. All 41 FDIC change codes decoded, acquirer/target linkage, and a delta feed of new mergers, failures and charter changes.
- FDIC Deposit Market Share & HHI Rollup
— Summary of Deposits by county and MSA with market share and Herfindahl concentration. Join on
county_fipsto see whether a branch exit actually moved deposits. - FDIC & NCUA Bank Health Rollup
— capital, asset quality and earnings for the same certificates. Join on
certto see whether a bank is retrenching from strength or from stress.
FAQ
Why is offices_end bigger than offices_current?
Because FDIC records no outbound branch event when a charter is absorbed, so the acquired bank's
balance never returns to zero. offices_current is the verified figure and reconciliation_delta
is the gap. This is a property of the source, documented rather than hidden.
Why did my run fail with "did not honour the filter"? A source-integrity assertion tripped: FDIC returned something other than what was asked for. The run is failed deliberately so you are not billed for a national dataset when you asked for one state. Re-run; if it persists, FDIC's API has changed.
Zero rows — is that a bug? Only if the run is red. A green run with zero rows means FDIC honoured a well-formed query and genuinely has no branch events for that state and window. A broken query fails loudly instead.
How far back does the data go?
The branch-event stream reaches back to 1970. Branches established before that have no opening
event, which is one reason the event balance is a lower bound and offices_current is not.
Can I get branch-level addresses and coordinates?
events mode gives branch names, numbers, UNINUMs and cities. Street addresses and latitude and
longitude for the standing network live in FDIC /banks/locations and can be surfaced on request.
Is the data licensed? FDIC data is US federal government work product and in the public domain. Attribution to the FDIC is good practice.