# License Verification API — Nurses, MDs & OIG Exclusions (`malonestar/license-verifier`) Actor

Primary source verification for US professional licenses. Search 19 state boards by name or license number: status, expiration, disciplinary actions. Cross-checks the NPPES NPI registry and screens the HHS-OIG exclusion list. Bulk roster screening.

- **URL**: https://apify.com/malonestar/license-verifier.md
- **Developed by:** [Kyle Maloney](https://apify.com/malonestar) (community)
- **Categories:** Business, Automation, Developer tools
- **Stats:** 4 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/actors/running/actors-in-store.md#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

## Professional License Verification API — Primary Source Verification, NPI Lookup & OIG Exclusion Screening

**Nurse license verification, physician license lookup, and provider credentialing automation in one call.** Verify US professional licenses against official state board rosters, cross-check the federal **NPPES NPI registry**, and screen the **HHS-OIG LEIE exclusion list** — for a single person or a whole roster. Keyless, no captcha, no anti-bot.

### Who it's for

- **Healthcare credentialing** teams doing **primary source verification (PSV)** before onboarding or during re-credentialing cycles.
- **Payer / provider network** teams running **monthly OIG exclusion monitoring** as a condition of billing Medicare and Medicaid.
- **Healthcare staffing and travel-nurse agencies** doing **bulk license verification for a roster** of clinicians.
- **HR compliance** teams running **license expiration monitoring** across a workforce on a schedule.
- **Insurance producer onboarding** teams verifying a batch of agents before appointment.
- **AI agents** needing a "verify a US professional license" tool, single or batch.

### What makes this different

Most license lookups return a list of name matches and leave you to guess. This one returns a **verdict**.

| It does this | Why it matters |
|---|---|
| **Names the boards it could not reach** | A failed source lookup is reported as `INCONCLUSIVE_SOURCE_ERROR`, never as "not licensed". You can tell "we checked and found nothing" apart from "we could not check". |
| **Scores identity 0–100** | 125 Texas RNs are named "Mary Smith". A surname hit is not an identification, and the output says so. |
| **Cross-walks to the NPI registry** | The provider's self-reported license number in NPPES pins the exact board record — collapsing those 125 candidates to one. |
| **Screens the federal exclusion list** | HHS-OIG LEIE, 83,000+ records. **`oig_excluded` is only ever true on an exact NPI match.** A name match is surfaced as a review candidate with its DOB and city attached — because name and state do not identify a person. |
| **Uses each board's real status vocabulary** | Texas publishes `CURRENT (C)`, not "Active". A substring guess marks 434,301 active nurses as inactive. This doesn't. |

### Verdicts

| `verdict` | Meaning |
|---|---|
| `LICENSED_ACTIVE` | Confidently identified, license in good standing. |
| `LICENSED_NOT_ACTIVE` | Confidently identified; expired, inactive, revoked, surrendered or deceased. See `status_class`. |
| `AMBIGUOUS` | Records found, but identity is not confirmed (weak score or a tie). Supply a first name, middle name, state or license number. |
| `NOT_FOUND` | Every board answered, none had a record. |
| `INCONCLUSIVE_SOURCE_ERROR` | **A source could not be reached.** This is not evidence the person is unlicensed. See `boards_failed` and `boards_failed_detail`. Emitted in both modes — a single search whose boards all failed returns this one row rather than an empty dataset. |

### Coverage — what each board actually contains

A bare state code searches **every** board in that state. `"TX"` reaches TDLR *and* all three nursing boards.

| Board ID | State | Agency | What it actually covers |
|---|---|---|---|
| `IL` | Illinois | IDFPR | All professions — medical, nursing, real estate, cosmetology, engineering. Includes disciplinary flag. |
| `CT` | Connecticut | CT DCP eLicense | All state credentials — health, trades, professional. |
| `CO` | Colorado | CO DORA | All professional and occupational licenses. **Disciplinary actions inline** (case number, action, date) on 59,319 records. |
| `TX` | Texas | TX TDLR | **Trades only** — contractors, electricians, HVAC, cosmetology. Not healthcare. |
| `TX-BON` | Texas | TX Board of Nursing | **Registered Nurses (RN)** — incl. board-action flag, specialty, nursing school. |
| `TX-LVN` | Texas | TX Board of Nursing | **Licensed Vocational Nurses (LVN)**. |
| `TX-APRN` | Texas | TX Board of Nursing | **APRN / Nurse Practitioners** — incl. prescriptive authority status. |
| `WA` | Washington | WA DOH | **All health professions** — nurses, physicians, dentists, therapists, counselors. Real disciplinary flag. |
| `WA-CPA` | Washington | WA Board of Accountancy | Certified Public Accountants. |
| `WA-CONTRACTOR` | Washington | WA L\&I | Registered construction contractors + principal names. |
| `OR` | Oregon | OR CCB | Construction contractors (active only). |
| `OR-BCD` | Oregon | OR BCD | Electricians, plumbers, boiler, elevator, inspectors. |
| `DE` | Delaware | DE DPR | All DPR boards, incl. city/ZIP and issue date. **Disciplinary actions joined** from the DPR enforcement dataset. |
| `NY` | New York | NYS Gaming Commission | **Horse-racing occupations only.** Not a professional or healthcare credential source. |
| `NY-RE` | New York | NY DOS | Real estate salespersons and associate brokers. |
| `NY-COS` | New York | NY DOS | Cosmetology, appearance enhancement, barbering. |
| `NY-NOTARY` | New York | NY DOS | Commissioned notaries public. |
| `NY-APPRAISER` | New York | NY DOS | Certified real estate appraisers. |
| `VT-DFS` | Vermont | VT Division of Fire Safety | Electricians, plumbers, gas installers. |

Plus, on every run: **NPPES NPI Registry** (federal, all US providers) and the **HHS-OIG LEIE exclusion list** (federal, 83,000+ records, refreshed monthly).

#### What each board does and does not publish

Boards differ in which columns they expose, so some fields are legitimately empty depending on which board answered. `board_coverage` on every row states what that board covers, and `unsupported_filters` names any filter it could not apply.

| Field | Boards that populate it |
|---|---|
| `city` / `zip` | IL, CO, CT, DE, OR, OR-BCD, NY-RE, NY-APPRAISER, WA-CPA, WA-CONTRACTOR, VT-DFS. **Not** WA DOH, TX (any), NY, NY-COS, NY-NOTARY. |
| `county` | IL, OR, OR-BCD, TX, TX-BON, TX-LVN, TX-APRN, NY-RE, NY-NOTARY, NY-APPRAISER. |
| `specialty` | IL, CO, TX-BON, TX-APRN, OR-BCD, WA-CONTRACTOR, VT-DFS, and DE (board category). |
| `ever_disciplined` + `board_action_found` | IL, CO, DE, WA, TX-BON, TX-LVN. `null` on the rest — meaning *not checked*, never *clear*. |
| `discipline_action` / `discipline_reason` / `board_action_date` | IL (narrative reason), CO (action type, case number, effective date). DE and NY physician actions arrive via the joined enforcement datasets. |
| `nursing_school`, `state_of_original_licensure`, `practice_setting` | TX-BON and TX-LVN only. |
| `prescriptive_authority_*` | TX-APRN only. |
| `npi` and the rest of the NPI block | Any board, when the licensee is in NPPES — i.e. healthcare. Empty for trades, notaries and real estate. |
| `title` | IL, TX-BON, TX-LVN, TX-APRN, OR, OR-BCD, NY-NOTARY, WA-CPA, WA-CONTRACTOR, VT-DFS. |
| `business_name` | IL, CT, CO, TX, NY-RE, NY-APPRAISER, WA-CONTRACTOR. |

#### Verify a Texas RN license

```json
{ "states": ["TX-BON"], "firstName": "Mary", "lastName": "Smith" }
```

#### Verify a Washington nurse credential

```json
{ "states": ["WA"], "firstName": "Mary", "lastName": "Galligan", "licenseType": "Registered Nurse" }
```

#### Check OIG exclusion status while verifying

Exclusion screening is on by default (`screenExclusions`) and costs nothing extra per row.

| `exclusion_match_basis` | `oig_excluded` | Meaning |
|---|---|---|
| `npi` | **true** | Exact NPI match. Authoritative. |
| `name_state_review` | false | Someone with this exact name in this state is excluded. **This is not an assertion about your licensee.** Compare `exclusion_candidate_name`, `exclusion_candidate_dob` and `exclusion_candidate_city` before acting. |
| `surname_only_review` | false | A namesake exists; `exclusion_surname_hits` says how many. No detail is shown. |
| `null` | false | Clean — nothing matched. |

This distinction is not pedantry. A live check of "Mary Smith, TX, Registered Nurse" matches LEIE record **MARY CLAIRE SMITH** (DOB 1951-05-18, Kingsville TX, excluded 1992), while the licensee actually identified is **MARY L MURRAY SMITH**, license 454090, Travis County, licensed 1980. Same name, same state, same profession, different human being. Only the NPI tells them apart.

#### Find disciplined licensees

```json
{ "states": ["IL", "WA"], "lastName": "Smith", "onlyDisciplined": true }
```

Supported on IL, CO, DE, WA and the two Texas nursing boards (RN and LVN). Boards without the column report it in `unsupported_filters` instead of silently ignoring you.

### Bulk license verification for a roster

Verify a whole roster in one run — built for **healthcare credentialing**, **provider network management** and **HR compliance**.

```json
{
  "roster": [
    { "firstName": "Mary", "lastName": "Smith", "state": "TX", "profession": "Registered Nurse" },
    { "firstName": "Jane", "lastName": "Doe", "middleName": "A", "state": "WA" },
    { "firstName": "John", "lastName": "Roe" }
  ]
}
```

- `firstName` / `lastName` — at least one required. **Supplying both is the threshold for a confident verdict.**
- `middleName` — optional, breaks same-name ties.
- `state` — optional. Omit to search every board.
- `profession` — optional filter, also adds to the match score.

Each entry produces **one verdict row**, capped at 200 entries per run.

#### Key output fields

| Field | Meaning |
|---|---|
| `verdict` | The credentialing decision (table above). |
| `match_score` / `match_tier` | 0–100 identity confidence and how it was reached (`verified_npi_join` is strongest). |
| `candidates_returned` / `candidates_tied` / `candidates_truncated` | How many people matched, how many tied, and whether the candidate list was capped. |
| `boards_searched` / `boards_failed` / `boards_failed_detail` | Exactly what was checked and what failed. |
| `license_no`, `license_status`, `is_active`, `status_class`, `expiration_date` | The license itself. |
| `npi`, `primary_taxonomy`, `npi_license_number`, `practice_city`, `practice_state` | NPI cross-walk. |
| `oig_excluded`, `exclusion_match_basis`, `exclusion_type`, `exclusion_date`, `exclusion_review_required` | Federal exclusion screen. |
| `board_action_found`, `board_action_type`, `board_action_date` | Disciplinary actions. `null` means *could not check*, not *clean*. |
| `checked_at`, `leie_as_of` | Audit trail. |

### Use as an MCP tool

This Actor is callable directly by any MCP-compatible AI agent through Apify's hosted
MCP server. There is no server to run and no integration code to write - the tool
schema an agent sees is generated from this Actor's own input and dataset schemas.

**Endpoint**

```
https://mcp.apify.com?tools=malonestar/license-verifier
```

**Claude Desktop, Claude Code or Cursor** - add to `claude_desktop_config.json`,
`.mcp.json` or `.cursor/mcp.json` respectively:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com/?tools=malonestar/license-verifier",
      "headers": { "Authorization": "Bearer YOUR_APIFY_TOKEN" }
    }
  }
}
```

Get a token at https://console.apify.com/settings/integrations. Claude Desktop can
also authenticate interactively via OAuth against `https://mcp.apify.com` with no
`headers` block. Full reference: https://docs.apify.com/platform/integrations/mcp

