NPI API

Look up any U.S. provider by NPI number.

One REST API to validate NPIs, fetch the full NPPES record for a provider or organization, and search by name, specialty or location. Clean JSON, no CSV parsing, 9.8 million records, refreshed from CMS weekly.

Free plan: 1,000 requests a month, all five endpoints, no credit card.

GET /v1/providers/1234567893 200 OK
{
  "data": {
    "npi": "1234567893",
    "entity_type": "individual",
    "name": {
      "first": "Jane",
      "middle": "A",
      "last": "Smith",
      "credential": "MD"
    },
    "organization_name": null,
    "status": "active",
    "enumeration_date": "2008-04-14",
    "last_updated": "2026-08-21",
    "deactivation_date": null,
    "primary_taxonomy": {
      "code": "207RC0000X",
      "description": "Cardiovascular Disease Physician"
    },
    "practice_address": {
      "address_1": "123 Main St",
      "address_2": null,
      "city": "Traverse City",
      "state": "MI",
      "postal_code": "49684",
      "country": "US"
    }
  },
  "meta": {
    "source": "CMS NPPES"
  }
}

Example response. The provider shown is fictional.

How to look up an NPI with the API

Send a GET request to /v1/providers/{npi} with your API key. You get back the provider's NPPES record as JSON: name or organization name, status, primary taxonomy, practice address and the dates that matter. Call it from your server, not from browser code, so your key stays private.

curl
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.npilayer.com/v1/providers/1234567893"
JavaScript
async function lookupNpi(npi) {
  const res = await fetch(`https://api.npilayer.com/v1/providers/${npi}`, {
    headers: { Authorization: 'Bearer YOUR_API_KEY' },
  });
  const body = await res.json();
  if (!res.ok) throw new Error(`${body.error.code}: ${body.error.message}`);
  return body.data;
}
Python
import requests

def lookup_npi(npi):
    res = requests.get(
        f"https://api.npilayer.com/v1/providers/{npi}",
        headers={"Authorization": "Bearer YOUR_API_KEY"},
        timeout=10,
    )
    body = res.json()
    if not res.ok:
        raise ValueError(f"{body['error']['code']}: {body['error']['message']}")
    return body["data"]

Every endpoint takes the same header. See authentication for the details, and the provider endpoint reference for PHP examples and every parameter.

What the NPI API returns

The lookup response has a data object and a meta object whose source is always CMS NPPES. These are the fields in data:

FieldTypeWhat it holds
npistringThe 10-digit NPI
entity_typestringindividual (Type 1) or organization (Type 2)
nameobject or nullFor individuals: first, middle, last and credential. Null for organizations
organization_namestring or nullThe organization's name. Null for individuals
statusstringactive or deactivated
enumeration_datedateWhen the NPI was assigned
last_updateddateWhen the NPPES record last changed
deactivation_datedate or nullSet when the NPI has been deactivated
primary_taxonomyobject or nullThe provider's primary taxonomy code and its description
practice_addressobject or nullPrimary practice location: address_1, address_2, city, state, postal_code, country

The source data has more than 300 columns, with taxonomy and address fields that repeat many times. We turn that into one consistent shape, so your code reads data.primary_taxonomy.code and not column 48 of a CSV.

Validate an NPI before you look it up

An NPI's last digit is a check digit, so a mistyped number can be caught without a database lookup. The API does that check for you and tells you exactly what went wrong, using four different outcomes instead of one generic error:

What you sentHTTP statusResponse
Not exactly 10 digits400invalid_npi_format
10 digits, but the check digit fails422invalid_npi_checksum
Well formed, but not in NPPES404provider_not_found
Found200The record, with status set to active or deactivated

For example, 1234567890 is ten digits but fails the check:

GET /v1/providers/1234567890 422
{
  "error": {
    "code": "invalid_npi_checksum",
    "message": "This NPI fails the standard NPI check-digit validation and cannot be a real, assigned NPI."
  }
}

Passing the check digit does not mean an NPI has been assigned, which is why there is a separate 404. 1234567893 passes the check but is not a real provider, so it returns:

GET /v1/providers/1234567893 404
{
  "error": {
    "code": "provider_not_found",
    "message": "No provider was found for the supplied NPI."
  }
}

Want the check-digit method itself? It is explained step by step in what an NPI is and how the check digit works.

NPI API endpoints

EndpointUse it to
GET /v1/providers/{npi}Look up one provider or organization by NPI. Docs
GET /v1/providers/searchSearch by name, organization, city, state, ZIP, specialty or taxonomy code. Docs
GET /v1/providers/nearbyFind providers within a radius of a ZIP code, nearest first. Docs
GET /v1/taxonomiesList the taxonomy (specialty) codes. Docs
GET /v1/taxonomies/searchSearch taxonomy codes by keyword or code. Docs

