# NMLS Consumer Access Scraper (`automation-lab/nmls-license-registry-scraper`) Actor

Extract public NMLS mortgage company, branch, loan-originator, license, contact, sponsorship, and regulatory-action records.

- **URL**: https://apify.com/automation-lab/nmls-license-registry-scraper.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Real estate
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

## NMLS Consumer Access Scraper

Extract public mortgage company, branch, and mortgage loan originator records from **NMLS Consumer Access**. Search by name or enrich exact NMLS IDs, then export normalized identity, contact, license, branch, sponsorship, and regulatory-action data.

This Actor is built for recurring mortgage-license compliance verification and market research. It initializes the protected NMLS portal autonomously, keeps one coherent US browser session, and returns source-linked records in the default Apify Dataset.

> This is an independent data-extraction tool. It is not affiliated with, endorsed by, or operated by NMLS, CSBS, SRR, or any regulator.

### What can the NMLS Consumer Access scraper do?

- Look up exact company, branch, or individual NMLS IDs.
- Search the NMLS Consumer Access database by legal or trade name.
- Return all three public entity types: `COMPANY`, `INDIVIDUAL`, and `BRANCH`.
- Extract current and prior legal names and trade names.
- Capture public addresses, phone numbers, websites, and email addresses.
- Return state license and registration rows with authorization status.
- Include license numbers, issue dates, status dates, and renewal years when shown.
- Capture company branch counts and sponsored-MLO counts.
- Extract public regulatory-action rows when requested.
- Preserve the official source URL and extraction timestamp on every record.
- Produce stable `nmlsId` and `recordId` fields for warehouse upserts and comparisons.

The Actor focuses on point-in-time public registry extraction. It does not make approval, risk, eligibility, or legal conclusions.

### Who is this Actor for?

#### Mortgage compliance and onboarding teams

Verify a lender, branch, or MLO against the public registry before onboarding. Save the source URL and timestamp with the review record, and independently verify material decisions at the official source.

#### Mortgage recruiters and business-development teams

Find known lenders or originators, review public license coverage, and enrich a CRM record with an NMLS identifier and current public contact data.

#### Fintech and mortgage CRM teams

Use exact-ID runs to refresh existing entities. The stable `recordId` field makes repeated snapshots straightforward to upsert or compare.

#### Market researchers

Compare public lender footprints, active license counts, branch counts, and sponsored-MLO counts across selected entities.

### Why use this NMLS Consumer Access lookup?

The public portal is protected by Cloudflare and a BotDetect access gate. Simple HTTP requests can receive a 403 response, and the search workflow requires a coherent browser session.

This Actor handles that workflow without asking users for a CAPTCHA key:

1. It opens the source through a US Apify datacenter proxy.
2. It initializes a browser session.
3. It solves the five-character access image with bundled local OCR models.
4. It reuses the same proxy identity and cookies for search and detail pages.
5. It normalizes only successfully parsed source records.

There is no automatic residential-proxy fallback. Failed access attempts end explicitly instead of silently returning a false empty dataset.

### Getting started

1. Open the Actor input.
2. Choose **By NMLS ID** for exact enrichment or **By name** for discovery.
3. Enter one or more IDs or names.
4. Optionally restrict results to companies, individuals, or branches.
5. Choose whether to include public regulatory actions.
6. Set the maximum number of unique entities to save.
7. Click **Start**.
8. Open the Dataset tab to download JSON, CSV, Excel, XML, or RSS output.

A good first run is:

```json
{
  "mode": "by_nmls_id",
  "nmlsIds": ["3030"],
  "entityType": "COMPANY",
  "includeDisclosures": true,
  "maxItems": 1
}
```

### Input parameters

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `by_nmls_id` | Exact ID enrichment or name search. |
| `nmlsIds` | string\[] | `["3030"]` | Numeric identifiers used in `by_nmls_id` mode. |
| `names` | string\[] | `[]` | Company, branch, or individual names used in `by_name` mode. |
| `entityType` | string | all | Optional `COMPANY`, `INDIVIDUAL`, or `BRANCH` filter. |
| `includeDisclosures` | boolean | `true` | Include public regulatory-action table rows. |
| `maxItems` | integer | `20` | Maximum unique entities saved, from 1 to 1,000. |

