# NPI Registry - US Healthcare Provider Lookup (`j0401/npi-registry`) Actor

The US NPI registry (public open data, no key): look up any of the ~9M enumerated healthcare providers by NPI number, name, organization, taxonomy or location. Demographics, credential, license numbers, up to 15 taxonomies, every practice location and endpoint.

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

## Pricing

$0.10 / 1,000 npi provider 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

## NPI Registry - US Healthcare Provider Lookup

Every US healthcare provider who bills Medicare or Medicaid has a **National Provider Identifier** - a permanent 10-digit number issued by CMS and never reused. The NPPES registry behind it is public open data: **~9 million providers** (independent trackers put the register at roughly 9.4 million NPI records in mid-2026 - about 7.4 million individuals and 2 million organizations), no login and no key.

This actor turns that registry into a **charged-per-record lookup**: filter providers precisely, pull one provider's complete record by NPI, or roll the matches up by specialty, state or type.

**Built for:** credentialing and enrollment checks, provider-directory builds, sales and territory lists ("every cardiologist in this ZIP"), and payer or health-tech pipelines that need to resolve an NPI to a real, current address and specialty.

### Low cost

**From $0.0001 per record.** Pay-per-event: charged per provider record delivered, nothing for the query.

### A lookup registry, not a download

Nine million providers cannot be pulled wholesale - and no tool can, including this one. The registry answers a *query*, one page of 200 providers at a time, and it **refuses a query with nothing to search on**. There is no "give me everything" switch. What this actor sells is the opposite discipline: **say precisely who you want, and get exactly them**, with a full 10-digit-anchored verification path for the single provider in front of you. Every provider you receive is metered at $0.0001, so cost tracks the list you actually need.

### What one provider record holds

A record is **one provider** - an individual (type 1) or an organization (type 2). It is not one location: the same provider carries its own set of addresses, specialties, licenses, identifiers and endpoints. Across a 400-provider sample the flattened schema spans **92 distinct field paths**, and a single provider populates **30 to 64** of them (median 35).

| Block | What it gives you |
|---|---|
| Identity | `npi`, `enumerationType` (NPI-1 individual / NPI-2 organization), `status` |
| Individual | `firstName`, `middleName`, `lastName`, `namePrefix`, `nameSuffix`, `credential`, `gender`, `soleProprietor` |
| Organization | `organizationName`, `organizationalSubpart`, plus the **authorized official** - name, credential, title and phone |
| Dates | `enumerationDate`, `lastUpdated`, `certificationDate` |
| Addresses | both a **LOCATION** and a **MAILING** address, each fully split into street, city, state, ZIP, country, phone and fax |
| Specialty | the **primary taxonomy** (code, description, group, license number, licensing state) plus every other taxonomy the provider holds |
| Practice sites | every additional practice location, joined into one field |
| Identifiers | the provider's other numbers - Medicaid, Medicare, license ids |
| Other names | former names and DBAs, with the name type |
| Endpoints | the provider's registered endpoints where it has any - Direct messaging, FHIR, CONNECT SOAP and others, with type, use and affiliation |

Providers are deep in a way a bare name list is not. In that same 400-provider sample the busiest single record carried **7 taxonomies**; separate providers carried **5 identifiers**, **3 registered endpoints**, and - an extreme but real case - **110 practice locations** on one NPI. The record is flattened to one row (below), so you get all of it in a single line of output.

### Modes

- **`rows`** (default) - the providers matching your filters, paged 200 at a time.
- **`profile`** - one provider's complete record, given its 10-digit `npi`. The verify path: hand it an NPI, get back everything the registry holds on it.
- **`aggregate`** - a rollup by one dimension (type, status, gender, sole-proprietor, state, country, taxonomy code or taxonomy group) over the providers matching your filters.

### The filtering rules that actually matter

The registry is strict in ways that surprise people, so they are handled for you rather than thrown back raw:

