Skip to contents



The NPPES NPI Registry


The NPPES NPI Registry Public Search is a free directory of all active National Provider Identifier (NPI) records. Healthcare providers acquire their unique 10-digit NPIs to identify themselves in a standard way throughout their industry. After CMS supplies an NPI, they publish the parts of the NPI record that have public relevance, including the provider’s name, specialty (taxonomy) and practice address. It enables you to search for providers in the NPPES (National Plan and Provider Enumeration System.) All information produced by the NPI Registry is provided in accordance with the NPPES Data Dissemination Notice. There is no charge to use the NPI Registry.


The NPPES API

The NPPES Application Programming Interface (API) is a faster alternative to the downloadable NPPES data files. It allows systems to access NPPES public data in real-time, rather than through batched uploads. The API retrieves data from NPPES daily. An API query will return a maximum of 200 results per request. The Skip field in the API will let you skip up to 1000 records. By using these two fields with your search criteria, you can get up to a maximum of 1,200 records over six requests.


Searching the NPPES by NPI

The following is a basic example of searching for an NPI using the using prov_npi_nppes(). The only argument is the provider’s 10-digit National Provider Identifier:


# Load package
library(provider)

# Query the NPPES API
nppes_npi_resp <- provider_nppes(1114499340)

# View the results
nppes_npi_resp
#> # A tibble: 1 × 3
#>   datetime            outcome data_lists   
#>   <dttm>              <chr>   <list>       
#> 1 2022-12-11 22:01:52 results <df [1 × 11]>



This returns a tibble containing the NPI that was searched for (search), the datetime of the query, an outcome status column and data_lists, a list-column containing a collection of variables and nested data frames:


#> Error in loadNamespace(x): there is no package called 'lobstr'



The variables returned are as follows:

#> Error in loadNamespace(x): there is no package called 'lobstr'



  • created_epoch: date when the NPI was issued
  • enumeration_type: NPI Entity Type, either NPI-1(individual) or NPI-2(organizational)
  • last_updated_epoch: date when the information was last updated by the provider
  • number: provider’s NPI, as listed in the NPPES


Note: last_updated_epoch / created_epoch are dates in Unix time. They are repeated in the basic list as last_updated and enumeration_date, respectively.


The list-columns returned are as follows:


#> Error in loadNamespace(x): there is no package called 'lobstr'



  • basic: Basic information
  • other_names: Other names used by the provider
  • addresses: Primary location and mailing addresses
  • taxonomies: Specialty classification and related information
  • identifiers: Various other identifiers such as payer-specific IDs, CLIA numbers, etc.
  • practice_locations: Provider’s practice locations
  • endpoints: Information regarding the provider’s endpoints for Health Information Exchange (HIE)



Note: All list-columns may not be available for every provider. This depends on factors such as NPI type, incorporated individual status, subpart delegation, and, quite simply, the information provided to the NPPES by providers.



Unpack the Response


Flatten the response tibble into a tidy data format with prov_nppes_unpack():


provider_unpack(nppes_npi_resp)
#> # A tibble: 2 × 34
#>   npi     prov_…¹ first…² last_…³ crede…⁴ sole_…⁵ gender enume…⁶ last_…⁷ certi…⁸
#>   <chr>   <chr>   <chr>   <chr>   <chr>   <lgl>   <chr>  <chr>   <chr>   <chr>  
#> 1 111449… Indivi… DOMINI… LOUDA   DPT     FALSE   M      2018-1… 2020-0… 2020-0…
#> 2 111449… Indivi… DOMINI… LOUDA   DPT     FALSE   M      2018-1… 2020-0… 2020-0…
#> # … with 24 more variables: status <chr>, name_prefix <chr>,
#> #   country_code <chr>, address_purpose <chr>, address_1 <chr>, city <chr>,
#> #   state <chr>, postal_code <chr>, telephone_number <chr>, taxon_code <chr>,
#> #   taxonomy_group <chr>, taxon_desc <chr>, taxon_state <chr>,
#> #   taxon_license <chr>, taxon_primary <lgl>, pract_country_abb <chr>,
#> #   pract_country_name <chr>, pract_address_purpose <chr>,
#> #   pract_address_type <chr>, pract_address_1 <chr>, pract_city <chr>, …



Search for Multiple NPIs


To search for more than one NPI at a time, there are two options, the first of which is to create an unnamed list (using c() as opposed to list()) of said NPIs:


npi_list <- c(1336413418, 1710975040, 1659781227, 1528060837)


Then pipe the list into prov_npi_nppes() using purrr’s map_dfr() function to loop over the list:


