# CT Child Care & Youth Camp Licensing (`j0401/ct-childcare`) Actor

Connecticut child care & youth camp licensing (public open data): three linked tables keyed by case id - violations (353k code findings), inspections (99k by type/status) and investigations (34k complaints). Pull a case's full chain, filter by provider/outcome, or aggregate.

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

## Pricing

$0.10 / 1,000 ct child care licensing records

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Connecticut Child Care & Youth Camp Licensing

Connecticut's Office of Early Childhood licenses every child care provider and youth camp in the state - and publishes its **entire licensing file** as open data: the violations found on each visit, the inspections that found them, and the investigations that followed complaints. This actor turns those three linked tables into a **charged-per-record lookup, filter and aggregate tool**, all keyed by the **case id** that ties them together.

**Built for:** parents and employers vetting a provider, child-care operators researching the regulatory record, compliance and legal teams, and anyone who needs the **enforcement history behind a license** - not just a license lookup.

### What it covers

Three tables from CT OEC, each keyed by **`caseidnt`**:

| Corpus | Rows | What it is |
|---|---|---|
| **violations** | 353,810 | the code finding on a visit - the statute cited (`19a-87b-6(e)`), a plain-language description, and the regulatory text |
| **inspections** | 99,378 | every licensing inspection: type, status, outcome, with the provider's name and license number |
| **investigations** | 33,869 | complaints / self-reports and how they resolved, with the provider's name and license number |

**The through-key is the product.** `caseidnt` links all three: one `case` query returns a case's violations *and* its inspections *and* its investigation in one pull - the full regulatory chain for a single case, across three tables that the state publishes separately (verified: case 381579 returns 101 violations + 6 inspections).

Violation depth: **345,664** findings carry both a plain-language description and the statutory language, tagged by the visit kind (**306,979** primary inspections, **27,119** follow-up visits, 14,279 of the alternate `05-` inspection spelling, 5,433 untagged). Investigation depth: how it arrived (**26,916 complaints**, 6,672 self-reported, 168 licensing-initiated) and how it ended (**24,561 corrective action plans**, 7,607 referred to the legal division, and the rest no-action / voluntarily-closed / exempt).

### The fine print that matters

The **same visit kind is spelled two ways** in the source - `1-Inspection & Findings` and `05-Inspection & Findings` - so a naive substring match on "Inspection" silently welds the two families together (321,258 rows instead of the 306,979 in the primary one). The actor matches the exact source strings, and the filter exposes both. **`closedate` is a text column in MM/DD/YYYY** on violations and inspections (investigations use a real date), normalized to ISO on output. And a handful of visit dates are **junk far-future values** (up to year 2106) - excluded from every query and blanked on output, so a newest-first pull never surfaces them. Some records (28,049 violations, 4,240 inspections) carry **no visit date at all**; those are kept and sorted after the dated rows, so a date range simply won't match them.

### Typical questions

- "Everything on **case 381579** - violations, inspections, investigation."
- "Providers under an inspection still **IN PROCESS**."
- "Investigations that ended in **Referred to Legal Division**."
- "Every **follow-up visit** finding this year."
- "Aggregate the register by **action type**, **inspection status** or **resolution**."

### Inputs

| Input | What it does |
|---|---|
| `mode` | `violations` (default) / `inspections` / `investigations` / `case` / `aggregate` |
| `caseId` | the numeric through-key (use with `mode=case`) |
| `name` / `licenseNumber` | provider identity |
| `actionType` / `inspectionType` / `inspectionStatus` | inspection filters |
| `howReceived` / `resolution` | investigation filters |
| `visitFrom/To` / `openedFrom/To` | date ranges |
| `groupBy` + `corpus` | aggregate dimension over a chosen table |
| `maxResults` | cap rows (default 200) |

**Default run = the 200 most recent violation rows** - fast for the daily auto-test. For a targeted query add a filter; for a broad view use `aggregate`.

### Low cost

**From $0.0001 per record** - billed only for the rows you use, at the platform floor. Cost scales with what you pull, not with the size of the register, and because each record is metered individually there's no per-run charge cap to hit on a big pull.

The hard part here is that Connecticut splits one regulatory story across **three tables that only agree on a case id** - and the case id is a bare integer with no shared natural key, so joining violations to inspections to investigations is a real join, not an address match. On top of that the inspection kinds are **double-spelled** (`1-` / `05-`), so a `like '%Inspection%'` filter quietly returns the wrong family; the close dates are **text in two different formats** across the tables; and the visit dates carry **junk values out to year 2106** that sort to the top of any newest-first pull. Normalizing the through-key, the double-spelled kinds and the mixed text dates into a schema where a `caseId` query returns the whole chain and an `actionType` filter returns exactly one family 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.

### Source

- [Connecticut Open Data: Child Care & Youth Camp Licensing](https://data.ct.gov/) - OEC licensing violations, inspections and investigations, refreshed daily. Public open data. Records are the state's own published fields; not an endorsement of any provider.

# Actor input Schema

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

violations = code findings (default). inspections = licensing inspections. investigations = complaints / self-reports. case = every row for one `caseId` across all three tables. aggregate = one count row per group (see groupBy).

## `corpus` (type: `string`):

Which table to aggregate when mode=aggregate. Ignored in other modes.

## `caseId` (type: `string`):

Numeric OEC case id. The through-key linking violations, inspections and investigations — use with mode=case to pull the whole chain.

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

Provider / facility name substring (inspections and investigations carry the name). Blank = any.

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

Exact license number, e.g. 'DCFH.57872'.

## `actionType` (type: `string`):

The visit kind. Note the source keeps both a '1-' and a '05-' spelling of Inspection & Findings — pick the exact one. Blank = any.

## `inspectionType` (type: `string`):

Inspection type substring, e.g. 'UNANNOUNCED', 'FULL'. Blank = any.

## `inspectionStatus` (type: `string`):

Inspection status. OPENED + IN PROCESS = live work. Blank = any.

## `howReceived` (type: `string`):

How an investigation came in. Blank = any.

## `resolution` (type: `string`):

Outcome substring, e.g. 'Corrective Action Plan', 'Referred to Legal'. Blank = any.

## `visitFrom` (type: `string`):

Only rows with a visit date on/after this date (violations + inspections).

## `visitTo` (type: `string`):

Only rows with a visit date before this date (violations + inspections).

## `openedFrom` (type: `string`):

Only investigations opened on/after this date.

## `openedTo` (type: `string`):

Only investigations opened before this date.

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

Dimension to aggregate over. Blank = the corpus's own default (actionType for violations, inspectionStatus for inspections, resolution for investigations). Available dimensions depend on `corpus`.

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

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

## Actor input object example

```json
{
  "mode": "violations",
  "corpus": "violations",
  "caseId": "",
  "name": "",
  "licenseNumber": "",
  "actionType": "",
  "inspectionType": "",
  "inspectionStatus": "",
  "howReceived": "",
  "resolution": "",
  "visitFrom": "",
  "visitTo": "",
  "openedFrom": "",
  "openedTo": "",
  "groupBy": "",
  "maxResults": 50
}
```

# Actor output Schema

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

Connecticut child care / youth camp licensing 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/ct-childcare").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/ct-childcare").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/ct-childcare --silent --output-dataset

```

## MCP server setup

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

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/9PNyXNdJopgXJWEZy/builds/AG9P44m11R7mmI2AN/openapi.json