- **You must search on something.** An NPI, a first or last name, an organization name, a taxonomy, a city, a ZIP, or a foreign country. Without one the registry refuses the query.
- **`state` is not enough on its own** - and neither is `enumerationType`. Neither can stand alone, and *they do not rescue each other*: `state=TX` plus `enumerationType=NPI-1` is still refused. Pair them with a city, ZIP, name, taxonomy or NPI.
- **Individuals and organizations cannot be mixed** in one query. `firstName` / `lastName` describe a person; `organizationName` describes an organization. Mixing them is an error, not a union.
- **Taxonomy is a text search, not a code list.** There are ~800 taxonomy codes, so search by description - `Cardiology`, `Internal Medicine`, `Nurse Practitioner` - and wildcards work: `*cardio*`. A description that matches nothing is reported instead of silently returning zero.
- **Names and cities match whole words, not prefixes.** `MAYO CLINIC` finds the organization; `MAYO` on its own finds nothing, and `Dall` does not find `Dallas`. For a partial match, add wildcards - `*clinic*`, `*smith*`.
- **A ZIP matches from the front.** The registry stores ZIPs undashed, as 5 digits on some records and 9 on others, so a 5-digit code finds every provider whose stored ZIP starts with it (both forms), and a full 9-digit code matches those stored at full length. A `78701-4211` form is accepted here and normalized. The value can sit on the **LOCATION**, the **MAILING** address, or a listed **practice location** - a query returns a provider if any of its addresses matches.

A query the registry would refuse is turned into a clear message saying what *is* allowed, before anything is charged. A typo in a state code, a ZIP, a gender or an NPI is caught the same way - the registry itself answers several of those with a silent "no results", which would look like an empty provider list rather than a mistake.

### How the record is flattened

The source nests deeply (addresses, taxonomies, identifiers, other names, endpoints are arrays). Each is flattened to one row so nothing is lost and nothing collides:

- Both addresses are lifted to named columns: `location*` and `mailing*`.
- The **primary** taxonomy is lifted to `primaryTaxonomyCode` / `Desc` / `Group` / `State` / `License`.
- The full arrays are kept as joined strings beside a count - `taxonomyCount` + `taxonomies`, `identifierCount` + `identifiers`, and the same for practice locations, other names and endpoints. **Within one entry the parts are joined by `|`, and entries are separated by `;;`.** So `"207R00000X | Internal Medicine | True | MD | D0000290 ;; ..."` is one taxonomy per `;;` segment.
- Every field is prefixed deliberately (no flattened source field can overwrite an envelope key), the key set is identical in every mode, and a missing value is an empty string rather than a dropped key.

### Examples

**One provider, everything on it** - `mode=profile`, `npi=1003000126`:

```
NPI 1003000126 / NPI-1 / Active / ARDALAN ENKESHAFI
  LOCATION  6410 ROCKLEDGE DR STE 304, BETHESDA MD 208171841
  taxonomy  4 codes, primary 208M00000X | Hospitalist | DC | MD600003480
```

**Every organization in a city** - `city=Austin`, `enumerationType=NPI-2`.

**A specialty in a ZIP** - `taxonomyDescription=Cardiology`, `postalCode=77030`.

**Who is registered under a name** - `lastName=Smith`, `state=TX`.

**How a city's providers break down** - `mode=aggregate`, `groupBy=primaryTaxonomyGroup`.

### Inputs

| Input | What it does |
|---|---|
| `mode` | `rows` (default) / `profile` / `aggregate` |
| `npi` | 10-digit NPI - the profile key, or an exact filter |
| `enumerationType` | `NPI-1` individual / `NPI-2` organization |
| `firstName` / `lastName` / `useFirstNameAlias` | individual providers (nickname-aware) |
| `organizationName` | organization providers |
| `taxonomyDescription` | specialty text search, wildcards allowed |
| `city` / `state` / `postalCode` / `countryCode` | location |
| `groupBy` | the aggregate dimension |
| `maxResults` | cap records (default 200; 0 = up to 2000 per run) |

**Default run = the 200 providers in Austin, TX** - a query that reliably fills a page, so the daily auto-test stays fast. Change `city` / `state` to search anywhere.

### Source

