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/*
stablePre-rendered JSON files. Cache as long as you like. Refreshed weekly via the GitHub Actions cron and on every NDH release.
/api/v1/stats.jsonstaticSite-wide counters: release date, methodology version, total resources, findings tally.
curl -s https://ainpi.dev/api/v1/stats.json | jqExample 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" }
}/api/v1/findings/<slug>.jsonstaticPer-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 | jqExample 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": [...] }
}/api/v1/states/<state>.jsonstaticState-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/api/v1/states/va-cohort-critical.csvstaticFederally-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/api/v1/findings/endpoint-org-crosswalk.csvstaticResolved 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/api/v1/manifest.jsonstaticDiscovery 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 | jqLive API: /api/npd/* + /api/provider-search
stable, livePer-request reads against BigQuery (NDH) + NPPES + 4 payer FHIR directories. Median latency ~400ms; tail latency depends on which payers respond. No auth today.
/api/npd/search?npi=<10-digit>stableNDH-only profile lookup by NPI. Returns Practitioner with name, gender, qualifications, addresses.
curl -s "https://ainpi.dev/api/npd/search?npi=1306378096" | jqExample response
{
"type": "provider_profile",
"data": { "practitioner": { "npi": "1306378096", "family_name": "...", "given_name": "...", "qualifications": [...] } },
"source": "cms_npd",
"release_date": "2026-08-20"
}/api/npd/search?family=<name>&state=<XX>stableName-based NDH search across Practitioner + Organization + Location.
curl -s "https://ainpi.dev/api/npd/search?family=smith&state=VA"/api/provider-searchstableCross-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"}' | jqExample response
{
"ok": true,
"summary": { "sourcesQueried": 6, "matched": 2, "totalMs": 2562 },
"results": [
{ "sourceId": "ndh", "category": "directory", "matchCount": 1, "practitioners": [...] },
{ "sourceId": "nppes", "category": "authoritative", ... }
]
}/api/npd/data-quality?view=<summary|states|specialties|endpoints>stablePre-aggregated data quality breakdown sourced from Supabase (synced from BigQuery weekly).
curl -s "https://ainpi.dev/api/npd/data-quality?view=summary" | jq/api/npd/validationstableLive BigQuery vs source-manifest reconciliation. Reports per-resource expected/actual/delta + completeness.
curl -s https://ainpi.dev/api/npd/validation | jqCode 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/mcpFive 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.jsonand/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.
| Tier | Units/min | Units/day | How to get it |
|---|---|---|---|
| Anonymous | 120 | 2,000 | No key needed. |
| Free key | 600 | 20,000 | Email gene@fhiriq.com with what you are building. |
| Partner | 3,000 | 100,000 | Named 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.