# Connecticut Medical License Verification Scraper (`automation-lab/connecticut-medical-license-verification`) Actor

Search official Connecticut Physician/Surgeon license records by name, number, city, status or expiry. Export credential status, dates, public location and source provenance for provider credentialing.

- **URL**: https://apify.com/automation-lab/connecticut-medical-license-verification.md
- **Developed by:** [Automation Lab](https://apify.com/automation-lab) (community)
- **Categories:** Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.32 / 1,000 physician credential returneds

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

## Connecticut Medical License Verification Scraper

Refresh Connecticut physician credential evidence for provider onboarding and periodic credentialing. This Connecticut medical license verification scraper searches the official state Open Data **Physician/Surgeon** category and returns identity, raw status/reason, dates, public location and provenance in structured rows.

It is for credentialing analysts, healthcare staffing teams and provider-data engineers. It does not determine legal permission to practice. The state dataset may lag the interactive eLicense portal: inspect `recordRefreshedOn` and verify important decisions with the regulator.

### Who is it for?

Provider onboarding analysts use this for identity/status evidence, staffing teams for expiration review queues, and data engineers for repeatable structured credential exports. It does not replace regulator verification or your credentialing policy.

### What does it extract?

Only records classified as **Physician/Surgeon** in Connecticut's State Licenses and Credentials dataset. This is the combined physician/surgeon category, not a degree-classification service: the source does not distinguish MD from DO in these rows.

Nurses, physician assistants, resident credentials, dentists and other professions are excluded. No login, browser, proxy configuration or API key for the state source is required.

### Why use this Actor?

- Search by a supplied holder name or credential number rather than downloading unrelated professions.
- Combine city, raw status and expiration dates to prepare focused review queues.
- Keep stable credential identifiers and source-refresh dates for downstream comparisons.
- Deduplicate overlapping searches within one run.
- Export structured JSON, CSV or Excel through Apify's dataset interface.

Recurring schedules rerun your search; they do **not** add built-in alerts, change detection or historical snapshots. Store prior exports in your own system if you need comparisons.

### Getting started

1. Open the input tab and retain the default active Smith search for a small useful example.
2. Replace the search with your provider name or exact license number.
3. Set the global maximum unique credentials.
4. Run the Actor, then inspect the dataset and **Search completeness** summary.
5. Export the dataset or send it to your existing credentialing pipeline.

```json
{
  "queries": [{ "name": "Smith", "status": "ACTIVE" }],
  "maxItems": 20
}
```

### Input parameters

| Parameter | Behavior |
| --- | --- |
| `queries` | 1–25 search objects. Omitted: active Smith search. Empty array invalid. |
| `maxItems` | Global unique credential cap across all queries, default 20, range 1–10,000. Zero/unlimited invalid. |

Each query must supply at least one supported filter:

| Filter | Matching semantics |
| --- | --- |
| `name` | Case-insensitive literal substring of the full holder name, trimmed, at least two characters. |
| `licenseNumber` | Exact number, ignoring leading zeros, or exact full code such as `1.069649`, preserving prefix and padding. |
| `city` | Case-insensitive literal substring of public city, trimmed, at least two characters. |
| `status` | Exact raw status after trimming and uppercasing. Unknown status values return no matches. |
| `expirationDateFrom` | Inclusive earliest expiration, real `YYYY-MM-DD`. |
| `expirationDateTo` | Inclusive latest expiration, real `YYYY-MM-DD`; cannot precede the lower bound. |

Filters within one object use **AND**. Multiple objects use **OR**, processed in order with deduplication by credential ID. SQL wildcard characters `%` and `_` are rejected in name/city; there is no wildcard or token-boundary matching. Unknown fields fail validation instead of being silently ignored. Date bounds support 0001-01-01 through 9999-12-30; missing expiration dates do not match a date window.

### Search examples

Exact credential number:

```json
{"queries":[{"licenseNumber":"069649"}],"maxItems":10}
```

Public city and expiration window:

```json
{"queries":[{"city":"Hartford","expirationDateFrom":"2026-01-01","expirationDateTo":"2026-12-31"}],"maxItems":500}
```

Location is the **public address of record**, not necessarily a practice location or a Connecticut address. Connecticut license holders can have out-of-state locations. A city substring can match longer city names; use downstream exact comparison if that matters.

### Output fields

| Field | Meaning |
| --- | --- |
| `credentialId` | Stable source record identifier, useful for deduplication. |
| `holderName` | Public holder name. |
| `fullCredentialCode`, `credentialNumber` | Full official code and bare number as strings. |
| `credentialDescription` | Always Physician/Surgeon. |
| `status`, `statusReason` | Raw state values, not interpreted disciplinary history. |
| `isActive` | Source flag only; null if not supplied. |
| `issueDate`, `effectiveDate`, `expirationDate` | Source date strings, without an invented timezone. |
| `city`, `state`, `zip` | Public location of record, with ZIP preserved as text. |
| `recordRefreshedOn` | State record refresh timestamp, distinct from scrape time. |
| `sourceDatasetUrl` | Official dataset landing page. |
| `verificationUrl` | General interactive eLicense portal, not a person-specific deep link. |
| `scrapedAt` | Retrieval timestamp in UTC. |

Optional source values are null when missing. Street addresses, emails, phone numbers, degree distinctions and discipline histories are not exported.

### Output example

The following anonymized illustration uses the actual output structure; it is not a claim about any named physician:

```json
{
  "credentialId": "123456",
  "holderName": "EXAMPLE PHYSICIAN",
  "fullCredentialCode": "1.012345",
  "credentialNumber": "12345",
  "credentialDescription": "Physician/Surgeon",
  "status": "ACTIVE",
  "statusReason": null,
  "isActive": true,
  "issueDate": "2020-01-01T00:00:00.000",
  "effectiveDate": "2025-01-01T00:00:00.000",
  "expirationDate": "2026-01-31T00:00:00.000",
  "city": "HARTFORD",
  "state": "CT",
  "zip": "06103",
  "recordRefreshedOn": "2025-01-01T00:00:00.000",
  "sourceDatasetUrl": "https://data.ct.gov/Business/State-Licenses-and-Credentials/ngch-56tr",
  "verificationUrl": "https://www.elicense.ct.gov/Lookup/LicenseLookup.aspx",
  "scrapedAt": "2026-01-01T00:00:00.000Z"
}
```

### How much does it cost to verify Connecticut physician licenses?

Pay per event: **$0.001 per valid started run**, plus one `item` event per unique credential saved. Invalid inputs fail before the start event. No-match searches have a start fee but no item fee. Duplicate records have no additional item charge.

| Apify tier | Per credential |
| --- | ---: |
| FREE | $0.00253 |
| BRONZE | $0.0022 |
| SILVER | $0.001716 |
| GOLD | $0.00132 |
| PLATINUM | $0.00132 |
| DIAMOND | $0.00132 |

Estimated BRONZE run totals, including the start event: n=1: $0.0032; n=10: $0.023; n=100: $0.221; n=1,000: $2.201. Your applicable tier and Apify spending limits determine actual billing. The summary has no separate event charge.

### Pagination, completeness and failure behavior

The Actor requests up to 1,000 rows per page in ascending numeric credential-ID order. It stops when the source is exhausted, the global unique cap is reached or the configured charge budget limits saved output. At an exact cap, exhaustion is unknown; do not assume statewide completeness.

The `SUMMARY` key-value record contains run status, saved count, request/retry/byte/scanned/duplicate counters and per-query outcomes. It distinguishes no match, exhausted search, cap/budget termination and queries not attempted. A failed run may retain partial rows; use only as partial evidence, not a completed search.

Transient network errors, HTTP 429 and server errors receive up to three attempts per page with bounded backoff and 20-second request deadlines. Permanent errors, invalid JSON or wrong credential categories fail the run. There is no paid proxy fallback. Data can change during a run; pagination is not an immutable source snapshot.

### Integrations

- Schedule periodic credential exports and join by `credentialId` in your warehouse.
- Use Make, Zapier or webhooks to transfer completed datasets into review queues.
- Export CSV/Excel to compare credential numbers with an existing provider roster.
- Join NPI identifiers separately; a Connecticut credential is not an NPI.

No integration makes clinical, hiring or legal decisions automatically. Keep a human review step for ambiguous identities, expired source evidence and important credentialing decisions.

### API usage

Start once and retain the run ID. Use the run API to check that same run and obtain `defaultDatasetId`; then download a bounded page of its items.

```bash
curl -X POST 'https://api.apify.com/v2/acts/automation-lab~connecticut-medical-license-verification/runs' \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"queries":[{"name":"Smith","status":"ACTIVE"}],"maxItems":100}'
```

JavaScript with the official client:

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/connecticut-medical-license-verification').call({
  queries: [{ name: 'Smith', status: 'ACTIVE' }], maxItems: 100
});
if (run.status !== 'SUCCEEDED') throw new Error(`Run ended ${run.status}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems({ limit: 100 });
console.log(items);
```

Python:

```python
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/connecticut-medical-license-verification').call(
    run_input={'queries': [{'name': 'Smith', 'status': 'ACTIVE'}], 'maxItems': 100})
if run['status'] != 'SUCCEEDED':
    raise RuntimeError(run['status'])
items = client.dataset(run['defaultDatasetId']).list_items(limit=100).items
```

### MCP and AI assistants

Use an Actor-specific hosted endpoint after the Actor is available to your account:

```bash
claude mcp add --transport http apify \
  'https://mcp.apify.com?tools=automation-lab/connecticut-medical-license-verification'
```

Claude Desktop, Cursor, and VS Code: use an HTTP-capable MCP client with the following equivalent editor configuration:

```json
{"mcpServers":{"apify":{"url":"https://mcp.apify.com?tools=automation-lab/connecticut-medical-license-verification"}}}
```

Authenticate through your client's supported Apify connection. Discover actual tool names and argument schemas using `tools/list`. Actor selection also exposes run/data helper tools; it does not expose exactly one tool. Example prompt: “Find up to 20 active Connecticut Physician/Surgeon credentials containing Smith, show raw status and source refresh date, and do not infer permission to practice.”

For an authorized invocation, start once and retain its run ID. A call can return before completion (`waitSecs` 0–45 seconds, default 30). Check **that same run** with bounded backoff (2, 4, 8 seconds, capped at 10) and a 120-second overall deadline. Set the client timeout above the chosen server wait plus transport margin. On timeout recover the known run, never blindly start another. At deadline report pending with the run ID. Terminal failures are failures; any rows are partial.

After success, read bounded pages from the returned dataset ID using the discovered helper schema: for example 20 source rows per page, at most 100 rows or 64 KiB of serialized UTF-8 data admitted to model context. Enforce bytes host-side; disclose oversized omissions or truncation. If your client cannot intercept oversized responses, do not claim a hard byte guarantee. Track source offsets, report continuation offset and partial status, and keep full exports outside model context. Tool discovery transport is not necessarily model-visible context; caching behavior depends on the client, and no numeric token-saving guarantee is made.

### Source freshness and limitations

The source is Connecticut's public State Licenses and Credentials dataset, not a live interactive portal scrape. `scrapedAt` is retrieval time, **not** evidence that the regulator refreshed the record then. Inspect `recordRefreshedOn`; a new run can return unchanged or old data. Important checks should be confirmed at [Connecticut eLicense](https://www.elicense.ct.gov/Lookup/LicenseLookup.aspx).

This Actor does not provide disciplinary histories, alerts, legal practice eligibility, primary-source certification letters or identity assurance. Raw status reasons can contain administrative text; do not reinterpret them as findings of misconduct. Public location need not be a current office. Missing records and name collisions require independent investigation.

### Legality, responsible use and diagnostics

Use public credential data only for lawful, proportionate purposes. Public availability does not remove privacy obligations, regulator terms or your responsibilities for accurate decisions. This Actor is not affiliated with or endorsed by Connecticut government and is not legal advice.

No AI model is used at runtime or to generate credential outputs. Input goes only to the official Connecticut query API and Apify storage; failed-operation diagnostics go to our private error service. No patient data is needed. Apify dataset/KV retention follows your account settings; you can delete run storage in Console or through the API. There is no separate Actor cache or cross-run dataset. For help, use this Actor's Apify Issues tab; do not include sensitive provider or patient details in public messages.

Failed operations send sanitized diagnostic input, exceptions and actor/build/run IDs to our private GlitchTip service for repair. Secret fields and URL query strings are removed. Reports are retained for 30 days. Do not put patient information, credentials or other sensitive data into search filters.

### FAQ and troubleshooting

**Why are there no results?** Check the full code's padding, try a bare license number, remove overly restrictive filters and verify the category at the official portal. A successful empty search is explicitly marked no-match in SUMMARY.

**Why is the portal different?** The open dataset refresh cadence can lag the portal. Compare the source-refresh timestamp and consult the regulator for urgent decisions.

**Does ACTIVE mean the provider may practice?** No. It is a raw source value and does not incorporate every relevant legal or disciplinary fact.

**Can I retrieve nursing or PA credentials?** Not here. This product intentionally scopes every query to Physician/Surgeon.

**Why did fewer than maxItems return?** The source may be exhausted, queries may overlap, or a charge budget may stop output. Inspect SUMMARY rather than assuming an error or complete coverage.

**Can I get historical changes?** No historical endpoint or alert service is included. Schedule repeat exports and compare them in your own system, retaining source timestamps.

### Related Actors

- [Connecticut Nursing License Lookup](https://apify.com/automation-lab/connecticut-nursing-license-lookup): nursing-only credentials, a separate profession scope.
- [NPPES NPI Registry Provider Search](https://apify.com/automation-lab/npi-registry-provider-search): national provider identifiers, not a substitute for state credential status.

# Changelog

This Actor's version history is a separate document: https://apify.com/automation-lab/connecticut-medical-license-verification/changelog.md

# Actor input Schema

## `queries` (type: `array`):

1–25 objects, each with at least one filter. Processed in supplied order. Omitted queries default to active Smith name search; an empty array is invalid. No real-time portal or discipline-history lookup.

## `maxItems` (type: `integer`):

Global run cap across all queries after deduplication, 1–10000; default 20. Zero/unlimited not supported. Pages stop at the cap or charge budget. SUMMARY identifies unattempted queries and non-exhausted searches.

## Actor input object example

```json
{
  "queries": [
    {
      "name": "Smith",
      "status": "ACTIVE"
    }
  ],
  "maxItems": 20
}
```

# Actor output Schema

## `dataset` (type: `string`):

Unique physician credentials in the default dataset.

## `summary` (type: `string`):

Counters, query exhaustion and partial-failure state.

# 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 = {
    "queries": [
        {
            "name": "Smith",
            "status": "ACTIVE"
        }
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/connecticut-medical-license-verification").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 = {
    "queries": [{
            "name": "Smith",
            "status": "ACTIVE",
        }],
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/connecticut-medical-license-verification").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 '{
  "queries": [
    {
      "name": "Smith",
      "status": "ACTIVE"
    }
  ],
  "maxItems": 20
}' |
apify call automation-lab/connecticut-medical-license-verification --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/connecticut-medical-license-verification"
        }
    }
}
```

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/Ec2cnixIcQUkQu2Hf/builds/KWmP1OfeZu222YWtT/openapi.json
