Skip to content
FinObservatory

Consumer finance / Methodology

US Consumer Complaints: methodology

How the /consumer layer is built, what a complaint is and is not, and every convention behind the displayed numbers. The single source of truth for this page's caveats; the layer's code (scripts/build_cfpb_complaints.py, src/lib/consumer.ts) mirrors what is written here.

Source

The CFPB Consumer Complaint Database, via the Bureau's full bulk CSV export: files.consumerfinance.gov/ccdb/complaints.csv.zip, linked as "Download all complaint data" from the database landing page. US government work, public data. The bulk file is a full export of the published database and, per the landing page, "The database generally updates daily." The vintage behind the current build was fetched 2026-07-18 (server Last-Modified 2026-07-18 09:30 UTC): 17,010,343 rows spanning Date received 2011-12-01 to 2026-07-18.

FinObservatory displays aggregate statistics with attribution and does not re-serve the underlying records; the CFPB's own search tool and API are the place to retrieve complaint-level data.

What a complaint is not (the Bureau's own words)

Quoted verbatim from the database landing page, retrieved 2026-07-18:

  • "This database is not a statistical sample of consumers' experiences in the marketplace and these complaints are not necessarily representative of all consumers' experiences with a financial product or company."
  • "We do not adopt their views or verify that their experiences are accurate or unbiased." (on complaint narratives)
  • "The lack of complaints or a relatively low number of complaints published in the database about a product, issue, or company does not necessarily mean there is little or no consumer harm."
  • "When looking at complaint volume about a company or product, consider company size and/or market share. For example, companies with more customers may have more complaints than companies with fewer customers."
  • "When looking at complaint volume for a state or ZIP code, consider the population in that geography."

Publication criteria, same page: "Only complaints sent to companies for response are eligible to be published and are only published after the company responds, confirming a commercial relationship or after 15 days, whichever comes first." And: "We do not publish complaints referred to other regulators, such as complaints about depository institutions with less than $10 billion in assets." A complaint in this database is therefore a routed consumer submission with a confirmed or unanswered commercial relationship, not a verified allegation and not a finding of wrongdoing.

Pipeline

  1. The bulk zip stages on the external raw estate (the CSV is ~9GB; data/raw/cfpb_complaints/SOURCE.md points to it). The build aborts loudly if the drive is absent.
  2. scripts/build_cfpb_complaints.py streams the CSV in one single-threaded pass (the quoted multi-line narrative fields defeat parallel CSV readers) and projects only eight columns: date received, product, sub-product, state, company, company response, timely flag, complaint ID. The consumer narrative, ZIP code, and tags columns are never read into any output: derived files carry counts only.
  3. Duplicate complaint IDs (4,013 in the 2026-07-18 vintage, 0.02%) are deduplicated, keeping the first row per ID by date received. The deduped total behind the current build is 17,006,331.
  4. Aggregates are written to parquet, and every aggregation grain is asserted to sum to the same deduped total before anything is written: counts by product and sub-product by month, by state by year by product, by company response and timely flag by year, and top respondents by year and by product-year.

Conventions

  • Dates are the complaint's Date received. Months and years are calendar periods of that field.
  • Partial-period rule: the bulk file refreshes daily, so its newest calendar month is virtually always incomplete. Every monthly chart and "latest month" figure cuts at the last full month; annual figures cut at the last full calendar year. The collection opened 2011-12-01, so 2011 is a one-month stub year and annual series start at 2012.
  • Product families: the complaint form's product labels changed several times (there are 23 distinct historical product labels in the file). Charts group them into six families. The mapping, exactly as implemented:
    • Credit reporting: every product label starting with "Credit reporting" ("Credit reporting"; "Credit reporting, credit repair services, or other personal consumer reports"; "Credit reporting or other personal consumer reports"). The standalone "Credit repair services" label is not included.
    • Debt collection: "Debt collection".
    • Credit card or prepaid: "Credit card or prepaid card", "Credit card", "Prepaid card".
    • Mortgage: "Mortgage".
    • Bank account: "Checking or savings account", "Bank account or service".
    • All other products: every other label (student loans, vehicle loans, money transfer and virtual currency services, payday and personal loans, and the smaller legacy labels).
  • Per-capita rates divide a state's complaints in a calendar year by the US Census Bureau's annual resident population estimate for that state and year, retrieved from FRED's {ST}POP series (units: thousands of persons, converted to persons in the build). Coverage is the 50 states plus DC: FRED carries no population series for Puerto Rico, the territories, or military post codes, so those rows (and blank-state rows) are excluded from per-capita tables, stated on the page, and always retained in national totals.
  • Respondent concentration uses the exact top-10 respondents per year computed over the full file, and each year's top-3 share divides by that year's total published complaints. Company names appear verbatim as the database records them.
  • Timely response is the database's own "Timely response?" flag (the share of complaints whose company response arrived within the 15 days the CFPB allows). Company response categories are the company's characterization of its own answer, recorded by the Bureau; "Closed with explanation" makes no finding either way.

Known differences vs the CFPB's search tool

The Bureau's public search API serves the same database through a separately maintained index. For settled periods the two agree to well under 0.1% (cross-checks from the current build: full-year 2025 is 5,443,384 in the bulk file vs 5,442,989 via the API, 0.007% apart; Mortgage 2013 is 49,400 vs 49,398). For recent weeks the two diverge materially (June 2026: 784,405 bulk vs 650,433 API at the same retrieval time), consistent with the landing page's warning that recent data "will not yet include all complaints eligible for publication." This layer is built from the bulk export, the fuller of the two surfaces.

Refresh

The Bureau refreshes the bulk file daily; FinObservatory re-pulls it on the quarterly tier of scripts/refresh.sh (the file is ~1.4GB compressed, and the module's series are monthly and annual, so daily re-pulls would buy nothing). The refresh checks that the external staging drive is mounted and skips loudly, without failing the tier, when it is not.

Citation

Consumer Financial Protection Bureau, Consumer Complaint Database, https://www.consumerfinance.gov/data-research/consumer-complaints/ (complaints published through the vintage date shown on the page). Population denominators: US Census Bureau, Annual Estimates of the Resident Population, via FRED (Federal Reserve Bank of St. Louis).