[US NPPES National Provider Identifier registry](https://npiregistry.cms.hhs.gov/) - the Centers for Medicare & Medicaid Services' own public API. No key, no registration. The registry surface answers **active** providers: every one of 1,000 sampled records carried `status` `A`, and deactivated NPIs are not reachable through this endpoint - so this actor's reachable set is the active registry, not all 9.4 million records ever issued. Records are the government's own published fields, and an NPI is an identifier, not a credential or a license.

Each run is integrity-checked against the registry's known shape - a permanent reference NPI must still resolve, and the fields every provider carries must still be populated - so a degraded source fails loudly instead of returning empty or malformed records.

### Related actors

- **FMCSA Motor Carrier** - the DOT-number registry for the trucking industry, the same "verify one number, or filter precisely" shape.
- **CO Business Entities** - the Colorado corporate register with full filing history.

# Actor input Schema

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

rows = providers matching your filters (default). profile = one provider's complete flat record, given npi. aggregate = a rollup by one dimension over the matching providers.

## `npi` (type: `string`):

A 10-digit National Provider Identifier, e.g. '1003000126'. Exact match. In profile mode this is the provider to return; in rows mode it is a normal filter.

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

NPI-1 = an individual practitioner (person). NPI-2 = an organization (hospital, clinic, group practice). Blank = both. Cannot stand alone - combine it with a name, taxonomy, city, ZIP or NPI.

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

An individual provider's first name, matched as a whole word (not a prefix). Add \* for a contains search, e.g. '*john*'. Individual records only - not usable with organizationName.

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

An individual provider's last name, matched as a whole word (not a prefix). Add \* for a contains search, e.g. '*smith*'. Individual records only - not usable with organizationName.

## `useFirstNameAlias` (type: `boolean`):

When on, a first-name search also matches the provider's registered nickname or former name (e.g. 'Bob' finds 'Robert'). Only meaningful with firstName.

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

An organization provider's name, matched as a whole word - 'MAYO CLINIC' matches, 'MAYO' alone does not. Add \* for a contains search, e.g. '*clinic*'. Organization records only - not usable with firstName or lastName.

## `gender` (type: `string`):

NOT USABLE as a filter - the registry accepts it and ignores it (identical rows for any value), so it is rejected rather than charged for. It is still a valid aggregate dimension.

## `taxonomyDescription` (type: `string`):

Free-text specialty search over the healthcare taxonomy descriptions, e.g. 'Cardiology', 'Internal Medicine', 'Nurse Practitioner'. Wildcards work: '*cardio*'. There are ~800 codes, so this is a text search.

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

NOT USABLE as a filter - the registry accepts it and ignores it (identical rows for any value), so it is rejected rather than charged for. Listed only to explain its absence.

## `city` (type: `string`):

City of any of the provider's addresses, e.g. 'Dallas'. Matched as a whole word - 'Dall' does not match 'Dallas'. Blank uses the run default.

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

Two-letter USPS state code, e.g. 'TX', 'CA', 'NY'. Blank = any state. Cannot stand alone - combine it with a city, ZIP, name, taxonomy or NPI.

## `postalCode` (type: `string`):

5 or 9 digits, e.g. '78701' or '787014211' (a '78701-4211' dash form is normalized). A 5-digit ZIP finds every provider whose stored ZIP starts with it; a 9-digit code matches a ZIP stored at full length. Matches the LOCATION, MAILING or practice-location address.

## `countryCode` (type: `string`):

Two-letter ISO country code of the provider's address, e.g. 'CA' for Canada. Leave blank for the US. 'US' cannot stand alone - combine it with another criterion.

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

The dimension the aggregate mode rolls up. The registry has no grouped query, so aggregate scans the first maxResults matching providers and counts them locally.

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

Cap the number of provider records delivered (0 = up to 2000 per run). The registry serves 200 per request and is paged automatically. Each record is metered individually, so there is no per-run charge cap.

## Actor input object example

```json
{
  "mode": "rows",
  "npi": "",
  "enumerationType": "",
  "firstName": "",
  "lastName": "",
  "useFirstNameAlias": false,
  "organizationName": "",
  "gender": "",
  "taxonomyDescription": "",
  "addressPurpose": "",
  "city": "",
  "state": "",
  "postalCode": "",
  "countryCode": "",
  "groupBy": "primaryTaxonomyGroup",
  "maxResults": 50
}
```

# Actor output Schema

## `recordsUrl` (type: `string`):

US NPI provider records or aggregates - as JSON

## `datasetUrl` (type: `string`):

No description

## `runUrl` (type: `string`):

No description

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("j0401/npi-registry").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("j0401/npi-registry").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 '{}' |
apify call j0401/npi-registry --silent --output-dataset

```

## MCP server setup

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

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/3nGosZGISuwkHjgxW/builds/0vXYr8fP1bpgNsuDU/openapi.json