**Try asking your agent**

> Verify whether Mary Smith holds an active nursing license in Texas, cross-walk her NPI, and check the HHS-OIG exclusion list.

**Chains well with** - expose these alongside it by comma-separating the `tools`
parameter, and the agent can carry results from one into the next:

- `malonestar/medicaid-exclusion-screener`
- `malonestar/childcare-provider-leads`

```
https://mcp.apify.com?tools=malonestar/license-verifier,malonestar/medicaid-exclusion-screener,malonestar/childcare-provider-leads
```

Billing is unchanged when called as an MCP tool: this Actor is Pay-Per-Event and an
agent pays the same per-result price a human does. A run that cannot answer fails
without billing rather than returning an unverified negative.

### FAQ

**What is primary source verification?** Checking a credential directly against the issuing board's own record. Every row carries `source_agency` and `source_url` pointing at the official verification page.

**Does it include license expiration?** Yes — `expiration_date` on every board that publishes it, normalized to ISO.

**Does it include disciplinary and board actions?** Yes, where published: IL, CO, DE, WA and the Texas nursing boards carry a flag on the record itself; Colorado adds the case number, action type and effective date inline; Delaware and New York physician actions are also joined from dedicated enforcement datasets. `board_action_found` is `false` when the board publishes the column and the licensee is clear, and `null` when the board publishes no such column at all — so "checked and clear" is never confused with "not checked".

**Does it check the OIG exclusion list?** Yes — HHS-OIG LEIE, on by default, matched by NPI first. Ideal for monthly exclusion monitoring.

**Does it look up NPI numbers?** Yes — the NPPES registry, returning NPI, taxonomy and practice address, and using the license number to confirm identity.

**Which states?** CO, CT, DE, IL, NY, OR, TX, VT, WA across 19 boards, plus two federal sources. See the coverage table.

**Any captchas or anti-bot?** No. All sources are official open data or public federal APIs.

### Source & freshness

State professional-licensing open-data portals (Socrata), the CMS NPPES NPI Registry, and the HHS-OIG LEIE. Official, keyless (optional `socrataAppToken` raises rate limits). The exclusion list is cached per run and refreshed when OIG publishes a new file — `leie_as_of` records which edition was used.

### Pricing (Pay Per Result)

Billed per row returned.

- A **single search** that matches nothing returns no rows and **costs nothing**.
- A **roster entry always returns one verdict row and is always billable** — including `NOT_FOUND` and `INCONCLUSIVE_SOURCE_ERROR`. A verified negative is the deliverable.
- `statusOnly` returns **fewer fields at the same price per row**. It is a convenience, not a discount.
- NPI cross-walk, exclusion screening and disciplinary lookups add **no per-row cost**.

