# Medicaid Exclusion Screener — OIG LEIE + State Name/NPI Check (`malonestar/medicaid-exclusion-screener`) Actor

Screen names & NPIs against merged Medicaid/Medicare exclusion lists. Deduped OIG LEIE + state (NY OMIG) exclusions, name+NPI searchable, with a screen-verdict mode returning excluded yes/no, which list, and match confidence. Keyless. Screening/research tool, not compliance advice.

- **URL**: https://apify.com/malonestar/medicaid-exclusion-screener.md
- **Developed by:** [Kyle Maloney](https://apify.com/malonestar) (community)
- **Categories:** Lead generation, Developer tools, Agents
- **Stats:** 2 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.50 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Medicaid Exclusion Screener - OIG LEIE + State (NY OMIG) NPI/Name/DOB Check

Screen provider names, **NPIs and dates of birth** against a **merged Medicaid/Medicare exclusion list** - the federal **OIG LEIE** (HHS-OIG List of Excluded Individuals/Entities, 83,665 records) plus **state Medicaid exclusion lists** (New York OMIG live, 8,975 records; more states scaffolded) - in one keyless actor.

Two modes:

- **Screen (verdict)** - hand it a batch of `{name?, npi?, dob?}` targets and get a **gated verdict per target**. This is the compliance workflow and the primary **MCP tool** surface.
- **Search** - browse/filter the merged exclusion list by name, NPI, or state.

> This is a **screening tool over public government lists**, for research and due diligence. It is **not** a certified OIG/SAM screening service, **not** a compliance determination, and **not** legal advice. Confirm every hit - and every clear - against the primary source list before making an employment, enrollment, credentialing or payment decision.

### The four verdicts, and why a clear is not always available

`screening_verdict` is the field to read. It is **gated**: it can never say "clear" while a list this run asked for was unavailable.

| `screening_verdict` | What it means |
|---|---|
| `excluded_identifier_confirmed` | Matched on an **exact 10-digit NPI**, or on a name **corroborated by an exact date-of-birth match**. |
| `possible_match_review_required` | **Name-only match.** Not an identity determination. The LEIE contains many same-name individuals - screening the name `Mary Smith` returns 10 distinct records, 7 of them exact-name, all different people. Adjudicate against the primary source. |
| `no_match_on_screened_lists` | Every requested list loaded, passed its live drift assertions, and nothing matched. `excluded` is `false`. |
| `incomplete_not_screened` | **A requested list could not be loaded.** `excluded` is `null`, not `false`. Nothing is asserted about this target. |

`excluded` is tri-state and must be read that way: `true` = matched, `false` = screened and clear, **`null` = not screened**. If every requested list fails, the run **fails loudly** and emits and bills nothing.

**Why this matters (v1.0 defect, fixed 2026-08-01):** v1.0 logged a warning and carried on when a list failed to load, then reported every unmatched target as `excluded: false, match_type: "no-match"`. Reproduced live: the NY-OMIG excluded entity `1 MEDICAL SUPPLIES CORP` (NPI 1407487887) screened against a LEIE-only merge returned a billable, confident "not excluded". With both lists down, every target came back clean and the run reported SUCCESS.

### Two things to know about the underlying data

1. **Only ~10.5% of federal LEIE records carry an NPI** (8,767 of 83,665, measured 2026-08-01). NPI-only screening cannot cover the list; most exclusions are reachable only by name.
2. **94.9% of LEIE records carry a date of birth** (79,372 of 83,665). Supplying `dob` on a person target is the single most effective way to turn an ambiguous name match into a decision: an exact DOB match promotes the row to `excluded_identifier_confirmed`, and a **contradicting DOB rejects the match outright** (same name, provably different person).

### Who it's for

- **Healthcare credentialing and provider enrollment** teams verifying practitioners are not excluded.
- **Payer, pharmacy, MSO and hospital compliance** teams - CMS requires **monthly** exclusion screening of employees, contractors and vendors.
- **Revenue-cycle and billing** vendors avoiding claims tied to excluded providers.
- **AI agents** doing due diligence: call this as a tool and read back a structured, gated verdict.

### Example input (screen mode - the prefilled default)

```json
{
  "mode": "screen",
  "targets": [
    { "npi": "1972902351" },
    { "name": "John Smith" },
    { "name": "Nonexistent Testperson Xyz" }
  ],
  "fuzzyThreshold": 0.85
}
```

Returns an identifier-confirmed hit, name-only review items, and a verified clear - the three outcomes you need to see before trusting the tool.

Person screening with a date of birth:

```json
{
  "mode": "screen",
  "targets": [
    { "name": "Jane Q Provider", "npi": "1234567890" },
    { "name": "Gregory Testington", "dob": "1970-01-01" }
  ]
}
```

### Example input (search mode)

```json
{ "mode": "search", "npi": "1972902351" }
```

Other search filters: `name` (contains + fuzzy), `state` (e.g. `NY`). Rows are **merged, not deduplicated** - a party listed both federally and by a state returns one row per list, so you can see each authority separately.

### Output fields (41)

**Verdict and match**

| Field | Meaning |
|---|---|
| `screened_input` | The name or NPI that was queried (screen target, or the search filter value). |
| `screening_verdict` | Gated verdict - see the table above. |
| `verdict_reason` | Plain-language explanation, including the caveat when the match rests on a name alone. |
| `excluded` | Tri-state: `true` matched, `false` screened and clear, **`null` not screened**. |
| `identifier_match` | `true` when the match rests on an exact NPI, or a name corroborated by an exact DOB. |
| `name_only_match` | `true` when a name string is the only basis for the match. A review item, never a determination. |
| `dob_match` | `true`/`false` when both target and record carried a DOB; `null` when no comparison was possible. |
| `match_type` | `exact-npi`, `exact-name`, `fuzzy-name`, `no-match`, or `not-screened`. |
| `match_confidence` | 0-1. 1.0 for exact NPI or exact name; normalized similarity for fuzzy-name; `0` for a verified miss; `null` when not screened. |

**Per-source outcome contract** (on every row)

| Field | Meaning |
|---|---|
| `screening_complete` | `true` only when every requested list loaded and passed its live drift assertions. |
| `lists_requested` | Comma-separated keys of every list this run attempted. |
| `lists_screened` | The lists that actually loaded. The verdict is valid only against these. |
| `lists_unavailable` | Requested lists that failed to load or failed a drift assertion. `null` on a complete run. |
| `list_errors` | Per-list failure detail for anything in `lists_unavailable`. |
| `records_screened` | Total merged exclusion records the target was screened against. |
| `oig_leie_status` | `ok` / `unavailable` / `not_requested`. |
| `ny_omig_status` | `ok` / `unavailable` / `not_requested`. |
| `ca_medi_cal_status` | `not_requested` until that source URL is confirmed and enabled. |
| `tx_oig_hhsc_status` | `not_requested` until that source URL is confirmed and enabled. |

**The exclusion record**

| Field | Meaning |
|---|---|
| `list_source` | `OIG-LEIE`, `NY-OMIG`, ... `null` on a verdict-only row. |
| `last_name`, `first_name`, `middle_name` | Excluded individual name (`null` for entities). |
| `business_name` | Excluded entity name (`null` for individuals). |
| `npi` | 10-digit NPI. Populated on ~10.5% of LEIE records. |
| `upin` | Legacy Unique Physician Identification Number (5,957 LEIE records). A secondary corroborating identifier. |
| `dob` | Date of birth, `YYYY-MM-DD` (79,372 LEIE records). The field that separates same-name individuals. |
| `license_number` | Provider licence number where the source publishes one (NY OMIG). |
| `exclusion_type` | Statutory authority code, e.g. `1128a1`, `1128b4`. Closed 22-value vocabulary, asserted live. |
| `exclusion_date` | Effective date, `YYYY-MM-DD`. |
| `reinstatement_date` | Reinstatement date where published. |
| `waiver_date` | Waiver date where a waiver applies. |
| `waiver_state` | Two-letter state a waiver was granted for (LEIE `WVRSTATE`). |
| `specialty` | Provider specialty or type. |
| `general_category` | Coarse provider category, e.g. `NURSING PROFESSION`, `DME COMPANY`. |
| `address`, `city`, `state`, `zip` | Address of the excluded party where listed. |
| `source_url` | Link to the authoritative source list for verifying the row. |
| `retrieved_at` | ISO 8601 timestamp of the fetch. |

#### Sparse and structurally-null columns, with verified populating inputs

A dead-column audit over 259 live rows (2026-08-01) found four columns null. Each is accounted for, and each has an input recorded here so it can be re-verified:

| Column | Status | Input that populates it (verified live 2026-08-01) |
|---|---|---|
| `waiver_date`, `waiver_state` | Sparse by nature - only 4 of 83,665 LEIE records carry a waiver | `{"mode":"screen","targets":[{"npi":"1285673012"}]}` returns `waiver_date 2015-06-18`, `waiver_state TX` |
| `upin` | Sparse - 5,957 of 83,665 records | `{"mode":"screen","targets":[{"npi":"1871571406"}]}` returns `upin H95172` |
| `dob_match` | Populated only when the target supplies a `dob` AND the list record publishes one | `{"mode":"screen","targets":[{"name":"Mohamed Aswad","dob":"1968-06-18"}]}` returns `dob_match true`, verdict `excluded_identifier_confirmed`; the same name with `dob 1990-01-01` is correctly rejected as a different person |
| `license_number` | `NY-OMIG` rows only | `{"mode":"search","state":"NY","name":"pharmacy"}` (163 of 252 rows populated) |
| `reinstatement_date` | **Structurally always null against the current sources.** OIG `UPDATED.csv` is the *currently excluded* list: `REINDATE` is non-zero on **0 of 83,665** records because reinstated parties are removed from the file entirely. The column is retained (it is part of the v1.0 contract, and the mapper is pinned by an offline fixture) and will populate if a source that publishes reinstatements is added. | none today |

### Live drift assertions

Every run asserts the following **before any billable row is produced**, and fails the run when an assertion breaks:

- the exact 18-column LEIE header and 5-column NY-OMIG header (a renamed or reordered column silently remaps every field)
- record counts inside measured bands (LEIE 50,000-250,000; NY-OMIG 4,000-40,000)
- freshness: the newest exclusion date must be within 200 days (LEIE) or 400 days (NY-OMIG)
- population floors on NPI and DOB, so a parser that stops populating them cannot pass on row count alone
- the **closed 22-value `exclusion_type` vocabulary** - an unknown statutory code fails the run rather than passing through
- an HTML or markup body served with HTTP 200 (an error, login or maintenance page) is rejected as a list
- a **positive canary**: a record from each list must be found by its own NPI and by its own name, round-tripping fetch to match
- **negative controls**: an absent-but-valid NPI (`9999999999`) and a nonsense name must match **nothing**, which is what catches a matcher that has started matching everything

Every measured value is logged on every run (`[drift]` and `[canary]` lines) so the bands can be tightened on evidence.

### Sources

| Key | List | Format | Source |
|-----|------|--------|--------|
| `OIG-LEIE` | Federal HHS-OIG List of Excluded Individuals/Entities | CSV | oig.hhs.gov/exclusions |
| `NY-OMIG` | New York State OMIG Medicaid Exclusions | tab-delimited | omig.ny.gov |
| `CA-MEDI-CAL` | California DHCS Medi-Cal Suspended and Ineligible (scaffolded, disabled) | - | files.medi-cal.ca.gov |
| `TX-OIG-HHSC` | Texas HHSC-OIG Exclusions (scaffolded, disabled) | - | oig.hhs.texas.gov |

Use the `lists` input to restrict which lists load. A scaffolded list cannot be loaded until its source URL and columns are confirmed; asking for one on its own fails the run rather than returning an empty, clean-looking result.

### Use as an MCP tool

Callable by AI agents (Claude, Cursor, etc.) via **mcp.apify.com**. Field-level descriptions let an agent screen a name, NPI or DOB and read back `screening_verdict`, `identifier_match`, `name_only_match` and `screening_complete`, so an agent can tell "clear" from "not checked" without guessing.

### Pricing

Pay-Per-Event: one dataset record (one exclusion match or one screen verdict) is the billable unit, with graduated discounts on paid Apify plans. A run that fails its drift assertions emits nothing and bills nothing beyond the fractional actor start.

### FAQ

**How do I check if a provider is excluded from Medicare/Medicaid?**
Run `mode: "screen"` with the provider name, NPI and (for people) date of birth in `targets`. Read `screening_verdict`.

**Why does a name match not say "excluded"?**
Because a name is not an identity. Screening `Mary Smith` returns 10 records for different people. A name match is reported as `possible_match_review_required` and must be adjudicated against the primary source using an identifier and a date of birth.

**What happens if a list is down?**
Nothing is reported clear. Affected targets come back `excluded: null` with `screening_verdict: "incomplete_not_screened"` and `lists_unavailable` naming the list. If every requested list is down the run fails and bills nothing.

**Does it cover the federal OIG LEIE and state Medicaid exclusion lists?**
The federal OIG LEIE plus New York OMIG today; California and Texas are scaffolded and disabled until their sources are confirmed.

**Is this an official OIG screening or a compliance determination?**
No. It screens public government lists for research and due diligence. Confirm any hit, and any clear, against the official source before making an employment, enrollment or payment decision.

### Related actors

- **License Verifier** - state professional licence status across 19 boards, with the same identifier-first matching discipline.
- **KYB Company Verifier** - cross-registry business verification for the entity side of a vendor check.
- **Sanctions Screening List Change Monitor** - OFAC / BIS / State denied-party deltas from the Trade.gov Consolidated Screening List.

# Actor input Schema

## `mode` (type: `string`):

'search' browses/filters the merged exclusion list by name, NPI, or state. 'screen' takes a batch of targets and returns an excluded yes/no VERDICT per target (the compliance use case: check your providers/vendors against the lists).

## `targets` (type: `array`):

screen mode ONLY. An array of people/entities to check, each an object with an optional 'name', 'npi' and/or 'dob' (YYYY-MM-DD), e.g. \[{"name":"Jane Doe","dob":"1970-01-01"}, {"npi":"1234567890"}]. Supplying a dob is strongly recommended for person screening: a name alone can never confirm identity, and a dob that contradicts the list record rejects the match outright. Each target yields one row per matching exclusion record, or a single verdict row when nothing matches.

## `name` (type: `string`):

search mode. Case-insensitive contains + fuzzy match against provider/business names and person names across the merged list. Empty = no name filter.

## `npi` (type: `string`):

search mode. Exact 10-digit National Provider Identifier to look up in the merged list. Empty = no NPI filter.

## `state` (type: `string`):

search mode. Two-letter US state code (e.g. NY, CA, TX) to keep only exclusions whose recorded state matches. Empty = all states.

## `lists` (type: `array`):

Optional list keys to load, e.g. \["OIG-LEIE","NY-OMIG"]. Empty = all enabled lists (currently OIG-LEIE + NY-OMIG). If a requested list cannot be loaded the run reports incomplete\_not\_screened rather than a clear result, and if none can be loaded the run fails without emitting or billing anything. Scaffolded states (CA-MEDI-CAL, TX-OIG-HHSC) are disabled until their source URL and columns are confirmed.

## `fuzzyThreshold` (type: `number`):

Minimum name similarity (0-1) for a fuzzy-name match to count as a possible match. 1 = exact only; lower catches more typos and variants at the cost of more review items. Default 0.85. Name matches are always reported as possible\_match\_review\_required, never as a confirmed exclusion.

## `maxResults` (type: `integer`):

Maximum number of records to emit. One record = one billable result.

## Actor input object example

```json
{
  "mode": "screen",
  "targets": [
    {
      "npi": "1972902351"
    },
    {
      "name": "John Smith"
    },
    {
      "name": "Nonexistent Testperson Xyz"
    }
  ],
  "fuzzyThreshold": 0.85,
  "maxResults": 5000
}
```

# Actor output Schema

## `results` (type: `string`):

The default dataset of exclusion matches / screen verdicts.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "mode": "screen",
    "targets": [
        {
            "npi": "1972902351"
        },
        {
            "name": "John Smith"
        },
        {
            "name": "Nonexistent Testperson Xyz"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("malonestar/medicaid-exclusion-screener").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "mode": "screen",
    "targets": [
        { "npi": "1972902351" },
        { "name": "John Smith" },
        { "name": "Nonexistent Testperson Xyz" },
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("malonestar/medicaid-exclusion-screener").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "mode": "screen",
  "targets": [
    {
      "npi": "1972902351"
    },
    {
      "name": "John Smith"
    },
    {
      "name": "Nonexistent Testperson Xyz"
    }
  ]
}' |
apify call malonestar/medicaid-exclusion-screener --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=malonestar/medicaid-exclusion-screener",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/k9agePyFUA9TYzdvh/builds/r0UxwZUWPEUvANXze/openapi.json
