# NPI Registry Scraper: US Doctors & Healthcare Providers (`tidyharvest/npi-registry-scraper`) Actor

Build lists of US doctors, dentists, clinics and pharmacies from the official CMS NPI Registry: NPI, name, credential, specialty and license, practice address, phone, fax and Direct addresses. Splits big searches past the 1,200 cap. $1.20 per 1,000 providers.

- **URL**: https://apify.com/tidyharvest/npi-registry-scraper.md
- **Developed by:** [TidyHarvest](https://apify.com/tidyharvest) (community)
- **Categories:** Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.20 / 1,000 provider scrapeds

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## NPI Registry Scraper: US Doctors & Healthcare Providers

Build lists of **US doctors, dentists, nurses, therapists, pharmacies, clinics and hospitals** from the official **NPI Registry (NPPES)** published by CMS. Search by **specialty, state, city, ZIP code, name or organization**, or look up NPI numbers, and get clean rows with the **NPI, name, credential, specialty (taxonomy) and license, practice address, phone, fax, mailing address, authorized official, other identifiers and Direct messaging addresses**. **$1.20 per 1,000 providers**, no API key, no login, no proxy.

The NPI Registry API stops at 1,200 results per search. This actor **splits big searches by ZIP code automatically** and merges them without duplicates, so "every dentist in California" works.

### What you can do with it

- **Healthcare lead generation:** every orthodontist, chiropractor, physical therapist or home-health agency in a city or state, with practice address and phone, ready for a CRM.
- **Provider data enrichment and verification:** paste a list of NPI numbers and get back the current name, specialty, license, status and address.
- **Market and territory mapping:** count providers of a specialty by city or ZIP to size a market or plan sales territories.
- **New-practice triggers:** sort by `enumerationDate` to find providers and practices that registered recently.
- **Health-IT and interoperability:** pull organizations' Direct messaging endpoints for referral and records exchange.

### How to use it

1. Enter one or more **specialties** (e.g. `Dentist`, `Family Medicine`) and where: **states**, **cities** or **ZIP codes**. Or search by **name** / **organization name**, or paste **NPI numbers**.
2. Optionally choose **individuals (NPI-1)** or **organizations (NPI-2)**, and turn on **Primary specialty only** for tighter lists.
3. Set **Max providers**, click **Start**, and download JSON, CSV or Excel, or send the results to Sheets, a CRM or Zapier.

### Input

| Field | What it does |
|---|---|
| `taxonomyDescriptions` | Specialties as the NPI Registry names them (`Dentist`, `Cardiology`, `Nurse Practitioner`, `Pharmacy`). Each is searched separately. |
| `states` | 2-letter state codes. A state alone is not a valid search; combine it with a specialty, city, ZIP or name. |
| `cities` | City names, exact as written in addresses. |
| `postalCodes` | 5-digit ZIPs (`02114`) or prefixes (`021*`). |
| `enumerationType` | `NPI-1` individuals, `NPI-2` organizations, or empty for both. |
| `firstName`, `lastName`, `organizationName` | Name search; trailing wildcard after 2 letters (`SMI*`, `MAYO*`). |
| `npiNumbers` | 10-digit NPI numbers to look up or verify. Malformed numbers (wrong length or check digit) are skipped, not fatal; they and the numbers the registry doesn't know are listed in the run's `SUMMARY` record (`invalidNpis`, `notFound`) and cost nothing. |
| `addressPurpose` | Match the practice location (default), mailing address, or either. |
| `primaryTaxonomyOnly` | Keep only providers whose primary specialty matches your terms (dropped rows are not charged). |
| `splitLargeSearches` | Split searches that hit the 1,200 cap by ZIP prefix (default on). |
| `maxItems` | Cap on providers per run (default 100, up to 50,000). Also caps your cost. |

The list fields combine: `["Dentist","Orthodontics"]` x `["Boston","Cambridge"]` runs 4 searches (up to 500 per run). Results are de-duplicated by NPI.

Example:

```json
{ "taxonomyDescriptions": ["Dentist"], "states": ["MA"], "cities": ["Boston"], "enumerationType": "NPI-2", "maxItems": 500 }
```

### Output

One item per provider (NPI):

```json
{
  "npi": "1912857160",
  "enumerationType": "NPI-2",
  "entityType": "Organization",
  "name": "137 S ST SMILE STUDIO PLLC",
  "credential": null, "gender": null, "soleProprietor": null,
  "organizationName": "137 S ST SMILE STUDIO PLLC",
  "authorizedOfficial": {"firstName": "KHUSHBOO", "lastName": "JAIN", "title": "Dentist/owner", "phone": "650-450-1596"},
  "status": "Active",
  "enumerationDate": "2026-01-29", "lastUpdated": "2026-01-29",
  "primaryTaxonomyCode": "122300000X", "primaryTaxonomyDescription": "Dentist",
  "primaryTaxonomyLicense": null, "primaryTaxonomyState": null,
  "taxonomies": [{"code": "122300000X", "description": "Dentist", "primary": true, "license": null, "state": null, "group": "193400000X - Single Specialty Group"}],
  "practiceAddress1": "137 SOUTH ST", "practiceCity": "BOSTON", "practiceState": "MA", "practicePostalCode": "02111-2848",
  "practicePhone": "617-362-8032", "practiceFax": null,
  "mailingAddress1": "137 SOUTH ST FL 2", "mailingCity": "BOSTON", "mailingState": "MA", "mailingPostalCode": "02111-2848",
  "otherPracticeLocations": [], "otherNames": [], "identifiers": [],
  "endpoints": [], "directAddresses": [],
  "url": "https://npiregistry.cms.hhs.gov/provider-view/1912857160",
  "source": "NPPES NPI Registry (CMS)"
}
```

Individuals (NPI-1) also fill `firstName`, `middleName`, `lastName`, `namePrefix`, `nameSuffix`, `credential` (MD, DDS, NP...), `gender` and `soleProprietor`. `identifiers` lists other IDs such as Medicaid numbers; `endpoints` lists Direct messaging, FHIR and other endpoints, and `directAddresses` collects the Direct addresses.

`matchedLocation` (state/city/ZIP searches) is the practice address that matched your search. The registry also matches a provider's other practice locations, so about 1 in 40 rows has its main practice address in another state and an office in yours: `matchedLocation.type` says `primary practice location` or `other practice location`, with that office's address and phone.

### Pricing

Pay per result: **$1.20 per 1,000 providers** (`provider-scraped`). Duplicates, providers removed by **Primary specialty only**, and anything past `maxItems` are never charged. A search that finds nothing ends with a message saying so and costs nothing.

### Limits and notes

- **1,200 per search:** the NPI Registry API returns at most 200 rows per request and 1,200 per search. With `splitLargeSearches` on, a capped search is re-run by ZIP prefix (2, 3, 4, then 5 digits). If one single 5-digit ZIP still has more than 1,200 matches (rare: e.g. "Nurse" in a big hospital ZIP), that bucket is cut at 1,200 and named in the run's `SUMMARY` record (`truncatedBuckets`). Narrow by specialty or provider type to get all of them.
- Specialty search matches any of a provider's taxonomies, not only the primary one; use **Primary specialty only** if you need that.
- Requests are sent one at a time with a short pause, so very large pulls (tens of thousands of providers) take a few minutes.
- Data is what providers reported to NPPES. Addresses and phones are business/practice contacts; some providers update their records rarely (check `lastUpdated`); `status` shows the NPI status.
- No emails are scraped. `directAddresses` are secure clinical messaging addresses providers chose to publish in NPPES.

Data source: the National Plan and Provider Enumeration System (NPPES) NPI Registry, published by the Centers for Medicare & Medicaid Services (CMS) as public information. Each row links back to its NPI Registry page. This actor is not affiliated with or endorsed by CMS, HHS or any U.S. government agency.

# Actor input Schema

## `taxonomyDescriptions` (type: `array`):

Provider specialty as the NPI Registry names it, e.g. `Dentist`, `Family Medicine`, `Cardiology`, `Nurse Practitioner`, `Physical Therapist`, `Pharmacy`, `Home Health`. Each value is searched separately. Matches any of a provider's taxonomies (see 'Primary specialty only').

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

2-letter state codes (`CA`, `NY`, `TX`). Each state is searched separately. A state alone is not enough: combine it with a specialty, city, postal code or name.

## `cities` (type: `array`):

City names (`Boston`, `San Diego`). Each city is searched separately. Exact city name as written in the provider's address.

## `postalCodes` (type: `array`):

5-digit ZIP codes (`02114`) or prefixes (`021` or `021*` = every ZIP starting with 021). Each is searched separately.

## `enumerationType` (type: `string`):

Individuals (NPI-1: doctors, nurses, therapists...) or organizations (NPI-2: clinics, hospitals, pharmacies, group practices).

## `firstName` (type: `string`):

Individual provider's first name. Trailing wildcard allowed after 2 letters (`JO*`).

## `lastName` (type: `string`):

Individual provider's last name. Trailing wildcard allowed after 2 letters (`SMI*`).

## `organizationName` (type: `string`):

Organization (NPI-2) legal or other name. Trailing wildcard allowed (`MAYO*`).

## `npiNumbers` (type: `array`):

Look up specific 10-digit NPI numbers (e.g. to enrich or verify a list). Can be used alone or together with a search.

## `addressPurpose` (type: `string`):

Which address the state/city/postal filters match: the practice location (default), the mailing address, or either.

## `primaryTaxonomyOnly` (type: `boolean`):

Keep only providers whose PRIMARY specialty contains one of your specialty terms (the API also matches secondary specialties). Filtered out providers are not charged.

## `splitLargeSearches` (type: `boolean`):

The NPI Registry API returns at most 1,200 providers per search. When a search hits that cap, automatically re-run it by ZIP-code prefix (90\*, 902\*, 9021\*, 90210) and merge the results without duplicates.

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

Cap on providers for the whole run (default 100, up to 50,000). Also caps your cost.

## Actor input object example

```json
{
  "taxonomyDescriptions": [
    "Dentist"
  ],
  "states": [
    "MA"
  ],
  "cities": [
    "Boston"
  ],
  "enumerationType": "",
  "addressPurpose": "LOCATION",
  "primaryTaxonomyOnly": false,
  "splitLargeSearches": true,
  "maxItems": 100
}
```

# Actor output Schema

## `providers` (type: `string`):

US healthcare providers from the CMS NPPES NPI Registry, one item per NPI.

# 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 = {
    "taxonomyDescriptions": [
        "Dentist"
    ],
    "states": [
        "MA"
    ],
    "cities": [
        "Boston"
    ],
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("tidyharvest/npi-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 = {
    "taxonomyDescriptions": ["Dentist"],
    "states": ["MA"],
    "cities": ["Boston"],
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("tidyharvest/npi-registry-scraper").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 '{
  "taxonomyDescriptions": [
    "Dentist"
  ],
  "states": [
    "MA"
  ],
  "cities": [
    "Boston"
  ],
  "maxItems": 100
}' |
apify call tidyharvest/npi-registry-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tidyharvest/npi-registry-scraper"
        }
    }
}
```

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/ThCtKiuddgRqmDcWl/builds/s3gmmhpdAnPGOSgxV/openapi.json
