# CO Professional Licenses - Nurses, MDs, Engineers (`j0401/co-professional-licenses`) Actor

Colorado professional and occupational licenses (public open data, 1.6M records, 40+ license types): the state's register of licensed nurses, physicians, engineers, CPAs and more - licensee, license number, status, first-issued / last-renewed / expiration dates, specialty and discipline.

- **URL**: https://apify.com/j0401/co-professional-licenses.md
- **Developed by:** [Wenhao Yang](https://apify.com/j0401) (community)
- **Categories:** Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.06 / 1,000 co professional license records

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Colorado Professional Licenses - Nurses, MDs, Engineers

Colorado licenses its professions through the Division of Professions and Occupations, and the **license register is public open data**. This actor turns it into a **charged-per-record search, filter and aggregate tool**: verify a license by number or name, pull a whole profession or a city, filter by status or expiration, or aggregate the register.

**Built for:** healthcare and staffing credentialing, license verification and compliance screening, workforce research, and anyone who wants **Colorado's licensed-professional roster** without paging a portal one name at a time.

### What it covers

**1,608,059 license records** across **313 license types** and **23 status values**. Rows are licenses, and a number can repeat (an entity license plus its endorsements), so the register holds **669,686 distinct license numbers**.

The depth is the **license lifecycle**:

- **type** - RN (**238,749**), NA (**195,495**), APE, COS, DR (physician **69,185**), PE (engineer **63,335**), PN, AP, CPA, JW, MT, LPC, CSW, EC (electrician), and the other 300+
- **status** (exact, 23 values) - **Active 605,876**, Expired **912,462**, Cancelled, Revoked, Surrendered, Suspended, Retired, and the **Active variants kept distinct**: Active - With Conditions, Active - Provisional, Active - Telehealth ONLY, Active - Restricted, ...
- **dates** - first issued, last renewed and expiration
- **licensee** - person name or entity name, with city / state / zip, plus **specialty and degree** (the source's `title` column is empty on every row, so it is carried but never claimed as depth)
- **discipline** - the case number and program action, where a board has acted

### The fine print that matters

Two quirks are handled for you. The register holds **impossible far-future renewal dates** - a last-renewed out to **year 9020** - which are excluded from every query and blanked on output, so a newest-first pull can never surface a year-9020 row. And `status` has **23 values where "Active" is not one thing** - Active, Active - With Conditions, Active - Provisional, Active - Telehealth ONLY, Active - Restricted - so the actor matches the **exact status**, never a bare substring (a `%Active%` match would silently conflate six different statuses).

### Typical questions

- "Verify **one license** by number."
- "Every **active RN** in **Denver**."
- "Licenses **expiring** in the next 90 days."
- "A person's **license history** by name."
- "Aggregate the register by **type**, **status** or **specialty**."

### Inputs

| Input | What it does |
|---|---|
| `mode` | `rows` (default) / `aggregate` |
| `licenseNumber` | exact license number |
| `name` | licensee person or entity name |
| `licenseType` / `status` / `specialty` | type, exact status, specialty |
| `city` / `state` / `zip` | where |
| `issuedFrom/To` / `expiresFrom/To` | first-issued and expiration ranges |
| `groupBy` | aggregate over type / status / state / specialty |
| `maxResults` | cap records (default 50) |

**Default run = the 50 most recently renewed licenses** - fast for the daily auto-test. For a targeted query add a filter; for a broad view use `aggregate`.

### Example inputs

**Verify one licence by its number** - `licenseNumber` is exact.

```json
{ "licenseNumber": "1617657" }
```

**Every active RN in Denver** - `status` is matched exactly, so
`Active - With Conditions` is never folded into `Active`.

```json
{ "licenseType": "RN", "status": "Active", "city": "Denver",
  "maxResults": 50 }
```

**Licences expiring in the next 90 days** - an expiration window with the
current roster.

```json
{ "status": "Active", "expiresFrom": "2026-09-25",
  "expiresTo": "2026-12-24", "maxResults": 50 }
```

**The register by licence type** - one count per group.

```json
{ "mode": "aggregate", "groupBy": "licenseType" }
```

### Low cost

**From $0.0001 per record, down to $0.00006 at Gold** - billed only for the rows you use, at the low end of the store. Cost scales with what you pull, not with the size of the register, and each record is metered individually.

What a thin scraper misses is the **status granularity and the runaway dates**. "Active" is six different statuses in this register and a substring match collapses them into one wrong answer; the register also carries **renewal dates out to year 9020**, so a date-sorted pull puts junk at the top; and a license number is **not unique to a row** once endorsements are counted. Normalizing the exact-status set, the far-future dates and the license-vs-row grain into a schema where a `licenseType` / `status` / `specialty` query returns exactly the licensees you mean is the actual product. Every pull is integrity-checked against the register's known shape, so a degraded source fails loudly instead of returning bad rows.

### Example output

**One licence** - `licenseNumber=1617657` returns the record for that licence.
Every record also carries `platform`, `source`, `mode`, and the aggregate
columns `groupKey` / `groupCount` / `groupBy`, which are `""` outside aggregate
mode:

```json
{
  "platform": "co-professional-licenses",
  "source": "colorado-dora-professional-licenses",
  "mode": "rows",
  "groupKey": "", "groupCount": "", "groupBy": "",
  "licenseNumber": "1617657",
  "licenseType": "RN",
  "licenseTypeName": "",
  "status": "Active",
  "lastName": "Vandenberg-Harrison",
  "firstName": "Kristen",
  "middleName": "Montgomery",
  "suffix": "",
  "entityName": "",
  "city": "Avon",
  "state": "CO",
  "zip": "81620",
  "firstIssuedDate": "2012-08-10",
  "lastRenewedDate": "2026-10-01",
  "expirationDate": "2028-09-30",
  "specialty": "",
  "title": "",
  "degrees": "~Diploma in Nursing",
  "caseNumber": "",
  "programAction": ""
}
```

**`mode=aggregate`, `groupBy=licenseType`** - one row per type (the record
fields are `""`; the group lands in `groupKey` / `groupCount`):

```
RN                                                              238,749
NA                                                              195,495
DR                                                               69,185
PE                                                               63,335
```

### Source

- [Colorado: Professional and Occupational Licenses](https://data.colorado.gov/Regulations/Professional-and-Occupational-Licenses-in-Colorad/7s5z-vewr) - the Division of Professions and Occupations license register. Public open data. Records are the state's own published fields; not an endorsement of any licensee, and not a substitute for the board's official verification.

# Actor input Schema

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

rows = license records matching your filters (default). aggregate = one count row per group (see groupBy).

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

Exact license number. A number can appear on more than one row (an entity license plus its endorsements).

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

Person first / last name or entity name substring. Blank = any.

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

License type prefix (exact, 313 types in the register): RN (238,749), NA, APE, COS, DR (physician), PE (engineer), PN, AP, CPA, JW, EC (electrician), ... Blank = any.

## `status` (type: `string`):

Exact status (23 values, case-insensitive): Active, Active - With Conditions, Expired, Cancelled, Revoked, Surrendered, Suspended, Retired, Beyond 6 Years Expired, Grad to Higher Level, Need Master Hire - Cannot Practice, ... Active ~605,876; Expired ~912,462. Blank = any.

## `specialty` (type: `string`):

Specialty substring. Blank = any.

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

City substring. Blank = any.

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

Two-letter state. Blank = any.

## `zip` (type: `string`):

Zip code (prefix match). Blank = any.

## `issuedFrom` (type: `string`):

Only licenses first issued on/after this date (YYYY-MM-DD).

## `issuedTo` (type: `string`):

Only licenses first issued before this date (YYYY-MM-DD).

## `expiresFrom` (type: `string`):

Only licenses expiring on/after this date (YYYY-MM-DD). Combine with status=Active for the current roster.

## `expiresTo` (type: `string`):

Only licenses expiring before this date (YYYY-MM-DD).

## `groupBy` (type: `string`):

Which dimension to aggregate over. city is a filter, not a rollup (too many distinct values).

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

Cap the number of records pushed in rows mode (0 = up to ~10k per run; each record is metered individually, so there is no per-run charge cap). Aggregate mode returns every group.

## Actor input object example

```json
{
  "mode": "rows",
  "licenseNumber": "",
  "name": "",
  "licenseType": "",
  "status": "",
  "specialty": "",
  "city": "",
  "state": "",
  "zip": "",
  "issuedFrom": "",
  "issuedTo": "",
  "expiresFrom": "",
  "expiresTo": "",
  "groupBy": "licenseType",
  "maxResults": 50
}
```

# Actor output Schema

## `recordsUrl` (type: `string`):

Colorado professional license records or aggregates - as JSON

## `datasetUrl` (type: `string`):

No description

## `runUrl` (type: `string`):

No description

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("j0401/co-professional-licenses").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("j0401/co-professional-licenses").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 '{}' |
apify call j0401/co-professional-licenses --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,j0401/co-professional-licenses"
        }
    }
}
```

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/M5QEiqEe0RGa3hbrK/builds/4DvtttmCpHvDHmTPh/openapi.json
