gonpi

package module
v0.1.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 10, 2026 License: MIT Imports: 15 Imported by: 0

README

gonpi

Go Reference Go Report Card Go

Unofficial, production-ready Go client for the CMS NPI Registry API v2.1 with retry logic, caching, and batch operations.

Installation

go get github.com/sdsvn/gonpi

Quick Start

package main

import (
    "context"
    "fmt"
    "log"
    
    "github.com/sdsvn/gonpi"
)

func main() {
    client := gonpi.NewClient()
    
    // Get provider by NPI
    provider, err := client.GetProviderByNPI(context.Background(), "1043218118")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Provider: %s %s\n", provider.Basic.FirstName, provider.Basic.LastName)
    
    // Search providers
    results, err := client.SearchProviders(context.Background(), gonpi.SearchOptions{
        LastName: "Smith",
        State:    "CA",
        Limit:    10,
    })
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Found %d providers\n", len(results))
}

Features

  • Simple API: Clean, idiomatic Go interface
  • Production-ready: Exponential backoff retry, configurable timeouts
  • Caching: Optional in-memory cache with TTL
  • Batch operations: Concurrent NPI lookups
  • Full API coverage: All NPI Registry v2.1 search filters
  • OpenTelemetry tracing: Built-in distributed tracing support
  • Well-tested: 86.7% test coverage

Configuration

client := gonpi.NewClient(
    gonpi.WithCache(5 * time.Minute),
    gonpi.WithRetry(gonpi.RetryConfig{
        MaxRetries:   3,
        InitialDelay: 100 * time.Millisecond,
    }),
)
OpenTelemetry Tracing

Tracing is automatically enabled using the global OpenTelemetry tracer. Configure your tracer provider at the application level:

// All client operations are automatically traced
client := gonpi.NewClient()

// Operations include detailed spans
provider, err := client.GetProviderByNPI(ctx, "1043218118")

Traces include spans for:

  • Provider lookups and searches
  • Cache hits/misses
  • Retry attempts with errors
  • HTTP requests with status codes
  • Batch operations

Documentation

Testing

go test -v -cover

Resources

License

MIT License - see LICENSE file for details.


Not affiliated with CMS or the U.S. Department of Health and Human Services.

Documentation

Overview

Package gonpi provides a comprehensive Go client for the CMS NPI Registry API.

The National Provider Identifier (NPI) is a unique identification number for covered health care providers. This library allows you to search and retrieve provider information from the NPI Registry maintained by the Centers for Medicare & Medicaid Services (CMS).

Quick Start

Create a new client and look up a provider by NPI:

client := npiregistry.NewClient()
provider, err := client.GetProviderByNPI(context.Background(), "1043218118")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Provider: %s %s\n", provider.Basic.FirstName, provider.Basic.LastName)

Searching Providers

Search for providers using various filters:

opts := npiregistry.SearchOptions{
    FirstName: "John",
    LastName:  "Smith",
    State:     "CA",
    Limit:     10,
}
providers, err := client.SearchProviders(context.Background(), opts)

Configuration

Configure the client with custom options:

client := gonpi.NewClient(
    gonpi.WithCache(5 * time.Minute),
    gonpi.WithRetry(gonpi.RetryConfig{
        MaxRetries: 3,
        InitialDelay: 100 * time.Millisecond,
    }),
)

Batch Operations

Fetch multiple providers concurrently:

npis := []string{"1043218118", "1003000126"}
results, err := client.GetProvidersByNPIs(context.Background(), npis)

For more information, see the examples directory and README.md.

Index

Constants

