# HMDA Fair Lending Disparity Analysis — Denial & Pricing (`malonestar/hmda-fair-lending-disparity-rollup`) Actor

Fair-lending exam analytics on CFPB HMDA data: denial-rate disparity index with two-proportion z-tests, denial reasons, higher-priced lending, LMI tract redlining screen, and lender-vs-market benchmarks by LEI, state, MSA, county or tract.

- **URL**: https://apify.com/malonestar/hmda-fair-lending-disparity-rollup.md
- **Developed by:** [Kyle Maloney](https://apify.com/malonestar) (community)
- **Categories:** Agents, Developer tools, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.40 / 1,000 results

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 a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use 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

## HMDA Fair Lending Disparity Analysis — Denial, Pricing & Redlining Screening by Lender, MSA & Tract

**Fair-lending exam analytics** on official CFPB/FFIEC HMDA data. Not a raw-loan dump — this
actor returns the analysis those loans are for: a **denial-rate disparity index with a
two-proportion z-test**, **denial-reason mix**, **higher-priced lending (rate-spread) disparity**,
**LMI and majority-minority tract context**, and **lender-vs-market peer benchmarks** — for any
lender (LEI), state, MSA, county or census tract, 2018 through 2025. Keyless.

### The screen that survives review

A disparity ratio alone is not a finding. Colorado's Baca County, HMDA 2023:

| | Applications | Denial rate | Ratio | z | p | Verdict |
|---|---|---|---|---|---|---|
| American Indian / Alaska Native | 3 | 66.67% | **1.693** | 0.92 | 0.359 | **not significant** |
| White (reference) | 33 | 39.39% | 1.000 | — | — | reference |

A ratio-only screen reports that county as a 1.69x fair-lending disparity. It is three
applications. This actor runs a pooled **two-proportion z-test** and a **minimum-sample gate** on
every row, so that cell comes back `INSUFFICIENT_N`, not a finding.

Swept across **all 63 Colorado counties** (HMDA 2023, by race): **123 raw ">=1.5x" flags collapse
to 22 statistically significant ones — an 82.1% false-positive reduction.** At census-tract level
in Denver County the gate screens out 80 of 88. Every number here is reproducible from this
actor's own output.

### Lender vs market — the question a compliance officer actually asks

`leis: ["549300AG64NHILB7ZP05"], states: ["CO"], years: ["2023"]`

| | Black or African American | White | Index | p |
|---|---|---|---|---|
| **loanDepot (CO)** | 13.92% | 13.98% | **0.996** | 0.989 — not significant |
| **CO market, all 1,198 filers** | 32.27% | 21.24% | **1.519** | < 1e-6 — significant |

The lender is at parity inside a market running a significant 1.52x disparity. Without the market
row that institution looks unexamined; with it, it is exonerated in one line. Every lender row
carries `market_denial_rate_pct`, `market_disparity_index`, `lender_vs_market_index`,
`market_rank_by_volume` (21 of 1,198 here) and an optional national baseline.

### Denial-reason mix — the first table in an exam workpaper

HMDA reports up to four denial reasons per denied application. Colorado 2023, share of each
group's denials by **primary** reason:

| Group | Denials | Debt-to-income | **Credit history** | Collateral | Incomplete | Credit-history index |
|---|---|---|---|---|---|---|
| White (reference) | 26,728 | 37.4% | **22.9%** | 14.3% | 10.8% | 1.000 |
| Black or African American | 1,672 | 35.1% | **29.8%** | 12.7% | 9.9% | **1.298** |
| American Indian / Alaska Native | 635 | 33.4% | **30.0%** | 12.1% | 10.4% | 1.310 |
| Asian | 1,448 | 44.3% | **18.7%** | 12.3% | 8.4% | 0.817 |

### Higher-priced lending — the pricing half of the exam

Share of originations at or above the 1.5 rate-spread threshold, Colorado 2023
(rate-spread coverage 90.2% of originations; `NA`/`Exempt` are excluded, never counted as zero):

| Group | Higher-priced >=1.5 | Index vs White |
|---|---|---|
| American Indian / Alaska Native | 24.48% | **1.425** |
| Native Hawaiian / Pacific Islander | 20.54% | 1.195 |
| Black or African American | 17.69% | 1.030 |
| White (reference) | 17.18% | 1.000 |
| Asian | 10.78% | 0.628 |

### CRA / LMI tract context — the redlining screen

Colorado 2023, 1,419 census tracts with activity. Denial rate by CRA tract income level:
**Low 39.01%, Moderate 26.07%, Middle 22.58%, Upper 19.42%.**

| Group | Originations in LMI tracts | In majority-minority tracts |
|---|---|---|
| Black or African American | 37.42% | 43.35% |
| American Indian / Alaska Native | 33.23% | 30.51% |
| White | 20.77% | 14.25% |

`REDLINING_SCREEN_POSITIVE` fires only when a **statistically significant** denial disparity
coincides with LMI or minority-tract underservice.

### Ethnicity is a separate protected class

Hispanic or Latino origin is protected independently of race under ECOA and the Fair Housing Act,
and HMDA reports it separately. In Colorado 2023 it is the **larger** signal:

Hispanic or Latino **34.56%** vs Not Hispanic or Latino **20.23%** — index **1.708**, z 47.88.
Set `groupBy: "ethnicity"`.

### Who it's for

- **Bank fair-lending & CRA compliance officers** — screen your own LEI against your markets before an exam does, with a defensible statistical test attached to every finding.
- **Fair-lending consultants** — denial-reason and pricing workpapers across a client portfolio in one run.
- **Community-reinvestment analysts** — CRA assessment-area analysis by MSA, county and tract with LMI and minority-tract penetration indexes.
- **Bank M&A due-diligence teams** — screen a target institution's national and per-market disparity profile before signing.
- **Researchers & journalists** — a reproducible redlining signal with the exact CFPB source URL on every row.

### Modes

| Mode | Input | What you get |
|---|---|---|
| State / MSA / county | `states`, `msamds` or `counties` | Denial rate, disparity index, z-test per group |
| **Lender** | `leis` (+ one geography) | The above, plus market and national baselines, rank and peer count |
| **Denial reasons** | `includeDenialReasons: true` | Nine primary-reason counts, top reason, credit-history index |
| **Pricing** | `includeRateSpread: true` | Higher-priced 1.5/2.5 shares, median rate spread, HOEPA, index |
| **CRA / LMI** | `includeTractContext: true` | LMI and minority-tract origination shares, denial rates, penetration indexes |
| **Tract-level** | `tractLevel: true` | One row per (census tract, group), each with its own z-test |

### Example input

```json
{
  "leis": ["549300AG64NHILB7ZP05"],
  "states": ["CO"],
  "years": ["2023"],
  "groupBy": "race",
  "referenceGroup": "White",
  "includeDenialReasons": true,
  "includeRateSpread": true,
  "includeTractContext": true,
  "minSampleN": 30
}
````

Running with **no input** performs a bounded default analysis (Colorado, 2023, by race, vs White).

### Methodology (read this before citing numbers)

- **Disparity index** = group denial rate / reference-group denial rate. 1.0 is parity. It is a
  screening signal, not proof of discrimination: HMDA public data has no credit score, and LTV/DTI
  only in bands. **An index is never reported as a finding unless it also clears the z-test and the
  minimum-sample gate.**
- **Significance** = two-sided pooled two-proportion z-test vs the reference group, default
  alpha 0.05, plus `minSampleN` (default 30). Rows also carry a 95% **Wilson** confidence interval,
  which stays inside \[0,100] at small n where the normal approximation does not.
- **Denominator.** Default `narrow` = denied / (originated + denied) — the cleanest approve-deny
  decision rate, and the v1 behaviour. `ffiec` = denied / actions 1-5, adding approved-not-accepted,
  withdrawn and closed-for-incompleteness. The narrow basis runs hotter: Colorado 2023
  Black-vs-White is **1.519 narrow but 1.457 on the FFIEC basis**. Both are always emitted.
- **Withdrawal rate** (HMDA action 4) is emitted as a separate discouragement signal.
- Race, ethnicity and sex are HMDA *derived* fields. "Race Not Available" is itself a group (often
  large) and is included.
- **Null discipline.** The LAR tokens `NA`, `Exempt`, `1111` and `8888` are treated as missing,
  never as zero. Pricing figures ship with `rate_spread_coverage_pct` so you can see the base. The
  `census_tract` "NA" token (1,097 of 175,185 Colorado 2023 rows) is dropped rather than collapsed
  into a fake tract.
- **Zero-activity groups are never emitted and never billed.** The CFPB API returns explicit
  `count: 0` rows for every enumerated group — 6 of 9 in county 08111. A 63-county Colorado sweep
  suppresses 187 of 567 possible rows.
- The CFPB aggregation API allows at most 2 filters per call and its geography classes are mutually
  exclusive (`states` + `counties` silently returns statewide data with HTTP 200). This actor
  refuses to build such a request rather than report the wrong number.

### Output

80 fields per row. Headline: `denial_rate_pct`, `disparity_index`, `z_score`, `p_value`,
`is_significant_95`, `min_n_met`, `finding_strength`, `flags`. Plus peer (`market_*`,
`lender_vs_market_*`), denial reasons (`denied_*`, `credit_history_share_index`), pricing
(`higher_priced_*`, `median_rate_spread`, `hoepa_*`), CRA (`lmi_*`, `majority_minority_tract_*`,
`tract_income_level`), and `source_url` for audit on every row.

#### Which columns populate in which mode

Every column is nullable and many are deliberately mode-dependent. Nothing below is a defect —
this table is the contract. The default (prefill) run fills **66 of 80**.

| Column block | Populates when | Null otherwise because |
|---|---|---|
| Core counts, denial rate, disparity index, z/p, `finding_strength`, flags | **always, every mode** | — |
| FFIEC denominator block (`denial_rate_ffiec_pct`, `disparity_index_ffiec`, `applications_withdrawn`, `applications_approved_not_accepted`, `applications_closed_incomplete`, `withdrawal_rate_*`) | **always** — every query fetches HMDA actions 1-5 | — |
| `yoy_denial_rate_change_pp` | two or more **consecutive years** requested | one-year run has no prior year to difference against |
| `lei`, `institution_name`, `market_*`, `lender_vs_market_*`, `peer_lender_count`, `market_rank_by_volume` | **lender mode** (`leis` supplied) | a whole state/MSA/county **is** its own market — there is no distinct benchmark to compare it against, so a self-referential 1.000 would be noise |
| `national_*` | `includeNational: true` | costs one extra call per year; off by default |
| `denied_*`, `denial_reason_*`, `credit_history_share_index`, `secondary_reason_rate_pct` | `includeDenialReasons: true` | needs the loan-level engine; the aggregate API cannot express denial reasons at any price |
| `higher_priced_*`, `median_rate_spread`, `rate_spread_coverage_pct`, `originations_with_rate_spread`, `hoepa_*` | `includeRateSpread: true` | as above |
| `lmi_*`, `majority_minority_tract_*`, `tracts_with_activity` | `includeTractContext: true` | as above |
| `census_tract`, `tract_income_level`, `tract_median_family_income_pct` | `tractLevel: true` | these describe a single tract; they are meaningless on an aggregated row |

`finding_strength` is **never null**. It reads `REFERENCE`, `STRONG`, `MODERATE`,
`NOT_SIGNIFICANT`, `INSUFFICIENT_N`, or `NO_REFERENCE_GROUP` (loan-purpose grouping, where no
protected-class baseline exists and therefore no test is run) — so you can always read *why*
there is or is not a finding.

**Flags.** Findings: `SIGNIFICANT_DISPARITY_1_5X`, `SIGNIFICANT_DISPARITY_2X`,
`HIGHER_PRICED_DISPARITY`, `LMI_UNDERSERVED`, `MINORITY_TRACT_UNDERSERVED`,
`REDLINING_SCREEN_POSITIVE`. Screened out: `NOT_STATISTICALLY_SIGNIFICANT`, `INSUFFICIENT_N`.
Legacy ungated flags (`DISPARITY_ABOVE_1_5X`, `DISPARITY_ABOVE_2X`, `IS_REFERENCE_GROUP`,
`LOW_SAMPLE`, `DENIAL_RATE_RISING`) are preserved unchanged for existing consumers.

Dataset views: **Fair-lending findings**, **Denial-reason mix**, **Higher-priced lending**,
**Lender vs market**, **CRA / LMI tract context**.

### Use as an MCP tool

Add this actor via **mcp.apify.com** — AI agents (Claude, Cursor) can call it with an LEI or a
state list and get back a compact, fully-described fair-lending table, ideal for chaining into
compliance-report or diligence workflows.

### FAQ

**Which years are available?** **2018 through 2025**, all verified live. Request consecutive years
for year-over-year trend columns.

**Does it cover counties, MSAs, tracts or individual lenders?** All of them. Use `states`,
`msamds`, `counties` or `leis`; add `tractLevel` for per-census-tract rows.

**Do I need an API key?** No. The source is the official public CFPB/FFIEC HMDA data-browser API.

**How is this different from other HMDA actors?** They export raw loan-level rows and leave you to
build the analysis. This one returns the fair-lending analysis itself — disparity index,
significance test, denial reasons, pricing disparity, CRA tract context, peer benchmark — with the
exact source call attached to every row.

**Is a disparity index above 1.5 illegal?** No. It is a screening threshold used to prioritise
review, and on its own it is not even a finding — see the methodology note.

**Is this loan-level data?** The exam analytics are computed from the CFPB's loan-level LAR export,
streamed and aggregated in-process. You get the analysis, not a multi-GB file. Aggregate-only mode
(`engine: "aggregate"`) skips the download when you only need denial rates.

### Pricing

Pay per result row. A one-state, one-year race analysis returns 9 rows; a lender screened across
one market returns up to 9; a 50-state two-year sweep returns ~900. Zero-activity groups are never
billed, and the run logs its expected row count before fetching anything.

# Actor input Schema

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

Two-letter US state codes to analyze (e.g. CO, TX, NY). CFPB geography classes are MUTUALLY EXCLUSIVE and the API fails silently (states + counties returns statewide data with HTTP 200), so supply only one of states / MSAs / counties. If you supply more than one, the narrowest wins and a warning is logged.

## `msamds` (type: `array`):

Five-digit Metropolitan Statistical Area / Metropolitan Division codes (e.g. 19740 = Denver-Aurora-Lakewood, 35620 = New York-Newark-Jersey City). Use instead of states for CRA assessment-area work.

## `counties` (type: `array`):

Five-digit county FIPS codes (e.g. 08031 = Denver County, 48201 = Harris County TX). Use instead of states for county-level fair-lending screening.

## `leis` (type: `array`):

20-character Legal Entity Identifiers of the institutions to screen (e.g. 549300AG64NHILB7ZP05 = loanDepot). Supplying LEIs switches the actor into lender mode: each lender is measured against the market baseline for the same geography and year, plus the national baseline. LEIs may be combined with exactly one geography class to scope the lender to that market.

## `peerBenchmark` (type: `boolean`):

In lender mode, also fetch the all-lender market baseline and the filer roster for the same geography, so every row carries market\_denial\_rate\_pct, lender\_vs\_market\_index, market\_rank\_by\_volume and peer\_lender\_count. Costs 2 extra API calls per geography-year.

## `includeNational` (type: `boolean`):

Also fetch the nationwide baseline for each group and year (one extra call per year, cached across geographies), populating national\_denial\_rate\_pct and national\_disparity\_index.

## `years` (type: `array`):

HMDA filing years to analyze, e.g. \["2024"] or \["2023","2024"]. The CFPB data browser covers 2018 through 2025 (all verified live). Request two or more consecutive years to also get the year-over-year denial-rate change per group. Years outside the covered range are dropped with a warning rather than silently returning nothing.

## `groupBy` (type: `string`):

Protected class or loan dimension to break denial rates down by: "race" (9 HMDA derived-race groups), "ethnicity" (Hispanic or Latino origin, a separately protected class under ECOA/FHA and often the largest disparity in the data), "sex", or "loan\_purpose". Disparity indexes and significance tests are computed against the reference group.

## `referenceGroup` (type: `string`):

Group whose denial rate is the disparity baseline: disparity\_index = group denial rate / reference denial rate. Defaults to "White" for race, "Not Hispanic or Latino" for ethnicity and "Male" for sex. A value that does not exist for the chosen grouping falls back to that default AND logs a warning.

## `loanPurposes` (type: `array`):

Optional list of HMDA loan-purpose codes or labels to include when groupBy is "loan\_purpose" (1 Home purchase, 2 Home improvement, 31 Refinancing, 32 Cash-out refinancing, 4 Other, 5 Not applicable). Ignored for other groupings because the CFPB API allows at most 2 filters per call.

## `minSampleN` (type: `integer`):

Groups with fewer decisioned applications than this cannot produce a finding — they are marked INSUFFICIENT\_N instead. This is what stops a three-application census tract from manufacturing a 1.69x 'disparity'. Set to 0 to disable the gate.

## `significanceLevel` (type: `string`):

Two-sided significance threshold for the pooled two-proportion z-test comparing each group to the reference group. 0.05 = 95% confidence (default). Only disparities that clear BOTH this threshold and the minimum sample size raise a SIGNIFICANT\_DISPARITY flag.

## `denominator` (type: `string`):

"narrow" = denied / (originated + denied), the cleanest approve-deny decision rate and the v1 default. "ffiec" = denied / actions 1-5, which also counts approved-not-accepted, withdrawn and closed-for-incompleteness, matching how several regulator tables are built. The narrow basis runs hotter: CO 2023 Black-vs-White is 1.519 narrow but 1.457 on the FFIEC basis. Both are always emitted; this picks which one drives the headline fields.

## `includeDenialReasons` (type: `boolean`):

Break each group's denials down by HMDA primary denial reason (debt-to-income, credit history, collateral, incomplete, and five more) and index the credit-history share against the reference group. This is the first table in a fair-lending exam workpaper. Requires the loan-level engine, which streams the CFPB LAR export (one extra request per geography-year, roughly 3 seconds for a whole state).

## `includeRateSpread` (type: `boolean`):

Compute each group's share of originations at or above the 1.5 and 2.5 rate-spread thresholds, the median rate spread, HOEPA high-cost counts, and a higher-priced index vs the reference group. Pricing discrimination is the other core exam analytic alongside denials. Requires the loan-level engine.

## `includeTractContext` (type: `boolean`):

Add the geographic half of a fair-lending exam: each group's share of originations in low- and moderate-income tracts and in majority-minority tracts, denial rates within those tracts, and penetration indexes that drive the LMI\_UNDERSERVED, MINORITY\_TRACT\_UNDERSERVED and REDLINING\_SCREEN\_POSITIVE flags. Requires the loan-level engine.

## `tractLevel` (type: `boolean`):

Also emit one row per (census tract, group), each with its own disparity index, z-test and CRA income level. Warning: this multiplies result rows — Colorado 2023 alone has 1,419 active tracts. Cap it with maxResults. Requires the loan-level engine.

## `engine` (type: `string`):

"auto" (default) uses the fast aggregation endpoint unless you enabled any exam analytic above, in which case it streams loan-level data. "aggregate" forces counts-only. "loanlevel" forces the LAR stream, which is a strict superset (it reproduces the aggregate denial rates exactly) but costs a larger download.

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

Hard cap on emitted rows across all geographies, lenders and years (caps your cost). Rows with zero applications are never emitted and never billed. Expected rows = geographies x lenders x years x groups; the run logs this estimate before fetching anything.

## Actor input object example

```json
{
  "states": [
    "CO"
  ],
  "peerBenchmark": true,
  "includeNational": false,
  "years": [
    "2023"
  ],
  "groupBy": "race",
  "referenceGroup": "White",
  "minSampleN": 30,
  "significanceLevel": "0.05",
  "denominator": "narrow",
  "includeDenialReasons": true,
  "includeRateSpread": true,
  "includeTractContext": true,
  "tractLevel": false,
  "engine": "auto",
  "maxResults": 1000
}
```

# Actor output Schema

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

The default dataset with disparity rollup rows.

# 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 = {
    "states": [
        "CO"
    ],
    "years": [
        "2023"
    ],
    "groupBy": "race",
    "referenceGroup": "White",
    "minSampleN": 30,
    "significanceLevel": "0.05",
    "includeDenialReasons": true,
    "includeRateSpread": true,
    "includeTractContext": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("malonestar/hmda-fair-lending-disparity-rollup").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 = {
    "states": ["CO"],
    "years": ["2023"],
    "groupBy": "race",
    "referenceGroup": "White",
    "minSampleN": 30,
    "significanceLevel": "0.05",
    "includeDenialReasons": True,
    "includeRateSpread": True,
    "includeTractContext": True,
}

# Run the Actor and wait for it to finish
run = client.actor("malonestar/hmda-fair-lending-disparity-rollup").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "states": [
    "CO"
  ],
  "years": [
    "2023"
  ],
  "groupBy": "race",
  "referenceGroup": "White",
  "minSampleN": 30,
  "significanceLevel": "0.05",
  "includeDenialReasons": true,
  "includeRateSpread": true,
  "includeTractContext": true
}' |
apify call malonestar/hmda-fair-lending-disparity-rollup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=malonestar/hmda-fair-lending-disparity-rollup",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "HMDA Fair Lending Disparity Analysis — Denial & Pricing",
        "description": "Fair-lending exam analytics on CFPB HMDA data: denial-rate disparity index with two-proportion z-tests, denial reasons, higher-priced lending, LMI tract redlining screen, and lender-vs-market benchmarks by LEI, state, MSA, county or tract.",
        "version": "2.0",
        "x-build-id": "EqQ6Ur8N07Cpg5grC"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/malonestar~hmda-fair-lending-disparity-rollup/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-malonestar-hmda-fair-lending-disparity-rollup",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/malonestar~hmda-fair-lending-disparity-rollup/runs": {
            "post": {
                "operationId": "runs-sync-malonestar-hmda-fair-lending-disparity-rollup",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/malonestar~hmda-fair-lending-disparity-rollup/run-sync": {
            "post": {
                "operationId": "run-sync-malonestar-hmda-fair-lending-disparity-rollup",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "states": {
                        "title": "States",
                        "type": "array",
                        "description": "Two-letter US state codes to analyze (e.g. CO, TX, NY). CFPB geography classes are MUTUALLY EXCLUSIVE and the API fails silently (states + counties returns statewide data with HTTP 200), so supply only one of states / MSAs / counties. If you supply more than one, the narrowest wins and a warning is logged.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "msamds": {
                        "title": "MSA / MD codes",
                        "type": "array",
                        "description": "Five-digit Metropolitan Statistical Area / Metropolitan Division codes (e.g. 19740 = Denver-Aurora-Lakewood, 35620 = New York-Newark-Jersey City). Use instead of states for CRA assessment-area work.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "counties": {
                        "title": "County FIPS codes",
                        "type": "array",
                        "description": "Five-digit county FIPS codes (e.g. 08031 = Denver County, 48201 = Harris County TX). Use instead of states for county-level fair-lending screening.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "leis": {
                        "title": "Lender LEIs (lender mode)",
                        "type": "array",
                        "description": "20-character Legal Entity Identifiers of the institutions to screen (e.g. 549300AG64NHILB7ZP05 = loanDepot). Supplying LEIs switches the actor into lender mode: each lender is measured against the market baseline for the same geography and year, plus the national baseline. LEIs may be combined with exactly one geography class to scope the lender to that market.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "peerBenchmark": {
                        "title": "Peer benchmark (market baseline)",
                        "type": "boolean",
                        "description": "In lender mode, also fetch the all-lender market baseline and the filer roster for the same geography, so every row carries market_denial_rate_pct, lender_vs_market_index, market_rank_by_volume and peer_lender_count. Costs 2 extra API calls per geography-year.",
                        "default": true
                    },
                    "includeNational": {
                        "title": "Include national baseline",
                        "type": "boolean",
                        "description": "Also fetch the nationwide baseline for each group and year (one extra call per year, cached across geographies), populating national_denial_rate_pct and national_disparity_index.",
                        "default": false
                    },
                    "years": {
                        "title": "HMDA data years",
                        "type": "array",
                        "description": "HMDA filing years to analyze, e.g. [\"2024\"] or [\"2023\",\"2024\"]. The CFPB data browser covers 2018 through 2025 (all verified live). Request two or more consecutive years to also get the year-over-year denial-rate change per group. Years outside the covered range are dropped with a warning rather than silently returning nothing.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "groupBy": {
                        "title": "Group by",
                        "enum": [
                            "race",
                            "ethnicity",
                            "sex",
                            "loan_purpose"
                        ],
                        "type": "string",
                        "description": "Protected class or loan dimension to break denial rates down by: \"race\" (9 HMDA derived-race groups), \"ethnicity\" (Hispanic or Latino origin, a separately protected class under ECOA/FHA and often the largest disparity in the data), \"sex\", or \"loan_purpose\". Disparity indexes and significance tests are computed against the reference group.",
                        "default": "race"
                    },
                    "referenceGroup": {
                        "title": "Reference group",
                        "type": "string",
                        "description": "Group whose denial rate is the disparity baseline: disparity_index = group denial rate / reference denial rate. Defaults to \"White\" for race, \"Not Hispanic or Latino\" for ethnicity and \"Male\" for sex. A value that does not exist for the chosen grouping falls back to that default AND logs a warning."
                    },
                    "loanPurposes": {
                        "title": "Loan purposes (only with groupBy = loan_purpose)",
                        "type": "array",
                        "description": "Optional list of HMDA loan-purpose codes or labels to include when groupBy is \"loan_purpose\" (1 Home purchase, 2 Home improvement, 31 Refinancing, 32 Cash-out refinancing, 4 Other, 5 Not applicable). Ignored for other groupings because the CFPB API allows at most 2 filters per call.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "minSampleN": {
                        "title": "Minimum sample size for a finding",
                        "minimum": 0,
                        "maximum": 100000,
                        "type": "integer",
                        "description": "Groups with fewer decisioned applications than this cannot produce a finding — they are marked INSUFFICIENT_N instead. This is what stops a three-application census tract from manufacturing a 1.69x 'disparity'. Set to 0 to disable the gate.",
                        "default": 30
                    },
                    "significanceLevel": {
                        "title": "Significance level (alpha)",
                        "type": "string",
                        "description": "Two-sided significance threshold for the pooled two-proportion z-test comparing each group to the reference group. 0.05 = 95% confidence (default). Only disparities that clear BOTH this threshold and the minimum sample size raise a SIGNIFICANT_DISPARITY flag.",
                        "default": "0.05"
                    },
                    "denominator": {
                        "title": "Denial-rate denominator",
                        "enum": [
                            "narrow",
                            "ffiec"
                        ],
                        "type": "string",
                        "description": "\"narrow\" = denied / (originated + denied), the cleanest approve-deny decision rate and the v1 default. \"ffiec\" = denied / actions 1-5, which also counts approved-not-accepted, withdrawn and closed-for-incompleteness, matching how several regulator tables are built. The narrow basis runs hotter: CO 2023 Black-vs-White is 1.519 narrow but 1.457 on the FFIEC basis. Both are always emitted; this picks which one drives the headline fields.",
                        "default": "narrow"
                    },
                    "includeDenialReasons": {
                        "title": "Denial-reason mix",
                        "type": "boolean",
                        "description": "Break each group's denials down by HMDA primary denial reason (debt-to-income, credit history, collateral, incomplete, and five more) and index the credit-history share against the reference group. This is the first table in a fair-lending exam workpaper. Requires the loan-level engine, which streams the CFPB LAR export (one extra request per geography-year, roughly 3 seconds for a whole state).",
                        "default": false
                    },
                    "includeRateSpread": {
                        "title": "Higher-priced lending (pricing disparity)",
                        "type": "boolean",
                        "description": "Compute each group's share of originations at or above the 1.5 and 2.5 rate-spread thresholds, the median rate spread, HOEPA high-cost counts, and a higher-priced index vs the reference group. Pricing discrimination is the other core exam analytic alongside denials. Requires the loan-level engine.",
                        "default": false
                    },
                    "includeTractContext": {
                        "title": "CRA / LMI tract context",
                        "type": "boolean",
                        "description": "Add the geographic half of a fair-lending exam: each group's share of originations in low- and moderate-income tracts and in majority-minority tracts, denial rates within those tracts, and penetration indexes that drive the LMI_UNDERSERVED, MINORITY_TRACT_UNDERSERVED and REDLINING_SCREEN_POSITIVE flags. Requires the loan-level engine.",
                        "default": false
                    },
                    "tractLevel": {
                        "title": "Emit per-census-tract rows",
                        "type": "boolean",
                        "description": "Also emit one row per (census tract, group), each with its own disparity index, z-test and CRA income level. Warning: this multiplies result rows — Colorado 2023 alone has 1,419 active tracts. Cap it with maxResults. Requires the loan-level engine.",
                        "default": false
                    },
                    "engine": {
                        "title": "Engine",
                        "enum": [
                            "auto",
                            "aggregate",
                            "loanlevel"
                        ],
                        "type": "string",
                        "description": "\"auto\" (default) uses the fast aggregation endpoint unless you enabled any exam analytic above, in which case it streams loan-level data. \"aggregate\" forces counts-only. \"loanlevel\" forces the LAR stream, which is a strict superset (it reproduces the aggregate denial rates exactly) but costs a larger download.",
                        "default": "auto"
                    },
                    "maxResults": {
                        "title": "Max result rows",
                        "minimum": 1,
                        "maximum": 100000,
                        "type": "integer",
                        "description": "Hard cap on emitted rows across all geographies, lenders and years (caps your cost). Rows with zero applications are never emitted and never billed. Expected rows = geographies x lenders x years x groups; the run logs this estimate before fetching anything.",
                        "default": 1000
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