npi_list_resp <- npi_list |> purrr::map_dfr(provider_nppes)
#> # A tibble: 4 × 3
#>   datetime            outcome data_lists      
#>   <dttm>              <chr>   <list>          
#> 1 2022-12-11 22:01:53 results <df [1 × 11]>   
#> 2 2022-12-11 22:01:55 results <df [1 × 11]>   
#> 3 2022-12-11 22:01:55 Errors  <tibble [1 × 2]>
#> 4 2022-12-11 22:01:56 results <df [1 × 11]>


Unpacking the response is similar, adding only dplyr’s group_split() function to the chain. The parameter entered into group_split() must be search.


npi_list_resp |> dplyr::group_split(outcome) |> 
                 purrr::map_dfr(provider_unpack)
#> # A tibble: 33 × 42
#>    npi    prov_…¹ organ…² organ…³ enume…⁴ last_…⁵ certi…⁶ status autho…⁷ autho…⁸
#>    <chr>  <chr>   <chr>   <chr>   <chr>   <chr>   <chr>   <chr>  <chr>   <chr>  
#>  1 16597… Deacti… NA      NA      NA      NA      NA      NA     NA      NA     
#>  2 13364… Organi… LUMINU… NO      2012-0… 2020-0… 2020-0… A      Laurel  Gamage 
#>  3 13364… Organi… LUMINU… NO      2012-0… 2020-0… 2020-0… A      Laurel  Gamage 
#>  4 17109… Indivi… NA      NA      2005-1… 2007-0… NA      A      NA      NA     
#>  5 17109… Indivi… NA      NA      2005-1… 2007-0… NA      A      NA      NA     
#>  6 17109… Indivi… NA      NA      2005-1… 2007-0… NA      A      NA      NA     
#>  7 17109… Indivi… NA      NA      2005-1… 2007-0… NA      A      NA      NA     
#>  8 17109… Indivi… NA      NA      2005-1… 2007-0… NA      A      NA      NA     
#>  9 17109… Indivi… NA      NA      2005-1… 2007-0… NA      A      NA      NA     
#> 10 17109… Indivi… NA      NA      2005-1… 2007-0… NA      A      NA      NA     
#> # … with 23 more rows, 32 more variables:
#> #   authorized_official_middle_name <chr>,
#> #   authorized_official_telephone_number <chr>,
#> #   authorized_official_title_or_position <chr>,
#> #   authorized_official_name_prefix <chr>, first_name <chr>, last_name <chr>,
#> #   middle_name <chr>, credential <chr>, sole_proprietor <lgl>, gender <chr>,
#> #   name_prefix <chr>, name_suffix <chr>, country_code <chr>, …


Searching for a column of NPIs in a data frame is only slightly different. I’ll create a data frame with the same NPIs in the list example:


npi_df <- data.frame(npi = c(1336413418, 1710975040, 1659781227, 1528060837))
#>          npi
#> 1 1336413418
#> 2 1710975040
#> 3 1659781227
#> 4 1528060837


We can even chain the search and unpack function chains together using |>, R’s lovely pipe. Again, the group_split() input should be the column containing the NPIs.

Split and purrr, then split and purrr:

npi_df_resp <- npi_df |> tibble::deframe() |> 
                         purrr::map_dfr(provider_nppes) |> 
                         dplyr::group_split(outcome) |> 
                         purrr::map_dfr(provider_unpack)
#> # A tibble: 33 × 42
#>    npi    prov_…¹ organ…² organ…³ enume…⁴ last_…⁵ certi…⁶ status autho…⁷ autho…⁸
#>    <chr>  <chr>   <chr>   <chr>   <chr>   <chr>   <chr>   <chr>  <chr>   <chr>  
#>  1 16597… Deacti… NA      NA      NA      NA      NA      NA     NA      NA     
#>  2 13364… Organi… LUMINU… NO      2012-0… 2020-0… 2020-0… A      Laurel  Gamage 
#>  3 13364… Organi… LUMINU… NO      2012-0… 2020-0… 2020-0… A      Laurel  Gamage 
#>  4 17109… Indivi… NA      NA      2005-1… 2007-0… NA      A      NA      NA     
#>  5 17109… Indivi… NA      NA      2005-1… 2007-0… NA      A      NA      NA     
#>  6 17109… Indivi… NA      NA      2005-1… 2007-0… NA      A      NA      NA     
#>  7 17109… Indivi… NA      NA      2005-1… 2007-0… NA      A      NA      NA     
#>  8 17109… Indivi… NA      NA      2005-1… 2007-0… NA      A      NA      NA     
#>  9 17109… Indivi… NA      NA      2005-1… 2007-0… NA      A      NA      NA     
#> 10 17109… Indivi… NA      NA      2005-1… 2007-0… NA      A      NA      NA     
#> # … with 23 more rows, 32 more variables:
#> #   authorized_official_middle_name <chr>,
#> #   authorized_official_telephone_number <chr>,
#> #   authorized_official_title_or_position <chr>,
#> #   authorized_official_name_prefix <chr>, first_name <chr>, last_name <chr>,
#> #   middle_name <chr>, credential <chr>, sole_proprietor <lgl>, gender <chr>,
#> #   name_prefix <chr>, name_suffix <chr>, country_code <chr>, …