View Source
const (
	// DefaultBaseURL is the base URL for the NPI Registry API.
	DefaultBaseURL = "https://npiregistry.cms.hhs.gov/api"

	// DefaultTimeout is the default HTTP client timeout.
	DefaultTimeout = 30 * time.Second

	// DefaultMaxRetries is the default number of retry attempts.
	DefaultMaxRetries = 3

	// DefaultLimit is the default result limit per request.
	DefaultLimit = 10

	// MaxLimit is the maximum allowed result limit.
	MaxLimit = 200

	// MaxResponseBodySize is the maximum size for error response bodies (10MB).
	MaxResponseBodySize = 10 * 1024 * 1024

	// TracerName is the name used for OpenTelemetry tracer.
	TracerName = "github.com/sdsvn/gonpi"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type APIError

type APIError struct {
	StatusCode int
	Message    string
}

APIError represents an error from the NPI Registry API.

func (*APIError) Error

func (e *APIError) Error() string

type APIResponse

type APIResponse struct {
	ResultCount int        `json:"result_count"`
	Results     []Provider `json:"results"`
}

APIResponse represents the full response from the NPI Registry API.

type Address

type Address struct {
	CountryCode     string `json:"country_code"`
	CountryName     string `json:"country_name"`
	AddressPurpose  string `json:"address_purpose"`
	AddressType     string `json:"address_type"`
	Address1        string `json:"address_1"`
	Address2        string `json:"address_2"`
	City            string `json:"city"`
	State           string `json:"state"`
	PostalCode      string `json:"postal_code"`
	TelephoneNumber string `json:"telephone_number"`
	FaxNumber       string `json:"fax_number"`
}

Address represents a mailing or practice address for a healthcare provider. Each provider can have multiple addresses with different purposes (LOCATION, MAILING). The AddressPurpose field indicates whether this is a practice location or mailing address.

type BasicInfo

type BasicInfo struct {
	FirstName                         string `json:"first_name"`
	LastName                          string `json:"last_name"`
	MiddleName                        string `json:"middle_name"`
	Credential                        string `json:"credential"`
	SoleProprietor                    string `json:"sole_proprietor"`
	Gender                            string `json:"gender"`
	EnumerationDate                   string `json:"enumeration_date"`
	LastUpdated                       string `json:"last_updated"`
	Status                            string `json:"status"`
	Name                              string `json:"name"`
	NamePrefix                        string `json:"name_prefix"`
	NameSuffix                        string `json:"name_suffix"`
	OrganizationName                  string `json:"organization_name"`
	OrganizationalSubpart             string `json:"organizational_subpart"`
	AuthorizedOfficialFirstName       string `json:"authorized_official_first_name"`
	AuthorizedOfficialLastName        string `json:"authorized_official_last_name"`
	AuthorizedOfficialMiddleName      string `json:"authorized_official_middle_name"`
	AuthorizedOfficialTelephoneNumber string `json:"authorized_official_telephone_number"`
	AuthorizedOfficialTitleOrPosition string `json:"authorized_official_title_or_position"`
	AuthorizedOfficialCredential      string `json:"authorized_official_credential"`
	CertificationDate                 string `json:"certification_date"`
}

BasicInfo contains basic information about the provider.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the NPI Registry API client.

func NewClient

func NewClient(opts ...ClientOption) *Client

NewClient creates a new NPI Registry API client with optional configuration.

func (*Client) Close

func (c *Client) Close()

Close gracefully shuts down the client and stops background goroutines. Call this when the client is no longer needed to prevent goroutine leaks.

func (*Client) GetProviderByNPI

func (c *Client) GetProviderByNPI(ctx context.Context, npi string) (*Provider, error)

GetProviderByNPI retrieves a provider by NPI number, checking the cache first if the cache is enabled. If no providers are found, nil is returned along with a nil error.

The function returns the first matching provider. If the cache is not enabled, the function will always make an API request.

func (*Client) GetProvidersByNPIs

func (c *Client) GetProvidersByNPIs(ctx context.Context, npis []string) (map[string]*Provider, error)

GetProvidersByNPIs retrieves multiple providers by NPI number in a single batch operation. The function takes a list of NPI numbers and returns a map of successfully fetched providers. If any of the NPI numbers result in an error, the function will return an error containing the first error encountered. The function is designed to be safe for concurrent use and will limit the number of concurrent requests to the API.

func (*Client) SearchProviders

func (c *Client) SearchProviders(ctx context.Context, opts SearchOptions) ([]Provider, error)

SearchProviders searches for providers in the NPI Registry using the provided filters.

The SearchOptions struct defines all available filters for searching providers. All fields are optional and can be combined to narrow search results. At least one search criterion must be provided.

The function returns a slice of Provider structs, which contain detailed information about each provider. If no providers are found, an empty slice is returned along with a nil error.

The function also returns an error if the API request fails or if the response cannot be decoded into a slice of Provider structs.

type ClientOption

type ClientOption func(*Client)

ClientOption allows configuration of the Client.

func WithBaseURL

func WithBaseURL(baseURL string) ClientOption

WithBaseURL sets a custom base URL.

func WithCache

func WithCache(ttl time.Duration) ClientOption

WithCache enables in-memory caching with the specified TTL.

func WithHTTPClient

func WithHTTPClient(httpClient *http.Client) ClientOption

WithHTTPClient sets a custom HTTP client.

func WithRetry

func WithRetry(config RetryConfig) ClientOption

WithRetry configures retry behavior.

func WithTracer

func WithTracer(tracer trace.Tracer) ClientOption

WithTracer sets a custom OpenTelemetry tracer.

type Endpoint

type Endpoint struct {
	EndpointType            string `json:"endpointType"`
	EndpointTypeDescription string `json:"endpointTypeDescription"`
	Endpoint                string `json:"endpoint"`
	Affiliation             string `json:"affiliation"`
	UseDescription          string `json:"useDescription"`
	ContentType             string `json:"contentType"`
	ContentTypeDescription  string `json:"contentTypeDescription"`
	Country                 string `json:"country"`
	CountryName             string `json:"countryName"`
	Address                 string `json:"address"`
	City                    string `json:"city"`
	State                   string `json:"state"`
	Zip                     string `json:"zip"`
}

Endpoint represents an electronic service endpoint for the provider. This includes Direct addresses (secure email), FHIR endpoints, and other methods of electronic health information exchange.

type FlexInt

type FlexInt int64

FlexInt is a custom type that handles flexible JSON unmarshaling for numeric values. The NPI Registry API inconsistently returns epoch timestamps as either strings ("1234567890") or integers (1234567890). FlexInt automatically handles both formats during JSON unmarshaling.

Example usage:

type MyStruct struct {
    Timestamp FlexInt `json:"timestamp"`
}

// Works with both: {"timestamp": "1234567890"} and {"timestamp": 1234567890}
var s MyStruct
json.Unmarshal(data, &s)
fmt.Println(s.Timestamp.Int64()) // 1234567890

func (FlexInt) Int64

func (f FlexInt) Int64() int64

Int64 returns the underlying int64 value of the FlexInt. This is the primary method for accessing the numeric value after unmarshaling.

Example:

var provider Provider
json.Unmarshal(data, &provider)
timestamp := provider.CreatedEpoch.Int64() // Get the epoch as int64

func (*FlexInt) UnmarshalJSON

func (f *FlexInt) UnmarshalJSON(data []byte) error

UnmarshalJSON implements custom unmarshaling to handle both string and integer values. It checks the first byte to determine whether the value is a string or integer, then unmarshals accordingly for optimal performance. Empty strings are treated as 0.

Supported input formats:

  • Integer: 1234567890
  • String number: "1234567890"
  • Empty string: "" (returns 0)

Returns an error if the value cannot be parsed as an integer.

type Identifier

type Identifier struct {
	Code       string `json:"code"`
	Desc       string `json:"desc"`
	Identifier string `json:"identifier"`
	State      string `json:"state"`
	Issuer     string `json:"issuer"`
}

Identifier represents an additional identifier for the provider beyond the NPI. Common identifier types include state licenses, DEA numbers, and other professional credentials issued by various authorities.

type OtherName

type OtherName struct {
	Type             string `json:"type"`
	Code             string `json:"code"`
	Credential       string `json:"credential"`
	FirstName        string `json:"first_name"`
	LastName         string `json:"last_name"`
	MiddleName       string `json:"middle_name"`
	Prefix           string `json:"prefix"`
	Suffix           string `json:"suffix"`
	OrganizationName string `json:"organization_name"`
}

OtherName represents alternative names for the provider.

type PracticeLocation

type PracticeLocation struct {
	Address1        string `json:"address_1"`
	Address2        string `json:"address_2"`
	City            string `json:"city"`
	State           string `json:"state"`
	PostalCode      string `json:"postal_code"`
	CountryCode     string `json:"country_code"`
	CountryName     string `json:"country_name"`
	TelephoneNumber string `json:"telephone_number"`
	FaxNumber       string `json:"fax_number"`
}

PracticeLocation represents a location where the provider practices.

type Provider

type Provider struct {
	Number            string             `json:"number"`
	EnumerationType   string             `json:"enumeration_type"`
	Basic             BasicInfo          `json:"basic"`
	Addresses         []Address          `json:"addresses"`
	Taxonomies        []Taxonomy         `json:"taxonomies"`
	Identifiers       []Identifier       `json:"identifiers"`
	Endpoints         []Endpoint         `json:"endpoints"`
	PracticeLocations []PracticeLocation `json:"practice_locations"`
	OtherNames        []OtherName        `json:"other_names"`
	CreatedEpoch      FlexInt            `json:"created_epoch"`
	LastUpdated       string             `json:"last_updated"`
	LastUpdatedEpoch  FlexInt            `json:"last_updated_epoch"`
}

Provider represents a healthcare provider from the NPI Registry. This struct contains comprehensive information about individual healthcare providers (NPI-1) and organizational healthcare providers (NPI-2) including their basic information, addresses, specialties (taxonomies), identifiers, and endpoints.

Key fields:

  • Number: The unique 10-digit NPI number
  • EnumerationType: "NPI-1" for individuals, "NPI-2" for organizations
  • Basic: Core information (name, credentials, dates)
  • Addresses: Practice and mailing addresses
  • Taxonomies: Specialties and healthcare classifications
  • Identifiers: Additional IDs like state licenses
  • Endpoints: Electronic communication endpoints

func (Provider) FullName added in v0.1.2

func (p Provider) FullName() string

type RetryConfig

type RetryConfig struct {
	// MaxRetries is the maximum number of retry attempts.
	// 0 means no retries. Default: 3.
	MaxRetries int

	// InitialDelay is the delay before the first retry.
	// Default: 100ms.
	InitialDelay time.Duration

	// MaxDelay is the maximum delay between retries.
	// Prevents exponential backoff from growing indefinitely.
	// Default: 5 seconds.
	MaxDelay time.Duration

	// BackoffMultiplier is the factor by which the delay increases after each retry.
	// For example, with a multiplier of 2.0:
	//   Delay = InitialDelay * (BackoffMultiplier ^ retryNumber)
	// Default: 2.0 (exponential backoff).
	BackoffMultiplier float64
}

RetryConfig defines the exponential backoff retry behavior for transient HTTP errors. The client will retry failed requests with increasing delays between attempts.

Example usage:

client := NewClient(
    WithRetry(RetryConfig{
        MaxRetries:        5,
        InitialDelay:      100 * time.Millisecond,
        MaxDelay:          10 * time.Second,
        BackoffMultiplier: 2.0,
    }),
)

Retry logic:

  • Retry #1: waits InitialDelay (100ms)
  • Retry #2: waits InitialDelay * BackoffMultiplier (200ms)
  • Retry #3: waits 400ms
  • Continues up to MaxRetries, capped at MaxDelay

Only retries server errors (5xx) and rate limits (429). Client errors (4xx) are not retried.

type SearchOptions

type SearchOptions struct {
	// Number searches for a specific NPI number (10 digits).
	// When provided, this takes precedence and returns at most one result.
	Number string

	// EnumerationType filters by provider type:
	//   - "NPI-1" or "ind" for individual providers
	//   - "NPI-2" or "org" for organizational providers
	EnumerationType string

	// FirstName searches for individual provider's first name.
	// Supports partial matching (e.g., "John" matches "Johnny").
	// Only applicable for individual providers (NPI-1).
	FirstName string

	// LastName searches for individual provider's last name.
	// Supports partial matching and is case-insensitive.
	// Only applicable for individual providers (NPI-1).
	LastName string

	// OrganizationName searches for organization name.
	// Supports partial matching and is case-insensitive.
	// Only applicable for organizational providers (NPI-2).
	OrganizationName string

	// TaxonomyDescription searches by specialty or healthcare classification.
	// Examples: "Family Medicine", "Cardiology", "Hospital"
	// Supports partial matching (e.g., "Medicine" matches "Family Medicine").
	TaxonomyDescription string

	// AddressPurpose filters by address type:
	//   - "LOCATION" for practice addresses
	//   - "MAILING" for mailing addresses
	// Leave empty to search both address types.
	AddressPurpose string

	// City filters by city name (case-insensitive).
	City string

	// State filters by two-letter state code (e.g., "CA", "NY", "TX").
	// Must be uppercase.
	State string

	// PostalCode filters by ZIP code.
	// Supports 5-digit (e.g., "90210") or 9-digit (e.g., "90210-1234") formats.
	PostalCode string

	// CountryCode filters by two-letter country code (default: "US").
	// Examples: "US", "CA", "MX"
	CountryCode string

	// Limit specifies the maximum number of results to return per request.
	// Valid range: 1-200. Default: 10 if not specified or 0.
	// Values exceeding 200 are automatically capped at 200.
	Limit int

	// Skip specifies the number of results to skip for pagination.
	// Use this with Limit to implement pagination:
	//   - Page 1: Skip=0, Limit=10
	//   - Page 2: Skip=10, Limit=10
	//   - Page 3: Skip=20, Limit=10
	Skip int

	// Pretty formats the JSON response for human readability.
	// Only affects the raw API response; has no effect on returned Go structs.
	Pretty bool
}

SearchOptions defines all available filters for searching providers in the NPI Registry. All fields are optional and can be combined to narrow search results. At least one search criterion must be provided.

Example usage:

opts := SearchOptions{
    FirstName: "John",
    LastName:  "Smith",
    State:     "CA",
    TaxonomyDescription: "Family Medicine",
    Limit:     20,
}
providers, err := client.SearchProviders(ctx, opts)

type Taxonomy

type Taxonomy struct {
	Code          string `json:"code"`
	TaxonomyGroup string `json:"taxonomy_group"`
	Desc          string `json:"desc"`
	State         string `json:"state"`
	License       string `json:"license"`
	Primary       bool   `json:"primary"`
}

Taxonomy represents a provider's specialty or healthcare classification. Each provider can have multiple taxonomies, with one marked as primary. The taxonomy code follows the Healthcare Provider Taxonomy Code Set.

Example taxonomies:

  • 207Q00000X: Family Medicine
  • 208D00000X: General Practice
  • 2084P0800X: Psychiatry & Neurology - Psychiatry

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL