# FHLBank Membership Roster & Delta Monitor (`malonestar/fhlbank-membership-delta-monitor`) Actor

FHLBank System membership roster and quarter-over-quarter delta tracker sourced from FHFA's published member file: joins, exits and attribute changes across all 11 districts, with CERT/FED\_ID/NCUA\_ID/NAIC\_ID join keys for correspondent banking, KYB and competitive intel.

- **URL**: https://apify.com/malonestar/fhlbank-membership-delta-monitor.md
- **Developed by:** [Kyle Maloney](https://apify.com/malonestar) (community)
- **Categories:** Business, Developer tools, Agents
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.60 / 1,000 roster rows

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## FHLBank Membership Roster & Delta Monitor

FHLBank System membership roster and quarter-over-quarter delta tracker, sourced directly
from FHFA's own published member file (`fhlb_members_q<N><YYYY>.xlsx`). Covers all 11
FHLBank districts (Atlanta, Boston, Chicago, Cincinnati, Dallas, Des Moines, Indianapolis,
New York, Pittsburgh, San Francisco, Topeka) and every member type FHFA tracks: commercial
banks, savings institutions, credit unions, insurance companies and CDFIs.

Every row carries the standard cross-reference join keys published on the source file --
`cert` (FDIC certificate number), `fed_id` (Federal Reserve ID), `ncua_id` (NCUA charter
number) and `naic_id` (insurance identifier) -- so this is a clean join target for
[fdic-ncua-health-rollup](https://apify.com/malonestar/fdic-ncua-health-rollup) (bank/CU
financial health) and any other CERT- or NCUA-keyed data source.

### Who this is for

- **Correspondent banking / capital markets desks** tracking which institutions carry
  FHLBank advance access, or watching for new entrants and exits in a district.
- **KYB / vendor-onboarding teams** who need an authoritative FHFA-published cross-walk
  from an institution name to its FDIC CERT / Federal Reserve ID / NCUA charter number.
- **Fintech and correspondent-lending BD teams** building a target list of FHLBank members
  by state, charter type or district.
- **Bank/credit-union competitive intelligence** teams tracking membership changes --
  mergers, charter conversions, new entrants -- as a leading signal.

### Two modes

#### Roster mode (default)

Returns a filtered listing of current FHLBank members from the most recently posted
quarterly file (FHFA hosts several recent quarters simultaneously; this actor automatically
walks back up to 4 quarters if the very latest one is not yet posted).

```json
{ "mode": "roster", "states": ["CO"], "maxResults": 500 }
```

Every roster row also carries `is_new_since_last_run` -- whether this member was absent
from a cross-run baseline stored in your account's key-value store. On your very first run
this is `null` for every row (there is nothing to compare against yet), never `true`.

#### Delta mode

Resolves the prior quarter's file too (a second, real FHFA file -- not a guess) and returns
only the join/exit/attribute\_change EVENTS between the two:

```json
{ "mode": "delta", "eventTypes": ["join", "exit"], "states": ["TX"] }
```

- **join** -- a new FHFA\_ID present in the current quarter, absent from the prior one.
- **exit** -- an FHFA\_ID present in the prior quarter, absent from the current one (the
  member left the open-membership roster; FHFA's file does not say why).
- **attribute\_change** -- present in both quarters, but one or more of member\_name, city,
  state, zip, district, mem\_type, char\_type, cert, fed\_id, ncua\_id or naic\_id differs.
  `changed_fields` names which; `previous_values` holds the old value for each.

Because FHFA hosts multiple quarters' files at once, delta mode computes a real
two-file comparison **on your very first run** -- it does not need to wait for a second
scheduled run the way a single-file source would.

### Example output row (delta mode, a real "exit" event)

```json
{
  "mode": "delta",
  "fhfa_id": "54201",
  "member_name": "Comerica Bank",
  "city": "Dallas",
  "state": "TX",
  "district": "Dallas",
  "mem_type": "Commercial Bank",
  "char_type": "State",
  "cert": "00983",
  "fed_id": "0060143",
  "mem_date": "2008-02-14",
  "member_tenure_days": 6754,
  "delta_event_type": "exit",
  "delta_current_quarter": "q22026",
  "delta_prior_quarter": "q12026",
  "file_quarter": "q12026"
}
```

### Input fields

| Field | Type | Description |
|---|---|---|
| `mode` | string | `"roster"` (default) or `"delta"`. |
| `states` | array | US state/territory filter -- 2-letter code or full name, e.g. `"CO"` or `"Colorado"`. |
| `charterTypes` | array | Filter by `Federal`, `National`, `State`, or `None`. |
| `districts` | array | Filter by FHLBank district name. |
| `memberSince` | string | `YYYY-MM-DD` only -- see "Date format" below. Anything else FAILS the run. |
| `quarter` | string | Pin an exact quarter, e.g. `"q22026"`. Leave blank for the latest posted. |
| `eventTypes` | array | Delta mode only -- subset of `join`, `exit`, `attribute_change`. |
| `maxResults` | integer | Row/event cap for this run (default 1000). |

#### Date format -- `memberSince` accepts `YYYY-MM-DD` and nothing else

Accepted: `2020-01-15`, and the unpadded `2020-1-1` (padded for you). Blank or omitted
means no cutoff at all, which is not an error. Every other form **fails the run before
any file is downloaded, and bills nothing**.

`memberSince` is string-compared against membership dates already normalised to ISO, so
a wrong format does not error -- it silently returns the wrong row set and labels it a
successful filtered answer. Measured against a 3-row fixture (membership dates
2018-06-15, 2020-03-01, 2022-02-10) where the true answer for a 2020-01-01 cutoff is
**2 of 3**:

| You pass | Returned before this fix | Why |
|---|---|---|
| `01/15/2020` (US) | **3 of 3** -- the whole set, as if no cutoff were applied | `"01/..."` sorts below every `"20xx-.."` date, so the comparison is true for every row |
| `Jan 2020` | **0 of 3** -- a confident "no members joined since Jan 2020" | `"Jan..."` sorts above every `"2..."` date, so the comparison is false for every row |

Two more worth knowing, both now rejected: `2020-1-1` returned **1 of 3** (unpadded, so
it sorts below every zero-padded month and silently drops January through September),
and `2020-13-45` returned **1 of 3** -- a plausible-looking subset from an impossible
date. Slash forms are refused on purpose rather than guessed: `05/06/2025` is genuinely
ambiguous between US (May 6) and EU (June 5) order, and guessing trades a loud, fixable
error for a silent one-month window shift.

### Output fields

| Field | Description |
|---|---|
| `mode` | `roster` or `delta`. |
| `fhfa_id` | FHFA member ID, zero-padded to 5 digits. The delta join key. |
| `district`, `member_name`, `city`, `state`, `zip` | As published by FHFA. |
| `mem_type`, `char_type` | Closed-vocabulary institution/charter type. |
| `cert` | FDIC certificate number, when FDIC-insured. Join key to FDIC-sourced actors. |
| `fed_id` | Federal Reserve ID, zero-padded to 7 digits. |
| `ncua_id` | NCUA charter number. See `ncua_id_looks_placeholder`. |
| `ncua_id_looks_placeholder` | True for the rare all-zero `"00000"` value (very likely a not-yet-assigned placeholder, never guessed silently). |
| `naic_id` | Insurance-company identifier. |
| `appr_date`, `appr_date_basis` | Approval date + how it was derived (`parsed`/`blank`/`unparseable`/`absent`). |
| `mem_date`, `mem_date_basis` | Membership date (century-pivoted from FHFA's 2-digit-year format) + basis. |
| `member_tenure_days` | Days since `mem_date`. |
| `file_quarter` | Which quarter's file this row's member data is from. |
| `is_new_since_last_run`, `delta_baseline_status` | Roster mode: cross-run "new" flag + how it was determined. |
| `delta_event_type`, `delta_current_quarter`, `delta_prior_quarter` | Delta mode: which kind of event, and the two quarters compared. |
| `changed_fields`, `previous_values` | Delta mode, `attribute_change` rows only. |
| `delta_transition_already_reported` | Delta mode: whether this exact quarter pair was already reported by an earlier run (informational; the delta is always recomputed fresh). |
| `source_url`, `source_resolution_method`, `source_file_last_modified`, `source_row_count` | Provenance: exactly which file, how it was found, and its vintage. |
| `matched_rows_total`, `results_truncated` | So a capped answer is never mistaken for a complete one. |
| `monitored_at` | UTC timestamp of this run. |

### Use as an MCP tool

This actor is callable by AI agents (Claude, Cursor, etc.) via `mcp.apify.com` -- the
input/output schemas above are written to be self-describing to an agent choosing a tool.
A typical agent prompt: *"Which FHLBank Dallas members joined or left between Q1 and Q2
2026?"* maps directly to `{"mode":"delta","districts":["Dallas"],"eventTypes":["join","exit"]}`.

### FAQ

**Does this cover every FHLBank district?** Yes, all 11.

**What does "exit" mean -- did the institution fail?** No. FHFA's open-membership file
does not record a reason for a member's departure (merger, acquisition, voluntary
withdrawal, or termination are all possible). `exit` means the FHFA\_ID is no longer in the
open-membership roster as of the current quarter; it is a fact, not an inference.

**How current is the data?** FHFA posts a new quarterly file on a multi-week lag after each
quarter ends (verified: the April-June 2026 file posted in mid-August 2026). Every row
carries `source_file_last_modified` and `file_quarter` so you always know the vintage.

**Can I get the same member cross-referenced against FDIC/NCUA financial data?** Yes -- join
on `cert` (banks/thrifts) or `ncua_id` (credit unions) to
[fdic-ncua-health-rollup](https://apify.com/malonestar/fdic-ncua-health-rollup).

### Pricing

$12 per 1,000 result rows (Pay-Per-Event, Result event only), with the standard graduated
volume discount schedule.

# Actor input Schema

## `mode` (type: `string`):

"roster" returns a filtered listing of current FHLBank members. "delta" resolves the prior quarter's file too and returns only join/exit/attribute\_change events between it and the current quarter.

## `states` (type: `array`):

Optional list of US states/territories to filter by (2-letter postal code, e.g. "CO", or the full name, e.g. "Colorado" -- both are recognized). Leave empty for all states.

## `charterTypes` (type: `array`):

Optional list of charter types to filter by: "Federal", "National", "State", or "None". Leave empty for all charter types.

## `districts` (type: `array`):

Optional list of FHLBank districts to filter by (Atlanta, Boston, Chicago, Cincinnati, Dallas, Des Moines, Indianapolis, New York, Pittsburgh, San Francisco, Topeka). Leave empty for all districts.

## `memberSince` (type: `string`):

Optional cutoff. Only include members whose FHFA-published membership date is on or after this date. MUST be YYYY-MM-DD (e.g. 2020-01-15) - any other format is rejected and the run FAILS without billing, rather than silently returning every member or none of them. It is string-compared against ISO membership dates: an unvalidated '01/15/2020' returned the WHOLE matching set (as if no cutoff were applied) and 'Jan 2020' returned ZERO rows, both labelled a successful filtered answer. Slash forms are rejected on purpose - 05/06/2025 is ambiguous between US and EU order. Leave blank for no cutoff.

## `quarter` (type: `string`):

Optional exact quarter to use as the "current" file, in the form q<1-4><YYYY>, e.g. "q22026" for the April-June 2026 file. Leave blank to automatically use the most recently posted quarter (with rollback up to 4 quarters if it is not yet posted).

## `eventTypes` (type: `array`):

Delta mode only. Which event types to include: "join" (new members), "exit" (members no longer in the current file), "attribute\_change" (a field changed for an existing member). Leave empty to include all three.

## `maxResults` (type: `integer`):

Maximum number of rows (roster mode) or delta events (delta mode) to return in this run.

## Actor input object example

```json
{
  "mode": "roster",
  "states": [
    "CO"
  ],
  "charterTypes": [],
  "districts": [],
  "eventTypes": [],
  "maxResults": 1000
}
```

# Actor output Schema

## `results` (type: `string`):

The default dataset.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "mode": "roster",
    "states": [
        "CO"
    ],
    "maxResults": 1000
};

// Run the Actor and wait for it to finish
const run = await client.actor("malonestar/fhlbank-membership-delta-monitor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "mode": "roster",
    "states": ["CO"],
    "maxResults": 1000,
}

# Run the Actor and wait for it to finish
run = client.actor("malonestar/fhlbank-membership-delta-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "mode": "roster",
  "states": [
    "CO"
  ],
  "maxResults": 1000
}' |
apify call malonestar/fhlbank-membership-delta-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,malonestar/fhlbank-membership-delta-monitor"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/v9WZA3DeingscKChG/builds/AlJc5NBmcrNysKS4q/openapi.json
