# NPI Append: Match Clinician Names to NPI Numbers (`overlookdata/npimcp-resolve`) Actor

Append NPIs to a list of clinicians by first and last name, state, and city. One row per record: the resolved NPI with a plain match tier, credential, specialty, licensed states, Medicare enrollment, OIG exclusion flag, and active status. Paste records or link a CSV. $5 per 1,000 records.

- **URL**: https://apify.com/overlookdata/npimcp-resolve.md
- **Developed by:** [Aaron Melton](https://apify.com/overlookdata) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$5.00 / 1,000 results

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 Append: Match Clinician Names to NPI Numbers

You have a list of clinicians. You need their NPIs and the compliance and specialty facts that hang off them.

Paste your list (or link a CSV) with first and last names and, ideally, state and city. Get one row back per person: the NPI we resolved, how confident the match is, and the provider's primary credential, specialty, practice location, licensed states, Medicare enrollment, OIG exclusion status, and whether the NPI is still active. Matching runs against our normalized copy of the full NPPES registry (9.4M providers), with nickname handling (Bill finds William) and compound-surname handling (Van Der Berg finds Berg) built in.

### Every record gets an answer, and every answer is billed

Each record you submit returns exactly one row with a `tier` that tells you how much to trust it:

- **A\_state\_city**: one provider fits, and both the state and the city agree. The strongest match.
- **B\_state**: one provider fits and the state agrees, but the city does not (or you gave no city).
- **C\_name\_unique\_nationwide**: only one provider in the country carries this name, but the state you gave does not corroborate it (or you gave none, or the only agreement rests on a nickname or partial surname plus a license in that state). Worth a quick look before you rely on it.
- **ambiguous**: several providers fit equally well. No NPI is returned; turn on `include_alternates` to see up to 3 of them.
- **no\_match**: we searched the registry with your first and last name (and their nickname and surname variants) and found no individual provider carrying it.
- **invalid\_input**: the record could not be searched. The `reasons` field says why: `first_name_missing` (no first name, or fewer than 2 letters), `last_name_missing`, `last_name_no_letters`, or `api_rejected`.

The `reasons` field on searched rows adds detail such as `nickname_variant`, `compound_fallback`, `weak_evidence`, `inactive`, `class_mismatch` (the job title you gave does not fit the provider's specialty), or `no_state_given`.

**Ambiguous, no\_match, and invalid\_input rows are billed like matches.** They are still answers: we looked at that record and are telling you what we found. That includes records without a first name, which cannot be matched and come back as invalid\_input with reason `first_name_missing`. Duplicate records in your list are resolved and billed each time.

### What you get on a matched row

| Field | What it holds |
|---|---|
| `npi` | the resolved 10-digit NPI |
| `first_name`, `middle_name`, `last_name` | the name as NPPES has it |
| `primary_credential`, `credential_raw` | e.g. `MD` parsed from `M.D., FACC` |
| `taxonomy_code`, `taxonomy_classification`, `taxonomy_grouping` | primary specialty |
| `practice_city`, `practice_state`, `practice_postal_code` | practice location |
| `licensed_states` | every state with a license on file |
| `is_active`, `deactivation_date` | NPI status |
| `accepts_medicare` | Medicare enrollment (CMS PECOS) |
| `career_stage` | derived from the enumeration date |
| `nppes_last_updated` | when the provider last updated their record |
| `oig_excluded`, `oig_exclusion_date`, `oig_exclusion_type`, `oig_exclusion_reason` | HHS OIG LEIE exclusion by NPI match |
| `leie_as_of` | the LEIE publication date checked against (same on every row of a run) |
| `n_candidates`, `n_tied`, `reasons` | how the match was decided |

Every row also echoes your input (`id`, `input_first_name`, `input_last_name`, `input_state`, `input_city`, `input_title`) so you can join results back to your list. Rows without a match carry the same fields with nulls; `oig_excluded` is null (not false) when no NPI was resolved.

### OIG screening

Every matched row says whether the resolved NPI appears on the HHS OIG List of Excluded Individuals/Entities (LEIE), with the exclusion date, type, and reason when it does. This checks the LEIE by NPI match only: many older LEIE records carry no NPI, and full screening programs also check SAM.gov and state Medicaid lists. **It complements a full multi-source screening program. It does not replace one.**

### Worked example

Input record:

```json
{"id": "row-1", "first_name": "Alex", "last_name": "Testperson", "state": "TX", "city": "Austin", "title": "Family Nurse Practitioner"}
```

Output row (illustrative values; "Alex" was matched to "Alexandra" through the nickname table):

```json
{
  "id": "row-1",
  "input_first_name": "Alex", "input_last_name": "Testperson",
  "input_state": "TX", "input_city": "Austin", "input_title": "Family Nurse Practitioner",
  "tier": "A_state_city",
  "npi": "1000000001",
  "first_name": "Alexandra", "middle_name": "J", "last_name": "Testperson",
  "primary_credential": "FNP", "credential_raw": "MSN, APRN, FNP-C",
  "taxonomy_code": "363LF0000X",
  "taxonomy_classification": "Nurse Practitioner",
  "taxonomy_grouping": "Advanced Practice Providers",
  "practice_city": "Austin", "practice_state": "TX", "practice_postal_code": "78701",
  "licensed_states": ["TX"],
  "is_active": true, "deactivation_date": null,
  "accepts_medicare": true, "career_stage": "mid",
  "nppes_last_updated": "2026-05-14",
  "oig_excluded": false, "oig_exclusion_date": null,
  "oig_exclusion_type": null, "oig_exclusion_reason": null,
  "leie_as_of": "2026-09-01",
  "n_candidates": 3, "n_tied": 1,
  "reasons": ["nickname_variant"],
  "alternates": []
}
```

### Input

Provide **either** `records` **or** `csv_url`, not both. If both are filled in, the run stops before resolving or billing anything: clear the records field (including the prefilled examples) to use a CSV, or remove the CSV link to use records.

**A first name is required to get a match.** Records need a first name with at least 2 letters and a last name. A record without a usable first name returns `invalid_input` with reason `first_name_missing` and is billed.

- **records**: a JSON list of objects with `first_name` and `last_name`; `middle_name`, `state` (2-letter code or full name), `city`, `title`, and `id` are optional. The more you give, the more records land in the confident tiers.
- **csv\_url**: a link to a CSV file that anyone can download without logging in. It needs a header row with `first_name` and `last_name` columns (the run stops if either is missing); `middle_name`, `state`, `city`, `title`, and `id` columns are used when present, and any other columns are ignored. Headers are matched ignoring case, spaces, and underscores, so `First Name`, `first_name`, and `FIRSTNAME` all work. Files saved by Excel (with a byte-order mark or Windows line endings) are fine.
- **include\_alternates**: when on, ambiguous rows list up to 3 candidate providers with their scores so you can choose by hand.

**Limit: 10,000 records per run.** A longer list is truncated; the run's status message and log say how many records were dropped. Split larger lists across runs.

### Typical uses

- Append NPIs to a CRM or marketing list that only has names and locations
- Add credential, specialty, and Medicare enrollment to a prospect list before a campaign
- Flag contacts whose NPI is deactivated or on the OIG exclusion list

### What this does not do

Matching is by name and location only. It does not use email addresses or phone numbers, and it does not catch a surname change with no shared part (a maiden name replaced entirely). Organizations are not matched; this is for individual clinicians. Registry data is as reported by providers to CMS, not independently checked.

### Use from AI agents (MCP)

This actor works as an MCP tool out of the box. Point your agent at `mcp.apify.com` with this actor enabled and it can resolve names directly ("find the NPIs for these 40 cardiologists in Ohio"). Results land in a dataset your agent can read back.

### Pricing

**$5 per 1,000 records** ($0.005 per row). Every record you submit returns exactly one row, whatever its tier, and each row is one billable event. Appending NPIs to a 2,000-name list costs $10.

### Data source

CMS NPPES registry (public federal data on healthcare providers), with Medicare enrollment from CMS PECOS and exclusions from the HHS OIG LEIE. Provider enrollment data only; no patient data, no HIPAA scope.

# Actor input Schema

## `records` (type: `array`):

Clinicians to match, one JSON object each. A first name (at least 2 letters) and a last name are required to get a match; optional fields are middle\_name, state (2-letter code or full name), city, title (job title, used to prefer the right specialty), and id (your own key, echoed back). A record without a first name is returned as invalid\_input with reason first\_name\_missing, and one without a last name with reason last\_name\_missing; both are still billed. Every record produces exactly one billable result row, including ambiguous, no\_match, and invalid\_input rows. Duplicates are resolved and billed each time. Capped at 10,000 records per run; a longer list is truncated and the dropped count is reported in the run's status message and log. Provide either this field or a CSV link, not both: if both are filled in, the run stops without billing anything, so clear this field (including the prefilled examples) to use a CSV.

## `csv_url` (type: `string`):

A publicly fetchable link to a CSV file with a header row. Required columns: first\_name and last\_name (the run stops if either header is missing). Optional columns: middle\_name, state, city, title, id. Headers are matched ignoring case, spaces, and underscores (First Name, first\_name, and FIRSTNAME all work); other columns are ignored. Same rules as records: a row without a first name returns invalid\_input (first\_name\_missing) and is billed, one billable row per data row, capped at 10,000 rows per run. Clear the records field (including the prefilled examples) when using this, or the run stops.

## `include_alternates` (type: `boolean`):

When several providers fit a record equally well, list up to 3 of them (NPI, score, name, practice city and state, specialty) so you can pick by hand. Does not change billing.

## Actor input object example

```json
{
  "records": [
    {
      "id": "row-1",
      "first_name": "Alexandra",
      "last_name": "Testperson",
      "state": "TX",
      "city": "Austin",
      "title": "Cardiologist"
    },
    {
      "id": "row-2",
      "first_name": "Bill",
      "last_name": "Samplename",
      "state": "OH",
      "title": "Nurse Practitioner"
    },
    {
      "id": "row-3",
      "first_name": "Priya",
      "last_name": "Exampleton-Ray",
      "state": "California"
    }
  ],
  "include_alternates": false
}
```

# Actor output Schema

## `npi_matches` (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 = {
    "records": [
        {
            "id": "row-1",
            "first_name": "Alexandra",
            "last_name": "Testperson",
            "state": "TX",
            "city": "Austin",
            "title": "Cardiologist"
        },
        {
            "id": "row-2",
            "first_name": "Bill",
            "last_name": "Samplename",
            "state": "OH",
            "title": "Nurse Practitioner"
        },
        {
            "id": "row-3",
            "first_name": "Priya",
            "last_name": "Exampleton-Ray",
            "state": "California"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("overlookdata/npimcp-resolve").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 = { "records": [
        {
            "id": "row-1",
            "first_name": "Alexandra",
            "last_name": "Testperson",
            "state": "TX",
            "city": "Austin",
            "title": "Cardiologist",
        },
        {
            "id": "row-2",
            "first_name": "Bill",
            "last_name": "Samplename",
            "state": "OH",
            "title": "Nurse Practitioner",
        },
        {
            "id": "row-3",
            "first_name": "Priya",
            "last_name": "Exampleton-Ray",
            "state": "California",
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("overlookdata/npimcp-resolve").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 '{
  "records": [
    {
      "id": "row-1",
      "first_name": "Alexandra",
      "last_name": "Testperson",
      "state": "TX",
      "city": "Austin",
      "title": "Cardiologist"
    },
    {
      "id": "row-2",
      "first_name": "Bill",
      "last_name": "Samplename",
      "state": "OH",
      "title": "Nurse Practitioner"
    },
    {
      "id": "row-3",
      "first_name": "Priya",
      "last_name": "Exampleton-Ray",
      "state": "California"
    }
  ]
}' |
apify call overlookdata/npimcp-resolve --silent --output-dataset

```

## MCP server setup

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

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/vAvC3tcIRUe1RZ8gi/builds/qBiXLqF2DbNvQB3qF/openapi.json
