CMS Open Payments API — Physician Roll-up (Sunshine Act)
Pricing
from $4.40 / 1,000 results
CMS Open Payments API — Physician Roll-up (Sunshine Act)
CMS Open Payments (Sunshine Act) API: look up physician or manufacturer general payments by name, NPI or manufacturer, rolled up per physician × manufacturer — total USD, payment count, date range and payment-type mix. Pharma-compliance ready. Keyless, official CMS.
Pricing
from $4.40 / 1,000 results
Rating
0.0
(0)
Developer
Kyle Maloney
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
0
Monthly active users
2 days ago
Last modified
Categories
Share
CMS Open Payments API - Physician x Manufacturer Roll-up (Sunshine Act)
A CMS Open Payments (Sunshine Act) API: look up physician or manufacturer general payments by name, NPI, profile ID or manufacturer, and roll them up per recipient x manufacturer - total USD, payment count, date range, product mix and payment-type mix. Turn millions of disclosure rows into a compliance-ready summary. Keyless, official CMS source.
Every row states its own completeness. CMS returns an exact match count for
your query; this Actor reports it as matching_rows_total alongside
rows_fetched, rows_coverage_pct and a totals_are_partial flag, so a capped
pull can never be mistaken for a relationship total.
Who it's for
- Pharma / medtech compliance teams auditing physician payment exposure.
- Investigative and policy researchers analysing industry-to-physician money.
- Healthcare due-diligence teams and journalists profiling a physician, teaching hospital or manufacturer.
- AI agents needing a "look up Open Payments" tool.
What it does
Resolves the correct CMS General Payment detailed dataset from the live CMS catalogue (program years 2019-2025 as of 2026-08-01), runs four live drift assertions, queries the datastore, and aggregates each recipient x manufacturer pair into one row: total dollars, payment count, first/last payment dates, natures of payment, products and NDC codes, and CMS compliance indicators (dispute status, third-party payment, physician ownership, delayed publication). Optional NPPES specialty enrichment.
Example input
{"lastName": "smith","firstName": "john","programYear": "2025","includeSpecialty": true,"maxResults": 500}
Look up one physician precisely (the recommended mode):
{ "npi": "1659344299" }
Profile a manufacturer:
{ "manufacturer": "Genentech", "maxResults": 500 }
Inputs
| Input | Type | Notes |
|---|---|---|
lastName | string | Covered-recipient last name. Substring match - Smith also matches Goldsmith, Nesmith, Smithers. |
firstName | string | Covered-recipient first name, substring match. |
npi | string | Exact 10-digit NPI. The most precise lookup. |
profileId | string | Exact CMS covered-recipient profile ID. |
manufacturer | string | Manufacturer / GPO name, substring match. |
programYear | string | e.g. 2025. Blank = most recent published year. A year CMS does not publish now fails the run and lists the available years instead of silently answering about a different year. |
includeSpecialty | boolean | Best-effort NPPES specialty enrichment. Reported in nppes_status. |
maxResults | integer | Cap on raw CMS rows pulled before roll-up. CMS pages via SQL OFFSET, which slows sharply past a few thousand rows. |
At least one filter is required. An unfiltered run fails immediately - nothing is emitted and nothing is billed.
Output fields
One row per (recipient x manufacturer) relationship. 70 declared fields.
Recipient
| Field | Description |
|---|---|
recipient_name | Physician full name, or the teaching hospital name when the recipient is a hospital. |
recipient_npi | 10-digit NPI, if disclosed. |
recipient_profile_id | CMS covered-recipient profile ID. |
recipient_type | e.g. Covered Recipient Physician, Covered Recipient Teaching Hospital. |
recipient_key_basis | How the recipient was identified for grouping: npi, profile_id, teaching_hospital_id, teaching_hospital_name, person_name, unidentified. |
recipient_city / recipient_state / recipient_zip_code / recipient_country | Primary business address. |
recipient_specialty | Specialty from CMS slot 1, optionally NPPES-enriched. |
recipient_specialties_all | All six CMS specialty slots, deduped. |
teaching_hospital_name / teaching_hospital_ccn / teaching_hospital_id | Populated when the recipient is a teaching hospital. Verified populating input: {"manufacturer":"Genentech","maxResults":500}. |
Payer
| Field | Description |
|---|---|
manufacturer_name | Manufacturer or GPO that made the payments. |
manufacturer_id | CMS payer ID. |
manufacturer_state / manufacturer_country | Payer location. |
submitting_manufacturer_name | The entity that submitted the record - frequently different from the payer. |
Money
| Field | Description |
|---|---|
total_amount_usd | Sum of published payment amounts in the relationship. |
payment_count | Number of CMS rows aggregated. |
payments_included_total | Sum of CMS number_of_payments_included_in_total_amount - the payments those rows represent. |
largest_single_payment_usd | Largest individual payment. |
mean_payment_usd | Mean across rows with a published amount. |
amount_rows_unparseable | Rows where CMS published no usable amount. Excluded from the sum, never counted as $0. |
first_payment_date / last_payment_date | Date range, YYYY-MM-DD. |
intensity | Human-readable "$X across N payment(s)". |
Payment character
| Field | Description |
|---|---|
natures_of_payment / distinct_natures_count | Consulting fee, food and beverage, travel, royalty, etc. |
forms_of_payment | Cash or cash equivalent, in-kind items and services, etc. |
products | Product names from CMS slot 1 (legacy field, unchanged). |
products_all / distinct_products_count | Product names across all five CMS slots. |
product_types | Drug, Biological, Device, Medical Supply. |
product_therapeutic_areas | CMS product category / therapeutic area. |
product_ndc_codes | NDC codes CMS associates with the products. |
covered_product_payment_count / noncovered_product_payment_count | Covered vs non-covered product rows. |
related_product_payment_count | Rows CMS ties to at least one covered product. |
contextual_information_samples | Up to five free-text notes the manufacturer attached. |
record_ids_sample | Up to 25 CMS record_id values so any figure can be traced to source rows. |
change_types | CMS change_type: NEW, CHANGED, UNCHANGED. |
Compliance indicators
| Field | Description |
|---|---|
disputed_payment_count / any_payment_disputed | Payments the recipient disputed with CMS. |
delayed_publication_count | Rows CMS flagged for delayed publication. |
physician_ownership_payment_count | Rows with the CMS physician-ownership indicator. |
third_party_payment_count / third_party_entities | Payments routed to a third party. |
charity_payment_count | Rows with the CMS charity indicator. |
Completeness, provenance and per-source status
| Field | Description |
|---|---|
matching_rows_total | The exact number of CMS rows matching your query. |
rows_fetched | Rows actually retrieved and aggregated. |
rows_coverage_pct | rows_fetched as a percentage of the match count. Null when unknown - never 0 as a stand-in. |
totals_are_partial | True when fewer rows were retrieved than CMS reports matching, so the money figures are lower bounds. |
result_completeness | complete or partial. |
completeness_note | Plain-language statement of what the row does and does not cover. |
query_stopped_by | exhausted, max_results, time_budget or page_error. |
name_match_mode | exact_key (NPI / profile ID) vs substring (name) vs manufacturer_substring. |
cms_datastore_status | ok, partial, partial_error. Never blank. |
cms_datastore_error | Error text when a page failed mid-run. Null on a healthy run - that is good news, not a dead column. |
nppes_status | not_requested, ok, partial, unavailable. A dead NPPES is reported here, never rendered as "no specialty". |
dataset_identifier / dataset_title | The CMS dataset that answered the query. |
program_year_requested / program_year_resolved | What you asked for vs what was queried. |
program_year | Reporting year from the CMS rows. |
payment_publication_date | Latest CMS publication date across the rows - the data vintage. |
retrieved_at | UTC timestamp of the query. |
source_system / source_url | Provenance. |
Reliability - what this Actor asserts live, on every run
An offline test cannot see a silently degraded upstream. Before any billable row is emitted, this Actor fails the run if any of these regress. Every measured value is logged.
- Column presence - all 91 CMS columns it reads must still exist. A CMS
rename returns HTTP 400
Column not found., which previously became zero rows. - Dataset size - the national file must be in a plausible band (measured 2026-08-01: 2025 = 16,131,856 rows; 2024 = 15,498,687).
- Negative control - an impossible NPI must match exactly 0 rows. If CMS ever starts dropping unsatisfied filters, every roll-up would silently become a slice of the whole national file.
- Positive canary -
last_name LIKE %SMITH%must match a plausible band (measured 2026-08-01: 89,441).
If the CMS datastore never answers, the run fails with an explicit message - it never emits "no payments found", which is not the same fact.
Limits you should know
- CMS pages via SQL
OFFSET, which is O(n). Deep pulls are slow; a wall-clock budget stops very deep paging and flags the result partial. - The datastore rejects
limit > 500(HTTP 400), so pages are 500 rows. - A name search is a substring sweep. Use
npiorprofileIdfor a single-physician answer. - These are CMS general payments. Research payments and ownership/investment interest are separate CMS files and are not included.
Use as an MCP tool
Available to AI agents through Apify's MCP server. Field-level descriptions are
written for tool selection: an agent can read result_completeness and
matching_rows_total to decide whether to re-query with a narrower filter.
Pricing
Pay per result: one event per emitted (recipient x manufacturer) relationship row. A run that fails a drift assertion or never reaches CMS emits nothing and bills nothing.
FAQ
Why did my NPI lookup return nothing before? Until v1.1 the input schema
carried a server-side default of lastName: "Smith", which Apify injected into
every run - so an NPI-only lookup executed as
npi = X AND last_name LIKE %SMITH%Are these totals complete? Only when result_completeness is complete.
Check totals_are_partial before quoting a figure.
Is a payment here evidence of wrongdoing? No. Open Payments is a transparency disclosure. This is a screening and research tool, not a determination about any individual.
Related actors
- CMS Part D Prescriber Delta - year-over-year Medicare Part D drug spending.
- License Verifier - multi-state clinician licence and OIG-exclusion checks.
- CMS NADAC Price Movers - drug acquisition-cost movement.