# CFPB consumer complaints - search API, normalised (`retrainmap/cfpb-consumer-complaints`) Actor

Search the CFPB Consumer Complaint Database (public API, no key) and get one normalised row per complaint: product, issue, company, state, ZIP, company response, timeliness, tags, narrative and link. Filter by term, company, product, state, date range and narrative. Default: last 30 days.

- **URL**: https://apify.com/retrainmap/cfpb-consumer-complaints.md
- **Developed by:** [RetrainMap Team](https://apify.com/retrainmap) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 records

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#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

## CFPB consumer complaints — search API, normalised

One row per complaint from the Consumer Financial Protection Bureau's **Consumer Complaint
Database** (17.5 million complaints on 2026-09-04), read live through the CFPB's public search
API — no key, no scraping. Filter by search term, company, product / sub-product, state, date
received and whether a consumer narrative was published; get the complaint id, dates, product,
issue, company, state, ZIP, company response, timeliness, channel, tags, the narrative (trimmed
to 5,000 characters) and the link to the complaint's page — normalised, with the free-text
`product`, `sub_product` and `company` inputs resolved against the database's own published
names so you do not have to know the exact spelling.

**Complaints are unverified.** The CFPB publishes complaints as consumers submitted them after
the company responds or after 15 days; neither the CFPB nor this Actor verifies the facts alleged.

### Data source and status

| | |
|---|---|
| Publisher | Consumer Financial Protection Bureau (CFPB), Consumer Complaint Database |
| Site | https://www.consumerfinance.gov/data-research/consumer-complaints/ |
| API read by this Actor | `https://www.consumerfinance.gov/data-research/consumer-complaints/search/api/v1/` — plain GET requests; docs at https://cfpb.github.io/api/ccdb/ |
| How it reads | One request resolves product / company names from the API's aggregations, then pages of up to 500 complaints, newest first, keyset-paged with `search_after` (the API's offset paging stops at 10,000; this does not) |
| Refresh cadence | The CFPB refreshes the database **daily** (the API's `_meta.last_updated`, also stored in the run summary). The Actor reads live on every run; `fetched_at` is the fetch time |
| Copyright | Works of the United States Government are not subject to copyright in the U.S. (17 U.S.C. §105); the API additionally labels the data **CC0**. Please cite the CFPB as the source |
| Known caveats | Complaint narratives appear only when the consumer consented and after CFPB scrubbing of personal information; `consumer_consent_provided` and `consumer_disputed` are **not** returned by the search API and are therefore not in the rows (they are in the CFPB's CSV download). Company names must be the published ones — the Actor resolves your text against them and lists the names it used in the run summary |

Identification: every request carries the User-Agent `RetrainMap data actor
info@steelyardclinical.com` and the contact address again in the standard `From:` header. The
CFPB publishes no rate limit; the Actor makes at most one request per 500 ms by default.

### Input

| Field | Type | Meaning |
|---|---|---|
| `search_term` | string | Optional full-text search over all fields (phrases in double quotes) |
| `company` | string | Optional. Word-matched, case-insensitive, against published company names; every match is filtered on exactly |
| `product` | string | Optional. A published product name equal to, or starting with, your text. `Credit reporting` selects the current *Credit reporting or other personal consumer reports* plus the two older credit-reporting products |
| `sub_product` | string | Optional. Same matching, within the matched products (or all products) |
| `state` | string | Optional two-letter consumer state, e.g. `NC` |
| `date_received_from` / `date_received_to` | `YYYY-MM-DD` | Optional. Both empty = the last 30 days |
| `has_narrative` | boolean | On = only complaints with a published narrative |
| `max_records` | integer | Default 500. Stop after this many rows |
| `request_interval_ms` | integer | Default 500 (floor 250) |
| `contact_email` | string | Sent in the `From:` header |

Example — Equifax complaints mentioning identity theft, with narratives, since June 2026:

```json
{ "company": "Equifax", "search_term": "identity theft", "has_narrative": true,
  "date_received_from": "2026-06-01", "max_records": 500 }
```

An unknown product or company fails the run with a message that lists the published names.

### Output (dataset row)

`complaint_id`, `date_received`, `product`, `sub_product`, `issue`, `sub_issue`, `company`,
`state`, `zip` (as published — the CFPB masks ZIPs to `XXXXX` / first three digits in some
cases), `company_response`, `company_public_response`, `timely`, `submitted_via`,
`date_sent_to_company`, `tags` (e.g. *Older American*, *Servicemember*), `has_narrative`,
`complaint_narrative` (first 5,000 characters, `null` when none was published),
`narrative_truncated`, `cfpb_url`, `fetched_at`.

Dates are `YYYY-MM-DD` as published. A run summary (the API's match count, the resolved
product / company filters, the database's `last_updated`, request count) is stored as
`RUN_SUMMARY` in the run's key-value store.

### Pricing (pay per event)

| Event | Price |
|---|---|
| `run-start` — once per run | $0.10 |
| `record` — per row written | $0.005 |

A 500-row pull costs $2.60. Rows stop when your run's maximum charge is reached; the run
summary says so.

### Operator

Steelyard Ventures LLC (RetrainMap) — info@steelyardclinical.com. The Actor writes only to its
own dataset and key-value store; it stores no credentials and sends nothing else.

# Actor input Schema

## `search_term` (type: `string`):

Optional full-text search over every field (the API's search\_term with field=all), e.g. identity theft. Phrases in double quotes.

## `company` (type: `string`):

Optional. Matched case-insensitively against the database's published company names (a word match, so equifax finds EQUIFAX, INC.); every company name that matches is filtered on exactly. The run summary lists the names used.

## `product` (type: `string`):

Optional. Matched case-insensitively against the published product names: a name that equals your text or starts with it matches, so Credit reporting selects the current 'Credit reporting or other personal consumer reports' and the two older credit-reporting products. All matching products are searched. An unknown product fails the run and lists the published names.

## `sub_product` (type: `string`):

Optional. Matched the same way against the sub-products of the matched products (or of every product when product is empty), e.g. Credit reporting, Conventional home mortgage.

## `state` (type: `string`):

Optional two-letter state or territory code of the consumer, e.g. NC.

## `date_received_from` (type: `string`):

Optional. When both dates are empty the Actor uses the last 30 days (today minus 30 days through today, UTC).

## `date_received_to` (type: `string`):

Optional upper bound on the date the CFPB received the complaint.

## `has_narrative` (type: `boolean`):

When on, only complaints whose consumer narrative the CFPB has published are returned (the API's has\_narrative=true). Off = no narrative filter.

## `max_records` (type: `integer`):

Stop after this many complaints have been written. Pages of up to 500 hits, newest first, keyset-paged so any depth works.

## `request_interval_ms` (type: `integer`):

Politeness delay towards consumerfinance.gov, which publishes no rate limit. Each page request returns up to 500 complaints; the floor is 250 ms.

## `contact_email` (type: `string`):

Sent in the standard From: request header so the data owner can reach the operator (RFC 9110 s10.1.2).

## Actor input object example

```json
{
  "product": "Credit reporting",
  "state": "NC",
  "has_narrative": false,
  "max_records": 200,
  "request_interval_ms": 500,
  "contact_email": "info@steelyardclinical.com"
}
```

# Actor output Schema

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

Every matched complaint as one row: id, dates, product, issue, company, state, ZIP, company response, timeliness, channel, tags, narrative, link.

## `results_csv` (type: `string`):

The same rows as a CSV file.

# 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 = {
    "product": "Credit reporting",
    "state": "NC",
    "max_records": 200
};

// Run the Actor and wait for it to finish
const run = await client.actor("retrainmap/cfpb-consumer-complaints").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 = {
    "product": "Credit reporting",
    "state": "NC",
    "max_records": 200,
}

# Run the Actor and wait for it to finish
run = client.actor("retrainmap/cfpb-consumer-complaints").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 '{
  "product": "Credit reporting",
  "state": "NC",
  "max_records": 200
}' |
apify call retrainmap/cfpb-consumer-complaints --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,retrainmap/cfpb-consumer-complaints"
        }
    }
}

```

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/cAafkqKpWt79D1FsP/builds/rEmNv0bRoF3NNzoMr/openapi.json