At 46 columns wide, this output is fairly unwieldy, so it really depends on what you need to do with the data.

#> Error in loadNamespace(x): there is no package called 'kableExtra'





Constraints for Invalid Parameters

Several constraints have been implemented so as to not batter the NPPES API with invalid searches. The input must be 10 digits or the function will throw an error:



provider_nppes(123456789)
#> # A tibble: 1 × 3
#>   datetime            outcome data_lists      
#>   <dttm>              <chr>   <list>          
#> 1 2022-12-11 22:02:01 Errors  <tibble [1 × 2]>



It also must be a valid NPI. The prov_npi_nppes() function checks for validity using the Luhn algorithm, as per it’s implementation by CMS (documented here):



provider_nppes(1234567891)
#> # A tibble: 1 × 3
#>   datetime            outcome data_lists
#>   <dttm>              <chr>   <list>    
#> 1 2022-12-11 22:02:03 results <list [0]>



Inputting letters will throw an error:



provider_nppes(abcdefghij)
#> Error in list2(...): object 'abcdefghij' not found



The Luhn check will catch any letters wrapped in quotes:



provider_nppes("abcdefghij")
#> # A tibble: 1 × 3
#>   datetime            outcome data_lists      
#>   <dttm>              <chr>   <list>          
#> 1 2022-12-11 22:02:04 Errors  <tibble [1 × 2]>



But will let quoted numbers through:



provider_nppes("123456789")
#> # A tibble: 1 × 3
#>   datetime            outcome data_lists      
#>   <dttm>              <chr>   <list>          
#> 1 2022-12-11 22:02:06 Errors  <tibble [1 × 2]>

As long as they’re 10 digits long.



API Documentation Overview


This section outlines the NPPES API’s documentation, going over valid search inputs, constraints to those inputs, and descriptions of the many possible outputs.




Terminology Notes



Enumeration Type


Two categories of health care providers exist for NPI enumeration purposes: Entity Type 1 (Individual) and Entity Type 2 (Organizational).

NPI-1: Individual

Individual health care providers (including sole proprietors) may get an NPI as Entity Type 1. As a sole proprietor, they must apply for the NPI using your own SSN, not an Employer Identification Number (EIN) even if they have an EIN. As a sole proprietor, they may get only one NPI, just like any other individual.

The following factors do not affect whether a sole proprietor is an Entity Type 1:

  • Number of different office locations
  • Whether they have employees
  • Whether the IRS issued them an EIN.

An incorporated individual is a single health care provider who forms and conducts business under a corporation. A sole proprietor is not an incorporated individual because the sole proprietor didn’t form a corporation. If you’re a sole/solo practitioner, it doesn’t necessarily mean you’re a sole proprietor, and vice versa. If you’re an individual health care provider who’s incorporated, you may need to get an NPI for yourself (Entity Type 1) and an NPI for your corporation or LLC (Entity Type 2).


NPI-2: Organizational


Organizational health care providers are group health care providers eligible for NPIs as Entity Type 2. Organization health care providers may have a single employee or thousands of employees. An example is an incorporated individual who is an organization’s only employee.

Some organization health care providers are made up of parts that work somewhat independently from their parent organization. These parts may offer different types of health care or offer health care in separate physical locations. These parts and their physical locations aren’t themselves legal entities but are part of the organization health care provider (which is a legal entity). The NPI Final Rule refers to the parts and locations as subparts.

An organization health care provider can get its subparts their own NPIs. If a subpart conducts any HIPAA standard transactions on its own (separately from its parent), it must get its own NPI. Subpart determination makes sure that entities within a covered organization are uniquely identified in HIPAA standard transactions they conduct with Medicare and other covered entities. For example, a hospital offers acute care, laboratory, pharmacy, and rehabilitation services. Each of these subparts may need its own NPI because each sends its own standard transactions to one or more health plans.

Subpart delegation doesn’t affect Entity Type 1 health care providers. As individuals, these health care providers can’t choose subparts and are not subparts.


Authorized Official


An appointed official (e.g., chief executive officer, chief financial officer, general partner, chairman of the board, or direct owner) to whom the organization has granted the legal authority to enroll it in the Medicare program, to make changes or updates to the organization’s status in the Medicare program, and to commit the organization to fully abide by the statutes, regulations, and program instructions of the Medicare program. See 42 CFR 424.502 for additional information.