Search filters combine. Use name, first_name, last_name or organization, narrow by city, state or zip, and filter by specialty (a text match) or taxonomy (an exact code). Results are paged: 25 per page by default and up to 100.

curl
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.npilayer.com/v1/providers/search?last_name=smith&state=MI"
Example response 200 OK
{
  "data": [
    {
      "npi": "1234567893",
      "entity_type": "individual",
      "name": "Jane Smith, MD",
      "specialty": "Cardiovascular Disease Physician",
      "city": "Traverse City",
      "state": "MI",
      "status": "active"
    }
  ],
  "meta": {
    "count": 1,
    "has_more": false,
    "page": 1,
    "limit": 25
  }
}

The nearby endpoint adds a radius and returns each provider's distance_miles, nearest first:

curl
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.npilayer.com/v1/providers/nearby?zip=49684&taxonomy=207RC0000X&radius=25"
Example response 200 OK
{
  "data": [
    {
      "npi": "1234567893",
      "entity_type": "individual",
      "name": "Jane Smith, MD",
      "specialty": "Cardiovascular Disease Physician",
      "city": "Traverse City",
      "state": "MI",
      "status": "active",
      "distance_miles": 3.8
    },
    {
      "npi": "1234567901",
      "entity_type": "individual",
      "name": "Robert Lee, DO",
      "specialty": "Cardiovascular Disease Physician",
      "city": "Traverse City",
      "state": "MI",
      "status": "active",
      "distance_miles": 7.2
    }
  ],
  "meta": {
    "count": 2,
    "page": 1,
    "limit": 25,
    "radius_miles": 25,
    "center": {
      "zip": "49684"
    }
  }
}

Three things to know about how matching works:

  • Distances are approximate. They are measured from the center of the ZIP code, not from a street address.
  • Filters match the primary taxonomy only. A provider who lists a specialty only as a secondary code does not match it, as explained here.
  • Use the taxonomy code for exact results. The specialty filter is a plain text match against NUCC's names, and NUCC calls general cardiology “Cardiovascular Disease”. A search for specialty=cardiology therefore finds only the subspecialties with that word in their name. The exact code 207RC0000X finds them all. Look up the code for any specialty.

Where the data comes from

NPILayer is built on the National Plan and Provider Enumeration System (NPPES), which CMS publishes as a full file each month and incremental updates each week. We import both into our own database. Your requests are answered from that copy, not from a live call to CMS, so lookups do not depend on anyone else's uptime.

The data is current as of September 13, 2026. Deactivated NPIs stay in the data with status set to deactivated, so you can flag them instead of failing to find them.

Provider information is based on publicly available NPPES data. An NPI does not indicate licensure, credentialing, network participation, or current practice status. The API is not a credentialing or primary-source verification service.

NPI API pricing

Plans are priced by request volume, and there are no per-record fees. Each plan has a monthly request quota, shared by all the keys on your account, and a per-minute burst limit that protects the service from runaway scripts.

Plan Price Requests per month Requests per minute
Free Free 1,000 30
Developer $29 per month, or $290 per year 25,000 60
Pro $149 per month, or $1,490 per year 250,000 300
Scale $499 per month, or $4,990 per year 1,000,000 1,000

If you go over your monthly quota, the API returns a 429 with the code monthly_quota_exceeded and the time it resets. Going over the per-minute limit returns a 429 with burst_rate_limit_exceeded and a Retry-After header. See rate limits for the details, or the full pricing page.

Get free API key

Frequently asked questions

Is there a free NPI API?

Yes. The free plan includes 1,000 requests a month and all five endpoints. You need an account and an API key, but not a credit card.

How do I authenticate?

Send your key as Authorization: Bearer YOUR_API_KEY. You can create and revoke keys from your dashboard, and a key is shown only once when you create it.

Does the API check that an NPI is valid?

It checks the format and the check digit, then looks the number up in NPPES. Each outcome has its own status and error code, as shown above. A number can pass the check digit and still not be assigned to anyone.

How current is the data?

We import CMS's weekly updates and monthly full file. Records can still lag what a provider has told CMS, because providers maintain their own NPPES information.

Can I use it to verify a provider's license or credentials?

No. An NPI shows that a provider or organization was enumerated by CMS. It does not show licensure, credentialing, network participation or whether they currently practice at an address. Verify those with the licensing board, the health plan or the provider.

Can I just use the free NPI lookup tool instead?

For looking up a provider by hand, yes: the free NPI lookup and provider search need no account. For anything automated, use the API. Our terms do not allow scraping the web tools.

Related