# Actor input Schema

## `states` (type: `array`):

State codes or explicit board IDs. A bare state code searches EVERY board in that state - e.g. "TX" covers TDLR trades AND the Board of Nursing (RN, LVN, APRN). Use a hyphenated ID to target ONE board: IL-IDFPR, CT-DCP, CO-DORA, TX-TDLR, TX-BON (RN), TX-LVN, TX-APRN, OR-CCB, OR-BCD, NY-RACING (horse racing only), NY-RE, NY-COS, NY-NOTARY, NY-APPRAISER, WA-DOH (health professions), WA-CPA, WA-CONTRACTOR, DE-DPR, VT-DFS. States: CO, CT, DE, IL, NY, OR, TX, VT, WA.

## `licenseNumber` (type: `string`):

Exact license number to verify. The most precise search available — use it when you have it.

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

Full-name search that works across every board (handles combined name fields). Use this if lastName/firstName return nothing.

## `lastName` (type: `string`):

Licensee last name (partial match).

## `firstName` (type: `string`):

Licensee first name (partial match). Supplying it raises match confidence sharply — first + last name exact is the threshold for a confident verdict.

## `businessName` (type: `string`):

Business or DBA name to search (partial match).

## `licenseType` (type: `string`):

e.g. "Registered Nurse", "Real Estate", "Cosmetology", "Professional Engineer". Boards without a license-type column skip this filter and say so in the log and in unsupported\_filters.

