Build on AINPI

AINPI for developers

Every number on this site is reachable as JSON. Apache-2.0, no auth on the static files, rate limits on the metered routes. The static /api/v1/* tier is the stable contract: breaking changes bump to /api/v2/ and never change shape in place. Live /api/npd/* and /api/provider-search are read-only against BigQuery + payer FHIR endpoints.

Quick start

Static contract: /api/v1/*

stable

Pre-rendered JSON files. Cache as long as you like. Refreshed weekly via the GitHub Actions cron and on every NDH release.

GET/api/v1/stats.jsonstatic

Site-wide counters: release date, methodology version, total resources, findings tally.

curl -s https://ainpi.dev/api/v1/stats.json | jq
Example response
{
  "release_date": "2026-08-20",
  "methodology_version": "0.7.3-draft",
  "commit_sha": "30ce442",
  "counters": {
    "resources_processed": 32468908,
    "npis_checked": 11607656,
    "npis_flagged": 4,
    "endpoints_live_pct": 85.37,
    "findings_published": 24
  },
  "counters_as_of": { "endpoints_live_pct": "2026-04-09" }
}

Schema: frontend/src/lib/api-v1-types.ts:ApiV1Stats

GET/api/v1/findings/<slug>.jsonstatic

Per-finding detail: headline, numerator, denominator, chart payload, notes. One file per H-series slug.

curl -s https://ainpi.dev/api/v1/findings/pii-exposure-ndh.json | jq
Example response
{
  "slug": "pii-exposure-ndh",
  "title": "Social Security Numbers exposed in the NDH bulk export",
  "hypotheses": ["H27"],
  "status": "published",
  "release_date": "2026-08-20",
  "headline": "41 of 50 flagged Practitioner resources …",
  "numerator": 41,
  "denominator": 7441211,
  "chart": { "type": "bar", "data": [...] }
}

Schema: frontend/src/lib/api-v1-types.ts:ApiV1Finding

GET/api/v1/states/<state>.jsonstatic

State-scoped view: VA, PA, OH (and growing). Denominators, state-vs-national findings table, verify-yourself sample NPIs.

curl -s https://ainpi.dev/api/v1/states/va.json | jq

Schema: frontend/src/lib/api-v1-types.ts:ApiV1State

GET/api/v1/states/va-cohort-critical.csvstatic

Federally-excluded VA-resident NPIs (LEIE or SAM, score ≥ 1.5) with per-NPI verification URLs (LEIE / SAM / NPPES).

curl -s https://ainpi.dev/api/v1/states/va-cohort-critical.csv
GET/api/v1/findings/endpoint-org-crosswalk.csvstatic

Resolved FHIR base URL → organization, for the NDH endpoints that carry a resolvable managing organization. Columns: endpoint_id, base_url, host, status, org_id, org_npi, org_name, org_state. Every row resolves to an org with an NPI, so this doubles as a base-URL-to-NPI lookup. Covers 14.7% of FHIR REST endpoints at the 2026-08-20 release, down from 16.9% at 2026-05-08; the rest have no owner in the directory (H50), and H51 shows public vendor files can name most of them. Direct Trust addresses are excluded, being messaging addresses rather than callable base URLs.

curl -s https://ainpi.dev/api/v1/findings/endpoint-org-crosswalk.csv | head -5
GET/api/v1/manifest.jsonstatic

Discovery manifest: every published finding URL + state slice URL + schema reference. The list AI agents and crawlers should poll.

curl -s https://ainpi.dev/api/v1/manifest.json | jq

Live API: /api/npd/* + /api/provider-search

stable, live

Per-request reads against BigQuery (NDH) + NPPES + 4 payer FHIR directories. Median latency ~400ms; tail latency depends on which payers respond. No auth today.

GET/api/npd/search?npi=<10-digit>stable

NDH-only profile lookup by NPI. Returns Practitioner with name, gender, qualifications, addresses.

curl -s "https://ainpi.dev/api/npd/search?npi=1306378096" | jq
Example response
{
  "type": "provider_profile",
  "data": { "practitioner": { "npi": "1306378096", "family_name": "...", "given_name": "...", "qualifications": [...] } },
  "source": "cms_npd",
  "release_date": "2026-08-20"
}
GET/api/npd/search?family=<name>&state=<XX>stable

Name-based NDH search across Practitioner + Organization + Location.

curl -s "https://ainpi.dev/api/npd/search?family=smith&state=VA"
POST/api/provider-searchstable

Cross-source merged search across NDH (BigQuery) + NPPES + Humana + Cigna + UnitedHealthcare + Molina. Returns a normalized payload with per-source response times and disagreement points.

curl -sX POST https://ainpi.dev/api/provider-search \
  -H "Content-Type: application/json" \
  -d '{"npi":"1306378096"}' | jq
Example response
{
  "ok": true,
  "summary": { "sourcesQueried": 6, "matched": 2, "totalMs": 2562 },
  "results": [
    { "sourceId": "ndh", "category": "directory", "matchCount": 1, "practitioners": [...] },
    { "sourceId": "nppes", "category": "authoritative", ... }
  ]
}
GET/api/npd/data-quality?view=<summary|states|specialties|endpoints>stable

Pre-aggregated data quality breakdown sourced from Supabase (synced from BigQuery weekly).

curl -s "https://ainpi.dev/api/npd/data-quality?view=summary" | jq
GET/api/npd/validationstable

Live BigQuery vs source-manifest reconciliation. Reports per-resource expected/actual/delta + completeness.

curl -s https://ainpi.dev/api/npd/validation | jq

Code samples

Python

import requests
stats = requests.get("https://ainpi.dev/api/v1/stats.json").json()
print(stats["counters"]["resources_processed"], "resources at", stats["release_date"])

# Lookup an NPI
r = requests.get("https://ainpi.dev/api/npd/search", params={"npi": "1306378096"})
print(r.json()["data"]["practitioner"]["family_name"])

TypeScript / Node

import type { ApiV1Stats } from "@ainpi/types"; // optional, vendored from the repo

const stats: ApiV1Stats = await fetch("https://ainpi.dev/api/v1/stats.json").then(r => r.json());
console.log(stats.counters.resources_processed, "resources");

// Cross-source merged search
const res = await fetch("https://ainpi.dev/api/provider-search", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ npi: "1306378096" }),
});

Anthropic Claude tool / function-call

// Define ainpi_lookup_npi as a Claude tool. The model returns provenance-rich
// answers without confabulating.
const tools = [{
  name: "ainpi_lookup_npi",
  description: "Look up a 10-digit NPI in the AINPI cross-source merged registry.",
  input_schema: {
    type: "object",
    properties: { npi: { type: "string", pattern: "^\\d{10}$" } },
    required: ["npi"],
  },
}];

// Tool handler:
async function handle(npi: string) {
  const r = await fetch("https://ainpi.dev/api/provider-search", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ npi }),
  });
  return await r.json();
}

For AI labs · MCP / function-calling

Wire AINPI into Claude / ChatGPT as a grounded provider-data tool

Every endpoint returns provenance-rich payloads (release date, methodology version, commit SHA, source attribution per finding). That makes AINPI a low-confabulation source for "is this provider real / active / federally excluded?" queries inside an LLM tool surface.

Live MCP server (streamable HTTP)

claude mcp add --transport http ainpi https://ainpi.dev/api/mcp

Five tools: list_findings, get_finding, get_state_audit, check_npi_cohort, lookup_npi. No key, no auth. Every response carries provenance (release date, methodology version) and the signals-not-findings disclaimer where flags are involved.

  • Discovery: /api/v1/manifest.json : single GET returns every published finding URL + schema reference.
  • NPI lookup tool: GET /api/npd/search?npi=<10-digit> : returns a single Practitioner profile with FHIR-friendly fields.
  • Cross-source verification tool: POST /api/provider-search : returns per-source agreement / disagreement so the model can surface conflicts to the user instead of collapsing them.
  • Federal-screening tool: /api/v1/findings/oig-leie-exclusions.json and /api/v1/findings/sam-exclusions.json : pre-joined LEIE + SAM cohorts. Use the headline number directly.

A reference MCP server wrapping all five tools is on the roadmap. Until it ships, the REST endpoints above can be pasted directly into Claude tool definitions or OpenAI function-call schemas.

Rate limits

Everything under /api/v1/ is static JSON on a CDN. It is free, needs no key, and is not rate limited, because serving it costs us nothing. The limits below apply only to the live query routes, which run a BigQuery scan per call.

Quota is counted in cost units rather than requests, because the calls are not the same size. Looking up an NPI hits a clustered column and scans about 0.04 GB. Searching a state and city scans about 9.7 GB. Charging both as “one request” would price a bulk extraction like a lookup.

TierUnits/minUnits/dayHow to get it
Anonymous1202,000No key needed.
Free key60020,000Email gene@fhiriq.com with what you are building.
Partner3,000100,000Named integrations. Ask.

Cost per call, measured rather than estimated: NPI lookup 2 units, name search 25, state or city search 27. Pass ?scope=primary on a geography search to match only the provider’s first listed address for 25 units instead of 27. Be aware of what that costs you: 1,095,257 practitioners (14.85%) list addresses in more than one state, and the cheap path will not find them outside their first one.

Send a key as Authorization: Bearer <key> or X-API-Key: <key>. Every response carries x-ratelimit-tier, x-ratelimit-cost-units and x-ratelimit-limit-day. Over the limit returns 429 with a Retry-After header.

There is also a daily ceiling across all callers. If it trips, anonymous requests to the live query routes get 503 while keyed partners keep working, so one stranger’s scraper cannot take out someone else’s integration. The static contract under /api/v1/ is never affected.

License + use rights

  • Code: Apache-2.0. Fork freely.
  • Data: Underlying CMS NDH bulk export is US federal-government public domain. AINPI's aggregated outputs are released under the same terms.
  • Citation: Use CITATION.cff. Pin to a release tag for audit-grade reproducibility.
  • AI inference + redistribution: OK to use AINPI numbers as grounded answers in an LLM, OK to redistribute the static /api/v1/* JSON, OK to embed in product UI. Attribution to AINPI / FHIR-IQ requested but not required.
  • SSN values, individual PII: not redistributed by AINPI even though present in the public NDH file. Don't re-publish them downstream.

Today's posture

AINPI is a research project, not a production SaaS. No auth, no rate limit, no SLA. Numbers are recomputed weekly + on every NDH release; the static contract files are the stable surface. If you're building a paid product on top, file an issue at github.com/FHIR-IQ/AINPI/issues so we can size SLA work and rate-limiting policy.