#### Exact NMLS ID lookup

Use this mode when an NMLS ID already exists in your CRM, onboarding record, application, or counterparty list.

```json
{
  "mode": "by_nmls_id",
  "nmlsIds": ["1000011"],
  "entityType": "INDIVIDUAL",
  "includeDisclosures": true,
  "maxItems": 1
}
```

#### Search by lender name

Use name search when the identifier is unknown.

```json
{
  "mode": "by_name",
  "names": ["Rocket Mortgage"],
  "entityType": "COMPANY",
  "includeDisclosures": false,
  "maxItems": 1
}
```

Name searches can return multiple entities. Use `entityType` and `maxItems` to keep the result set relevant and bounded.

### Output fields

Each entity is one row in the default Dataset.

| Field | Meaning |
| --- | --- |
| `recordId`, `nmlsId` | Stable public NMLS identifier. |
| `entityType` | `COMPANY`, `INDIVIDUAL`, or `BRANCH`. |
| `legalName` | Current legal name shown by the source. |
| `priorLegalNames` | Prior legal names, when present. |
| `otherTradeNames` | Current public DBAs or trade names. |
| `priorOtherTradeNames` | Prior DBAs or trade names. |
| `streetAddress`, `mailingAddress` | Public source address text. |
| `phone`, `tollFreePhone`, `fax` | Public contact numbers when listed. |
| `website`, `email` | Public source contact fields. |
| `sponsoredMlosCount` | Company sponsored-MLO count when present. |
| `branchLocationsActiveCount` | Active company branch count when present. |
| `branchLocationsInactiveCount` | Inactive company branch count when present. |
| `stateLicensesActiveCount` | Active license/registration count. |
| `stateLicensesTotalCount` | Total active and inactive license count. |
| `stateLicenses` | Structured jurisdiction license rows. |
| `hasRegulatoryActions` | Source Yes/No flag, or null when not displayed. |
| `regulatoryActions` | Structured public action rows when requested. |
| `sourceUrl` | Official NMLS Consumer Access detail URL. |
| `sourceName` | `NMLS Consumer Access`. |
| `scrapedAt` | UTC extraction timestamp. |

Fields can be null or empty when the source does not publish them for an entity type.

### Example output

This shortened example reflects the current output structure. Long license arrays are abbreviated here.

```json
{
  "recordId": "3030",
  "nmlsId": "3030",
  "entityType": "COMPANY",
  "legalName": "Rocket Mortgage, LLC",
  "priorLegalNames": ["Quicken Loans Inc.", "Quicken Loans, LLC"],
  "streetAddress": "1050 Woodward Avenue Detroit, MI 48226",
  "phone": "800-863-4332",
  "stateLicensesActiveCount": 166,
  "stateLicensesTotalCount": 184,
  "stateLicenses": [
    {
      "regulator": "Alabama",
      "licenseName": "Consumer Credit License",
      "authorizedToConductBusiness": true,
      "licenseNumber": "20979",
      "originalIssueDate": "11/10/2009",
      "status": "Approved",
      "statusDate": "11/10/2009",
      "renewedThrough": "2026"
    }
  ],
  "hasRegulatoryActions": true,
  "sourceUrl": "https://www.nmlsconsumeraccess.org/EntityDetails.aspx/COMPANY/3030",
  "sourceName": "NMLS Consumer Access",
  "scrapedAt": "2026-08-04T02:45:00.000Z"
}
```

### How much does it cost to look up NMLS license records?

The Actor uses pay-per-event pricing:

- **NMLS session start** — one charge per run for the US browser and autonomous access-gate initialization.
- **NMLS entity record** — one charge for each successfully saved entity.

At the Bronze rates of **$0.09 per run** and **$0.0008 per saved entity**, examples are:

| Saved entities | Estimated Bronze total (USD) |
| ---: | ---: |
| 1 | 0.0908 |
| 10 | 0.098 |
| 25 | 0.11 |
| 100 | 0.17 |

Higher Apify subscription tiers receive lower event rates. Failed, duplicate, or rejected detail records do not receive an entity-record charge. Always check the live Pricing tab because Store pricing is the source of truth.

### Recurring compliance snapshots