## `city` (type: `string`):

City to filter licensees by. Not every board publishes a city column.

## `onlyDisciplined` (type: `boolean`):

Return only licensees with a disciplinary history. Honoured by IL-IDFPR, CO-DORA, DE-DPR, WA-DOH, TX-BON and TX-LVN. Target those board IDs directly rather than a bare state code, or sibling boards that publish no disciplinary column will also return rows (they are reported in unsupported\_filters).

## `statusOnly` (type: `boolean`):

Return only license number, type, status, expiration, provenance and the OIG exclusion flags. Handy for recurring renewal monitoring. NOTE: this is the SAME price per row as a full record — it returns less data, not cheaper data.

## `screenExclusions` (type: `boolean`):

Check every result against the federal HHS-OIG List of Excluded Individuals/Entities (83,000+ records, refreshed monthly). Matched on NPI first, then last+first+state. A surname-only hit is NEVER reported as an exclusion — it is flagged for review instead. Adds no per-row cost.

## `npiLookup` (type: `boolean`):

For each roster entry, look the person up in the federal NPI registry and use their self-reported state license number to pin down the exact board record. This is what turns 125 same-name candidates into one verified match, and it returns NPI, taxonomy and practice address.

## `checkDiscipline` (type: `boolean`):

Join the best-matching licensee against secondary board-action datasets: Delaware DPR disciplinary actions and the NYS Office of Professional Medical Conduct. A failed lookup is reported as unknown, never as 'no action on file'.

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

Maximum number of license records to return per board. Each returned row is billable, so start small.

## `rosterLimitPerBoard` (type: `integer`):

How many candidate records to pull per board for each roster entry before scoring. Higher values reduce the chance of missing the right person for a common surname; candidates\_truncated tells you when the cap was hit.

## `socrataAppToken` (type: `string`):

Optional free Socrata app token to raise rate limits.

## `roster` (type: `array`):

Batch mode: verify a whole roster of professionals in one run. Each item: {firstName, lastName, state (optional — omit to search every board), profession (optional), middleName (optional, improves scoring)}. Emits one verdict row per entry with verdict, match\_score, match\_tier, NPI cross-walk, OIG exclusion screen and board-action status. Capped at 200 entries per run. Every verdict row is billable, including NOT\_FOUND and INCONCLUSIVE\_SOURCE\_ERROR — a verified negative is the deliverable.

## Actor input object example

```json
{
  "states": [
    "WA"
  ],
  "lastName": "Threlkeld",
  "firstName": "Judson",
  "onlyDisciplined": false,
  "statusOnly": false,
  "screenExclusions": true,
  "npiLookup": true,
  "checkDiscipline": true,
  "maxResults": 10,
  "rosterLimitPerBoard": 100
}
```

# Actor output Schema

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

Professional license records and credentialing verdicts in the default dataset.

# 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 = {
    "states": [
        "WA"
    ],
    "lastName": "Threlkeld",
    "firstName": "Judson",
    "maxResults": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("malonestar/license-verifier").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 = {
    "states": ["WA"],
    "lastName": "Threlkeld",
    "firstName": "Judson",
    "maxResults": 10,
}

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

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

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

```

## CLI example

```bash
echo '{
  "states": [
    "WA"
  ],
  "lastName": "Threlkeld",
  "firstName": "Judson",
  "maxResults": 10
}' |
apify call malonestar/license-verifier --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,malonestar/license-verifier"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

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