# Connecticut & Colorado Contractor License Feed (`automation-lab/ct-co-trade-license-feed`) Actor

Export official Connecticut and Colorado plumbing and electrical contractor license snapshots. Filter by name, credential, trade and issue or renewal dates for recurring credential checks and new-license monitoring.

- **URL**: https://apify.com/automation-lab/ct-co-trade-license-feed.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 $2.40 / 1,000 item extracteds

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 & Colorado Contractor License Feed

Export **US contractor licenses** from the official Connecticut and Colorado open-data snapshots. Select plumbing or electrical contractors, search by name or credential, and filter original issue or effective/renewal dates for recurring credential checks and new-license monitoring.

This is a focused two-jurisdiction feed, not a nationwide directory or a legal verification service.

### What does it collect?

- Connecticut: electrical limited, unlimited and limited solar contractors; plumbing and piping limited/unlimited contractors.
- Colorado: electrical contractors (`EC`) and plumbing contractors (`PC`).
- Identity, source status, original issue date, renewal/effective date, expiration and available mailing location.
- Dataset provenance and scrape timestamp for downstream comparisons.

Excluded: WA, TX, CT home-improvement contractors, nursing, journeypersons, apprentices, trainees and education providers. A contractor may be a person or a business; mailing addresses can be outside the licensing state.

### Who is it for?

Vendor onboarding teams can match a supplied credential before asking for authoritative confirmation. Compliance analysts can refresh a bounded list of contractor credentials. Data teams can collect recently issued licenses for an internal workflow, subject to responsible-use requirements.

### Why use this feed?

Both sources produce one consistent output shape while preserving their different date semantics. Exact contractor-class allowlists avoid treating electronic nicotine licenses, nursing registrations or home-improvement licenses as plumbing/electrical credentials. Missing fields remain null instead of being invented.

### Getting started

1. Choose CT, CO, or both licensing jurisdictions.
2. Choose electrical, plumbing, or both contractor trades.
3. Optionally enter a name substring, exact credential or inclusive date cutoff.
4. Set a global `maxItems` limit and run.
5. Download the default dataset as JSON, CSV or Excel through Apify.

Example:

```json
{"states":["CO"],"trades":["plumbing"],"issuedSince":"2026-01-01","maxItems":100}
```

### Input parameters

| Parameter | Meaning |
|---|---|
| `states` | Nonempty subset of CT/CO; default both. Processed in supplied order. Licensing jurisdiction, not mailing location. |
| `trades` | Nonempty subset of electrical/plumbing; ANY selected trade, contractor classes only. Default both. |
| `name` | Optional case-insensitive literal substring; outer whitespace trimmed. CT name/business/DBA; CO entity, first/last and joined first-last or first-middle-last names. Not whole-word matching. |
| `credentialNumber` | Exact case-insensitive source field. CT full code with prefix/punctuation; CO numeric string without EC/PC prefix. |
| `issuedSince` | Inclusive YYYY-MM-DD original-issue-date cutoff. |
| `renewedSince` | Inclusive CT effective-date or CO last-renewal-date cutoff, not a generic change timestamp. |
| `maxItems` | Global unique-license cap, default 100, range 1–100000. Zero is invalid. |

All supplied filters combine with AND. Missing dates do not match date filters. Empty arrays, invalid dates, unsupported fields and unsupported jurisdictions fail rather than silently changing scope.

### Output fields

| Field | Meaning |
|---|---|
| `recordId` | Jurisdiction plus stable source credential identity; used for deduplication. |
| `jurisdiction`, `trade` | Licensing state and contractor trade. |
| `name`, `businessName` | Display name and nullable source business/entity name. |
| `credentialNumber`, `credentialType` | Source credential and contractor class. |
| `status`, `statusReason` | Source status; reason is CT-only. |
| `issueDate` | Original issue date, when supplied. |
| `renewalDate` | CT effective date or CO last-renewed date. |
| `expirationDate` | Source expiration date. |
| `sourceRefreshedAt` | CT record-refresh date; null for CO. |
| `address`, `city`, `addressState`, `postalCode` | Mailing location. CO has no street address in this export. |
| `sourceUrl`, `scrapedAt` | Official dataset page and UTC collection timestamp. |

Source date strings are preserved without timezone conversion. Unavailable optional fields are null. The `SUMMARY` key-value-store record reports saved count, selected states/trades and global-limit status. It does not claim exhaustive historical coverage.

### Example output

Representative current Colorado plumbing output, with business identity anonymized:

```json
{
  "recordId":"CO:PC:123",
  "jurisdiction":"CO",
  "trade":"plumbing",
  "name":"Example Plumbing LLC",
  "businessName":"Example Plumbing LLC",
  "credentialNumber":"123",
  "credentialType":"Plumbing Contractor",
  "status":"Active",
  "statusReason":null,
  "issueDate":"2026-04-24T00:00:00.000",
  "renewalDate":"2026-04-24T00:00:00.000",
  "expirationDate":"2027-02-28T00:00:00.000",
  "sourceRefreshedAt":null,
  "address":null,
  "city":"Wheat Ridge",
  "addressState":"CO",
  "postalCode":"80033",
  "sourceUrl":"https://data.colorado.gov/d/7s5z-vewr",
  "scrapedAt":"2026-10-11T06:10:32.057Z"
}
```

### How much does it cost to export contractor licenses?

Pay-per-event pricing includes a one-time start fee and one item event per unique license saved. No separate charge applies to dates, location fields or the summary. The start fee is **$0.005 per run**. Per-license prices depend on qualifying monthly Apify Store spend: FREE $0.0046, BRONZE $0.004, SILVER $0.00312, GOLD/PLATINUM/DIAMOND $0.0024. These are spend tiers, not per-Actor volume discounts.

BRONZE estimates: 1 license $0.009; 25 licenses $0.105; 100 licenses $0.405, including the start fee. Empty valid runs incur only the start fee. A failure after startup may retain that fee and charges for already delivered rows. Check the Pricing tab for active rates; estimates are not guaranteed invoices.

### Limits and completeness

Sources are processed in your supplied order. With CT first, CT can fill the global limit before Colorado is fetched. Select one state per run if you need a specific count from each jurisdiction. Results follow stable source-row order, not newest-first.

Pagination uses bounded pages and deduplicates license identities; CO disciplinary rows can repeat the same license, so the first source row supplies that license's snapshot. Source updates during paging can affect consistency. No cross-run cache, change detector, alerts or historical archive is built in.

A platform spending cap may stop collection before the requested result limit. Such output is partial. Upstream errors fail the run; any rows already saved are partial, not proof of successful complete collection. Transient network/429/5xx failures receive at most three attempts per page. Each request has a 30-second timeout. No automatic paid proxies or browser fallbacks are enabled.

### Integrations and recurring checks

Schedule runs in Apify and compare `recordId`, status and dates in your own database. Use issue-date cutoffs to collect newly issued credentials; keep your own watermark and allow overlap to account for delayed source publication. For spreadsheets, download CSV or connect the dataset to Google Sheets. For ETL, fetch paginated JSON and retain `sourceUrl` and `scrapedAt`.

### API usage

Keep your Apify token in an environment variable, never in shared code.

```bash
curl -X POST "https://api.apify.com/v2/acts/automation-lab~ct-co-trade-license-feed/runs" \
  -H "Authorization: Bearer $APIFY_TOKEN" -H "Content-Type: application/json" \
  -d '{"states":["CO"],"trades":["electrical"],"maxItems":25}'
```