Apify Schedules can run the same input daily, weekly, or monthly. For a repeatable verification workflow:

1. Keep a bounded list of known NMLS IDs.
2. Schedule the Actor at the required review interval.
3. Export each Dataset to a warehouse or spreadsheet.
4. Upsert on `recordId` and retain `scrapedAt`.
5. Compare license status, authorization, sponsorship, and action fields.
6. Route detected changes to a human reviewer.
7. Verify consequential decisions at `sourceUrl`.

The Actor returns snapshots; it does not itself calculate changes or send alerts.

### CRM and data-pipeline integration

Useful patterns include:

- enrich a lender record from an exact NMLS ID;
- add the official source URL to a counterparty profile;
- update active-license counts during periodic review;
- send Dataset items to Google Sheets, Make, Zapier, or a webhook;
- load JSON output into BigQuery, Snowflake, PostgreSQL, or a data lake;
- compare scheduled snapshots using `nmlsId` as the stable key.

### Run with the Apify API

Replace `YOUR_TOKEN` with an Apify API token.

#### cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~nmls-license-registry-scraper/runs?token=YOUR_TOKEN&waitForFinish=300" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "by_nmls_id",
    "nmlsIds": ["3030"],
    "entityType": "COMPANY",
    "includeDisclosures": true,
    "maxItems": 1
  }'
```

#### JavaScript

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/nmls-license-registry-scraper').call({
  mode: 'by_nmls_id',
  nmlsIds: ['3030'],
  entityType: 'COMPANY',
  includeDisclosures: true,
  maxItems: 1,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python

```python
from apify_client import ApifyClient
import os

client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/nmls-license-registry-scraper').call(run_input={
    'mode': 'by_nmls_id',
    'nmlsIds': ['3030'],
    'entityType': 'COMPANY',
    'includeDisclosures': True,
    'maxItems': 1,
})
items = client.dataset(run['defaultDatasetId']).list_items().items
print(items)
```

### Use with Apify MCP

Add the Actor to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/nmls-license-registry-scraper"
```

#### Claude Desktop setup

