---
title: "Look up a batch of NPIs in the live NPPES registry"
method: POST
path: "/v1/healthcare/npi/lookup/batch"
tags: ["healthcare"]
---

# Look up a batch of NPIs in the live NPPES registry

`POST /v1/healthcare/npi/lookup/batch`

## Request

- Base URL: `https://api.snipget.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.snipget.ai/v1/healthcare/npi/lookup/batch`
- Auth: none declared

Batch version of /npi/lookup. Max 100 per batch (each is a live NPPES API call). Cost is 2 units per answered item; 1 unit for items rejected by the local Luhn check (NPPES never called); items that fail on an NPPES outage are not billed. Summary carries total/valid/invalid (Luhn verdicts — outage items count in `errors`, never `invalid`) plus found/not_found.

## Query parameters

- `include_receipt` boolean

## Headers

- `authorization` string, nullable
- `X-API-Key` string, nullable

## Request body

- NPILookupBatchRequest
  - `items` union[], required: List of NPI values to look up (max 100 — each is a live API call).
    - union
      - string
      - integer
  - `include_trace` boolean

## Response `200`

Successful Response

- HealthcareNpiLookupBatchResponse: Success envelope for `/v1/healthcare/npi/lookup/batch` — `result` carries this utility's precise shape.
  - `status` string
  - `confidence` number, required
  - `result` object, required
    - `items` object[], required: One entry per input item, in order.
      - `npi` union
        - integer
        - string
      - `is_valid` boolean
      - `nppes_found` boolean
      - `validation` HealthcareNpiLookupBatchNpiValidateResult: Result of :func:`validate`. ``why_invalid`` appears on any invalid verdict — the catalog's shared key, which the descriptions used to call 'reason' without the field existing (parked polish item). Entity type is NOT derivable from the number itself; use npi_lookup (registry data) for that.
        - `npi` integer, nullable, required
        - `is_valid` boolean, required
        - `checksum_valid` boolean, required
        - `input_was_clean` boolean, required
        - `why_invalid` string
      - `provider` HealthcareNpiLookupBatchNpiProvider: Clean provider summary as built by ``_extract_provider``.
        - `name` string, required
        - `entity_type` string, required
        - `enumeration_type` string, required
        - `status` string, required
        - `status_display` string, required
        - `credential` string, nullable, required
        - `sex` string, nullable, required
        - `enumeration_date` string, nullable, required
        - `last_updated` string, nullable, required
        - `taxonomies` HealthcareNpiLookupBatchNpiTaxonomy[], required
          - `code` string, nullable, required
          - `description` string, nullable, required
          - `primary` boolean, required
          - `state` string, nullable, required
          - `license` string, nullable, required
        - `addresses` HealthcareNpiLookupBatchNpiAddress[], required
          - `purpose` string, nullable, required
          - `line_1` string, nullable, required
          - `line_2` string, nullable, required
          - `city` string, nullable, required
          - `state` string, nullable, required
          - `postal_code` string, nullable, required
          - `country` string, nullable, required
          - `phone` string, nullable, required
          - `fax` string, nullable, required
      - `nppes_error` string
      - `input` unknown
      - `confidence` number: Confidence for this item.
    - `summary` object, required: Aggregate counts for the batch. Always carries `total`; adds `valid`/`invalid`, `matched`/`unmatched`, `format_valid`/`format_invalid`, and `exists`/`not_exists` counts when the per-item results carry those booleans, and `errors` (count of per-item rejects) when at least one item was rejected.
  - `meta` ResponseMeta: Metadata attached to every successful response.
    - `version` string
    - `build` string
    - `elapsed_ms` integer
    - `cost_units` integer: Cost units charged for this call: one per item processed (a batch of N items = N; live-registry lookups bill 2 per item). A call is one request; cost units are the billing measure.
    - `request_id` string, nullable
    - `trace` string[], nullable
    - `rate_limit_remaining` integer, nullable
    - `rate_limit_reset` integer, nullable
    - `quota_remaining` integer, nullable: Cost units left in the plan's monthly included bucket before this call's charge lands (None on unlimited tiers).
    - `quota_reset` integer, nullable: Unix timestamp when the monthly included bucket resets.
    - `credit_remaining_usd` number, nullable: Prepaid overage-allowance balance in USD, populated once the included cost units are used up and this call is drawing on it.
    - `receipt` ReceiptMeta: Opt-in execution receipt (ADR #68 B1) — caller-verifiable hashes. Requested with the ``include_receipt=true`` query parameter (free, like ``include_trace``). Reproduce either hash with any RFC 8785 (JCS) library: ``input_hash = sha256(jcs({"tool": <request path>, "args": <your JSON body minus include_trace/include_receipt>}))``; ``result_hash = sha256(jcs(result))``. For deterministic tools (everything except the generators) the same input on the same ``build`` reproduces ``result_hash`` — disputes are honored by replay.
      - `receipt_id` string, required
      - `build` string, required
      - `input_hash` string, nullable
      - `result_hash` string, nullable
      - `error` string, nullable
      - `message` string, nullable

## Other responses

- `400`: Input was syntactically valid but semantically unusable (`INVALID_INPUT`). Not retryable without changing the input.
- `401`: Missing or unrecognized API key. Send it as `Authorization: Bearer <key>` or `X-API-Key: <key>`. Keys are issued at https://snipget.ai.
- `403`: The key is valid but this caller may not use it: the request IP is outside the key's allowlist (`IP_NOT_ALLOWED`) or the account is deactivated (`ACCOUNT_DEACTIVATED`).
- `422`: The request body failed schema validation (`INVALID_REQUEST`). `details` lists the per-field errors. Not retryable without changing the request.
- `429`: Two distinct conditions share this status — branch on `error_code`. **`RATE_LIMITED`** is a per-second throughput throttle: retry after `retry_after_seconds` (the precise float; the `Retry-After` header is the same value rounded up). **`QUOTA_EXCEEDED`** is monthly capacity and is NOT retryable: recovery depends on `limit_type` — upgrade (`monthly_quota`), buy an overage allowance (`included_exhausted`), or top up (`overage_balance_exhausted`).
- `503`: A transient, retryable service condition — branch on `error_code`. **`MAINTENANCE_MODE`**: an admin-toggled maintenance window (auth, portal, and webhook surfaces stay up). **`UPSTREAM_UNAVAILABLE`**: a utility's external data source (PubChem, RxNorm, ClinicalTrials.gov, the FX feed, NPPES…) is down, blocking us, or timed out. **`UPSTREAM_RATE_LIMITED`**: that external source is throttling Snipget (it returned a 429) — the caller's own rate is fine, so this is NOT our `RATE_LIMITED` (429); it's a service-side throttle. All three are retryable; wait for `retry_after_seconds` / the `Retry-After` header when present.

---

[API](https://skmtc.dev/snipget/apis/snipget-api.md) · [All operations](https://skmtc.dev/snipget/apis/snipget-api/llms.txt) · [OpenAPI document](https://skmtc.dev/snipget/apis/snipget-api/revisions/ed1077fb1bcb?raw)