JavaScript:

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/ct-co-trade-license-feed').call({states:['CO'],maxItems:25});
const { items } = await client.dataset(run.defaultDatasetId).listItems({limit:25,offset:0});
console.log(items);
```

Python:

```python
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/ct-co-trade-license-feed').call(run_input={'states':['CO'],'maxItems':25})
print(client.dataset(run['defaultDatasetId']).list_items(limit=25,offset=0).items)
```

### MCP integration

Use the stable Actor-specific hosted endpoint:

```bash
claude mcp add --transport http apify "https://mcp.apify.com?tools=automation-lab/ct-co-trade-license-feed"
```

Claude Desktop, Cursor, and VS Code setup (HTTP-compatible clients):

```json
{"mcpServers":{"apify":{"url":"https://mcp.apify.com?tools=automation-lab/ct-co-trade-license-feed","headers":{"Authorization":"Bearer YOUR_APIFY_TOKEN"}}}}
```

Discover actual tool names and input schemas with read-only `tools/list`. Actor selection also exposes run/dataset/storage tools; it does not expose exactly one tool. Example prompts for authorized MCP usage: “Fetch at most 25 Colorado plumbing contractor licenses issued since January 1, 2026, and show names, statuses and source dates.”

Start once and retain the returned run ID. If nonterminal, check the same run with bounded backoff (2, 4, 8 seconds, capped at 10) and a 120-second total deadline. Set client timeout above server wait plus transport margin. On timeout, recover the known run rather than starting a duplicate. At deadline report pending and the run ID.

After success, read the returned `defaultDatasetId` using explicit offset, limit and fields. A consumer can use pages of 20 rows, at most 100 rows and 64 KiB of serialized content admitted to model context, whichever comes first. Enforce the byte cap host-side; disclose omitted/oversized rows, continuation offset and partial results. Without host interception, do not claim a hard byte cap. Keep full exports outside model context. These are consumer limits, not reduced Actor capabilities or universal token-saving guarantees.

### Legality and responsible use

Official datasets: [Connecticut State Licenses and Credentials](https://data.ct.gov/d/ngch-56tr) and [Colorado Professional and Occupational Licenses](https://data.colorado.gov/d/7s5z-vewr). Public snapshots can lag regulator systems. Confirm consequential decisions with the responsible licensing agency. This Actor does not certify legal eligibility, infer risk scores or supply private records, phone enrichment or owner relationships.

Use personal and business mailing information responsibly and comply with applicable laws and source terms. Do not use the feed for harassment or deceptive outreach.

The Actor uses no AI for extraction, verification or scoring and is not affiliated with either licensing agency. Name/credential filters are sent to the selected official data endpoint. Run inputs, outputs and logs remain in your Apify account under its retention controls until deleted; delete associated datasets and key-value stores when no longer needed. No cross-run copies or Actor-managed cache are kept. For help, use this Actor's Apify Store Issues tab.

Failed operations send sanitized diagnostic input, exceptions and actor/build/run IDs to our private GlitchTip service for repair. Secret fields and URL queries are removed; reports are retained for 30 days.

### FAQ and troubleshooting

**Why is Colorado missing from my two-state output?** The first source filled the global cap. Run CO alone or reverse the order.

**Why does my credential not match?** CT requires its full code, including prefix and punctuation; CO uses the bare number plus selected trade. Exact field equality is not a substring search.

**Does renewedSince mean any record changed?** No. It compares effective/renewal dates. CO provides no record-refresh timestamp here.

**Can I request nurses or journeypersons?** No; only the documented contractor classes are supported.

**Why did a valid query return no rows?** Filters may have no matching source records. A successful empty result is distinct from an upstream failure, which fails the run.

### Related Actors

For another jurisdiction, see [Washington Electrical Contractor Licenses](https://apify.com/automation-lab/washington-electrical-registry). Its coverage is separate and is not included in this feed.

# Changelog

This Actor's version history is a separate document: https://apify.com/automation-lab/ct-co-trade-license-feed/changelog.md

# Actor input Schema

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

Select one or both licensing jurisdictions, not mailing-address states. Sources are processed in the order supplied; duplicates are removed. An earlier source can fill the global limit before later sources are fetched.

## `trades` (type: `array`):

ANY selected trade, restricted to contractor classes: CT limited/unlimited electrical, limited solar and plumbing/piping contractors; CO EC and PC. Excludes journeypersons, apprentices, HIC and nursing.

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

Optional case-insensitive literal substring in CT name, business name or DBA; CO entity name, first name, last name or first-last/first-middle-last name. Outer whitespace is trimmed. Not whole-word or exact-name matching.

## `credentialNumber` (type: `string`):

Optional case-insensitive exact field match after trimming. CT requires the full source code including prefix and punctuation (for example ELC.0123456); CO requires its numeric license-number string without EC/PC prefix. Trade selection distinguishes CO prefixes.

## `issuedSince` (type: `string`):

Optional inclusive YYYY-MM-DD cutoff against original issue date, not scrape time. Missing source dates do not match. Invalid calendar dates fail.

## `renewedSince` (type: `string`):

Optional inclusive YYYY-MM-DD cutoff against CT effective date or CO last renewal date. This is not a general last-change filter. Missing dates do not match; combined with issuedSince using AND.

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

Global limit across selected states after source filtering and license-identity deduplication. Default 100; range 1–100000. Zero is invalid, not unlimited. Upstream exhaustion or the platform charge limit may stop earlier; source order is not newest-first.

## Actor input object example

```json
{
  "states": [
    "CT",
    "CO"
  ],
  "trades": [
    "electrical",
    "plumbing"
  ],
  "maxItems": 20
}
```

# Actor output Schema

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

Filtered unique contractor license snapshots in the overview dataset view

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

Returned license count, chosen scope and limit status

# 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": [
        "CT",
        "CO"
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/ct-co-trade-license-feed").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": [
        "CT",
        "CO",
    ],
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/ct-co-trade-license-feed").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": [
    "CT",
    "CO"
  ],
  "maxItems": 20
}' |
apify call automation-lab/ct-co-trade-license-feed --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/ct-co-trade-license-feed"
        }
    }
}
```

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/Ab5n2QSjHzH0Oz448/builds/eVoxwLpGvnZKio0Ga/openapi.json