Add this JSON to the Claude Desktop MCP configuration:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/nmls-license-registry-scraper"
    }
  }
}
```

#### Cursor setup

In **Cursor Settings → MCP**, add a remote server named `apify` with this URL:

```text
https://mcp.apify.com?tools=automation-lab/nmls-license-registry-scraper
```

#### VS Code setup

Add the same remote URL to your VS Code MCP server configuration:

```json
{
  "servers": {
    "apify": {
      "type": "http",
      "url": "https://mcp.apify.com?tools=automation-lab/nmls-license-registry-scraper"
    }
  }
}
```

Example prompts:

- “Look up company NMLS ID 3030 and summarize its active-license count.”
- “Find Rocket Mortgage in NMLS Consumer Access and return its official record URL.”
- “Extract the public regulatory actions for company NMLS ID 3030.”

### Performance and limits

- The Actor starts one browser and one coherent US proxy session per run.
- The access challenge can require several bounded OCR attempts.
- Company pages with hundreds of license rows are larger and slower than small individual records.
- Results are processed sequentially to preserve session stability and source politeness.
- `maxItems` is capped at 1,000 per run.
- Name search is less precise than exact-ID lookup.
- The portal may update or temporarily block a proxy identity.

For recurring large lists, split work into bounded scheduled inputs rather than one unbounded run.

### Empty results and failures

A legitimate search with no matching NMLS entity succeeds with an empty Dataset and only the start event.

The Actor fails explicitly when:

- the input mode and required list do not match;
- an NMLS ID is malformed;
- a working US proxy session cannot be created;
- Cloudflare blocks the selected session;
- the access challenge cannot be solved within the retry limit;
- every matched detail page fails to parse.

This distinction prevents an access failure from looking like a valid “no records” result.

### Data accuracy and responsible use

NMLS Consumer Access is the authoritative public display for these records, but this Actor provides a best-effort point-in-time extraction. Data can change after a run, source fields can be absent, and parser output can be incomplete during upstream changes.

- Verify material compliance or legal decisions at `sourceUrl`.
- Do not treat `hasRegulatoryActions: false` or an empty array as proof of a clean record.
- Do not use this output as a consumer report or for FCRA-regulated eligibility, adverse-action, employment, credit, insurance, or tenancy decisions.
- Follow applicable privacy, outreach, records-retention, and anti-discrimination rules.
- Respect NMLS Consumer Access terms and use bounded request volumes.
- Avoid unnecessary collection or redistribution of personal information.

### Troubleshooting

#### Why did the run fail before returning data?

Check the run log. A source access or CAPTCHA failure is reported explicitly. Retry later with the same bounded input; do not interpret a failed run as “no NMLS record.”

#### Why did a name search return an unexpected entity?

Use a more specific legal name, set `entityType`, reduce `maxItems`, or switch to exact NMLS ID lookup.

#### Why is a field null?

The portal publishes different fields for companies, branches, and individuals. Null means the value was not displayed or could not be normalized from that page.

#### Why is `regulatoryActions` empty when the source flag is true?

Confirm that `includeDisclosures` is enabled. The source can also expose a high-level flag while detailed rows vary by entity and regulator.

### FAQ

#### Is there a free public NMLS API?

The Actor uses the public NMLS Consumer Access portal. It does not claim an official unrestricted NMLS API.

#### Does it support loan originators?

Yes. Use `entityType: "INDIVIDUAL"` to restrict results to public MLO records.

#### Does it support branches?

Yes. Branch records are returned when the search or exact identifier resolves to an NMLS branch.

#### Can I search an entire state?

Not in this release. The supported inputs are exact NMLS IDs and name searches. State-wide enumeration is not claimed.

#### Does it monitor changes automatically?

The Actor produces snapshots. Use Apify Schedules and compare repeated outputs in your own workflow.

#### Are regulatory-action summaries legal advice?

No. They are normalized public source rows, not legal conclusions. Review the official source and consult qualified professionals when needed.

### Related automation-lab Actors

- [Zillow Mortgage Lenders Scraper](https://apify.com/automation-lab/zillow-mortgage-lenders-scraper) — collect lender marketplace profiles for broader lender-market research.
- [Mortgage Loan Calculator](https://apify.com/automation-lab/mortgage-loan-calculator) — calculate mortgage payment and amortization scenarios after lender research.

These Actors use different sources and should not be treated as substitutes for official NMLS verification.

# Actor input Schema

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

Use NMLS IDs for exact enrichment or names to discover matching entities.

## `nmlsIds` (type: `array`):

Exact numeric NMLS identifiers. Required when lookup mode is By NMLS ID.

## `names` (type: `array`):

Mortgage company, branch, or loan-originator names. Required when lookup mode is By name.

## `entityType` (type: `string`):

Optionally restrict matches to companies, individual mortgage loan originators, or branches.

## `includeDisclosures` (type: `boolean`):

Extract the public regulatory-action rows shown on each NMLS entity page.

## `maxItems` (type: `integer`):

Stop after saving this many unique NMLS entities.

## Actor input object example

```json
{
  "mode": "by_nmls_id",
  "nmlsIds": [
    "3030"
  ],
  "names": [
    "Rocket Mortgage"
  ],
  "entityType": "COMPANY",
  "includeDisclosures": true,
  "maxItems": 20
}
```

# Actor output Schema

## `dataset` (type: `string`):

Identity, contact, license, sponsorship, and regulatory-action records.

# 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": "by_nmls_id",
    "nmlsIds": [
        "3030"
    ],
    "names": [
        "Rocket Mortgage"
    ],
    "entityType": "COMPANY",
    "includeDisclosures": true,
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/nmls-license-registry-scraper").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": "by_nmls_id",
    "nmlsIds": ["3030"],
    "names": ["Rocket Mortgage"],
    "entityType": "COMPANY",
    "includeDisclosures": True,
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/nmls-license-registry-scraper").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 '{
  "mode": "by_nmls_id",
  "nmlsIds": [
    "3030"
  ],
  "names": [
    "Rocket Mortgage"
  ],
  "entityType": "COMPANY",
  "includeDisclosures": true,
  "maxItems": 20
}' |
apify call automation-lab/nmls-license-registry-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=automation-lab/nmls-license-registry-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/vMxswgMeyeJAtwg3f/builds/7dZDAL2amAIGLYSyq/openapi.json
