# EPA ECHO Compliance Diff API — CAA, CWA, RCRA, SDWA Enforcement (`changewire/epa-echo-compliance`) Actor

Daily diff feed for EPA ECHO. Emits JSONL records for every change to facility compliance status, violations, inspections, federal enforcement cases, or CAA/CWA/RCRA/SDWA permit listings. Replaces $30-200k/yr enterprise EHS-watch (Enhesa, Enviance) at $20-500/mo. For ESG analysts and EJ litigators.

- **URL**: https://apify.com/changewire/epa-echo-compliance.md
- **Developed by:** [ChangeWire](https://apify.com/changewire) (community)
- **Categories:** AI, Agents
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-usage

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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

## EPA ECHO API — Daily Facility Compliance Diff & Enforcement Case Tracker

> Daily diff of every EPA Enforcement and Compliance History Online
> (ECHO) facility for environmental-justice litigators, ESG screening
> desks, facility-siting due-diligence teams, plaintiffs-bar trade
> press, and state-AG / tribal-environmental enforcement offices.
> Watches ECHO for compliance-status flips, new violations, new
> inspections, new and resolved federal enforcement cases, and
> permit additions across CAA / CWA / RCRA / SDWA programs — emits
> one JSONL change record per detected change.

### What this does

- **Tracks ~840k facilities across all four big federal programs**
  (CAA stationary sources, CWA NPDES discharge permits, RCRA
  hazardous-waste handlers, SDWA public-water systems) with full
  diff coverage on six dimensions (compliance status / violations /
  inspections / enforcement cases / case resolutions / permits).
- **Cuts polluter-watchlist latency from quarter to one day** so
  environmental-justice litigators, ESG analysts, and trade-press
  reporters surface new violations and federal cases the same day
  ECHO refreshes rather than waiting for the next quarterly
  catch-all bulletin.
- **Replaces $30-200k/yr Enhesa / Enviance / Intelex enterprise
  EHS-watch tiers** with a metered pay-per-result Apify actor at
  $0.005 per change record (typically $20-500/month depending on
  watchlist size and violation activity).

### Use cases

#### EPA ECHO API for environmental-justice litigators

A community-law nonprofit watches the 50 highest-emission CAA
facilities in its service area on `registry_ids: [<50 ids>]`,
runs daily at 06:00 UTC, and routes the JSONL diff stream into
its case-intake queue. Any `violation_added` or
`enforcement_case_added` record routes straight to the staff
attorney with the facility name + before/after snapshot fields
+ ECHO source URL. Cost: roughly 5-30 diff records/day across the
50-facility watchlist × $0.005 = $0.75-4.50/day = $20-135/mo.
Replaces a $30-80k/yr Enhesa watch tier with same-day fidelity
rather than next-business-day digest emails.

#### EPA enforcement compliance feed for ESG screening desks

An ESG portfolio-screening desk feeds the REGISTRY_IDs of 800
portfolio-company industrial facilities into the watchlist and
runs daily. Each diff record becomes a structured event the
screening committee's quarterly-flag pipeline can route per-
holding into the firm's stewardship-engagement queue. The desk
saves the engineering cost of building ECHO scraping in-house
(~3-month FTE project at $200k+ loaded) and gets per-event
semantics (compliance flip vs new violation vs new case vs case
resolved) the desk doesn't have to derive from ECHO HTML reports.

#### EPA facility violation tracker for due diligence

A pre-acquisition diligence team filters this actor on a
specific `state` + `programs` combination (e.g. Texas + RCRA),
runs once at engagement kickoff to baseline against a known
target site's neighbors, then re-runs against the same input
30 days later and diffs the two snapshots. New violations or
enforcement cases against neighboring facilities surface
hidden remediation-cost exposure that title search and Phase I
ESA don't catch.

#### CAA CWA RCRA compliance API for plaintiffs-bar trade press

An *Inside EPA* / E&E News / *Environmental Forum* reporter
filters this actor with `programs: ["CAA","CWA","RCRA","SDWA"]`
+ `only_in_violation: true` and runs daily. New
`enforcement_case_added` records surface federal cases before
the EPA press-release cycle catches up, giving the reporter
24-72 hours of beat advantage on the trade-press wire. The
structured `change_type` field lets the reporter tag each event
by category for editorial prioritization without manual ECHO-
report re-parsing.

#### EPA polluter watch tool for state-AG and tribal-environmental offices

A state-AG environmental enforcement unit filters on its state
code + program subset and watches for `enforcement_case_added`
records on facilities the unit cooperates with EPA on. The
structured event stream lets the unit's case-prioritization
desk react to federal cases faster and avoid duplicate-effort
filings against facilities EPA just hit. Tribal-environmental
offices similarly track federal enforcement against facilities
in or adjacent to reservation lands.

### Input

See `.actor/input_schema.json` for the full schema. Key fields:

| Field | Type | Default | Description |
|---|---|---|---|
| `registry_ids` | array of strings | `[]` | Specific EPA Facility Registry Service IDs (e.g. `["110000334761"]`). Empty triggers state + program search. |
| `state` | string | `""` | Two-letter US state code; empty applies no state filter. |
| `programs` | array of strings | `["CAA","CWA"]` | Subset of {CAA, CWA, RCRA, SDWA}. |
| `only_in_violation` | bool | `false` | Drop in-compliance facilities before diff. |
| `enable_enforcement_cases` | bool | `true` | Walk per-facility federal enforcement-case detail. |
| `change_types` | array of strings | all six | Subset of {compliance_status_change, violation_added, inspection_added, enforcement_case_added, enforcement_case_resolved, permit_change}. |
| `max_pages` | int | `1` | Cross-program `get_facilities` pagination ceiling (1-25). |
| `page_size` | int | `200` | Rows per page (50-500). |
| `snapshot_key` | string | `echo-snapshot-latest` | Apify Key-Value Store key for previous snapshot. |
| `echo_base_url` | string | `https://echo.epa.gov/efservice` | Override for offline test mirrors. |
| `rate_limit_rps` | int | `2` | Polite RPS for ECHO GETs. EPA throttles aggressive callers. |

Realistic example input (50-facility environmental-justice litigator watchlist):

```json
{
  "registry_ids": ["110000334761", "110000123456", "110000789012", "..."],
  "programs": ["CAA","CWA","RCRA"],
  "change_types": ["violation_added", "enforcement_case_added", "compliance_status_change"],
  "rate_limit_rps": 2
}
````

### Output

JSONL dataset; one record per detected change:

```json
{
  "actor": "actor-7-epa-echo-compliance",
  "schema_version": "1.0.0",
  "extracted_at": "2026-05-22T06:00:00Z",
  "registry_id": "110000334761",
  "facility_name": "Acme Petroleum Refinery",
  "program": "CAA",
  "state": "TX",
  "current_compliance_status": "Significant Violator",
  "change_type": "enforcement_case_added",
  "before": {"open_cases_count": 0},
  "after": {
    "open_cases_count": 1,
    "newest_case": {
      "case_number": "CAA-06-2026-1234",
      "case_type": "Federal Civil Judicial",
      "filed_date": "2026-05-20",
      "statute": "Clean Air Act §113"
    }
  },
  "detected_at": "2026-05-22T06:00:00Z",
  "source_url": "https://echo.epa.gov/detailed-facility-report?fid=110000334761"
}
```

Apify Console preview shows an 8-column table view (registry id /
facility / program / state / compliance / change type / detected at /
ECHO URL) so analysts and non-engineers can sanity-check runs
before piping into a screening pipeline.

### Pricing & limits

- **$0.005 per result** (per detected diff record).
- **$79/mo subscription tier** for unlimited runs against a saved
  watchlist of up to 500 REGISTRY\_IDs with daily refresh — better
  for buyers with high-violation watchlists.
- **Free 7-day trial** so you can verify diff fidelity against
  your own watchlist before committing.
- **Recommended schedule:** daily at 05:00–07:00 UTC (ECHO
  refreshes overnight; running this window catches the freshest
  changes).
- **Estimated monthly cost:**
  - Environmental-justice litigator (~50 facilities, daily): **$20-135/mo PPR or $79/mo subscription**
  - ESG portfolio screening (~800 facilities, daily): **$120-350/mo PPR or $79/mo subscription if under 500 IDs**
  - Trade-press beat reporter (national-scope, in-violation only): **$50-200/mo PPR**
  - Replaces Enhesa / Enviance / Intelex enterprise EHS tiers
    ($30-200k/yr) with metered per-event fidelity for the
    federal-environmental-data subset.

### Data source & freshness

- **Source:** EPA ECHO REST service
  (`https://echo.epa.gov/efservice`) with optional per-facility
  detailed-facility-report enforcement walk.
- **Update cadence (source-side):** ECHO's underlying database
  refreshes daily for compliance-status flags + new violations +
  inspections; federal enforcement-case data refreshes within
  1-3 days of case-management-system entry. Quarterly ECHO
  snapshots also publish to `https://echo.epa.gov/tools/data-
  downloads` for bulk retrospective use.
- **Actor cadence (recommended):** Daily 06:00 UTC. Running
  more often than ECHO refreshes is harmless but emits zero
  records.
- **Public-records license:** ECHO is published under the Clean
  Air Act (42 USC §7414), Clean Water Act (33 USC §1318), RCRA
  (42 USC §6927), and Safe Drinking Water Act (42 USC §300j-4)
  public-disclosure mandates. 17 USC §105 removes copyright on
  the underlying EPA records. EPA's robots.txt allows
  `/efservice` paths and asks for an identifying User-Agent.
  Actor sets a non-spoofed UA and defaults to 2 rps.

### FAQ

#### What's the difference between ECHO and FRS?

**ECHO** (Enforcement and Compliance History Online) is the
buyer-facing report layer that aggregates 12-quarter rolling
compliance flags, violations, inspections, and enforcement
cases per facility. **FRS** (Facility Registry Service) is the
underlying registry-of-record that assigns each facility its
opaque `REGISTRY_ID`. ECHO records all carry a REGISTRY\_ID; if
you have only a state + program search you don't need to
dereference FRS separately.

#### How does this compare to Enhesa or Enviance?

Enhesa / Enviance / Intelex / Sphera package ECHO watching as
one feature in a $30-200k/yr enterprise EHS-management suite
that also includes 50-state regulatory tracking, foreign-
regulatory tracking, audit-management workflows, and human-
curated weekly reports. This actor does **only** the federal-
ECHO diff layer at $0.005 per change record. Pick this if your
team already does its own multi-jurisdictional regulatory
tracking and just needs a programmatic federal-ECHO feed; pick
Enhesa if you need bundled global EHS + human-curated alerts

- audit workflows.

#### What's a REGISTRY\_ID and how do I get a list?

`REGISTRY_ID` is EPA's opaque identifier for each facility in
the Facility Registry Service. It looks like a 12-digit
integer (e.g. `110000334761`). Most buyers either (a) maintain
their own watchlist via prior ECHO searches, (b) get IDs from
a state-environmental-permit list, or (c) leave the field
empty and pass `state` + `programs` instead — the actor walks
EPA's `get_facilities` endpoint to build the watchlist on
the fly.

#### What change types does this detect?

Six dimensions: `compliance_status_change` (12-quarter
compliance label moves between buckets — In Compliance,
Significant Violator, etc.), `violation_added` (new violation
row), `inspection_added` (new inspection row),
`enforcement_case_added` (new federal case),
`enforcement_case_resolved` (open case got a `settled_date`
populated), `permit_change` (NPDES / Title V permit-IDs dict
gains or loses a key).

#### How does this handle the cross-program walk?

When `registry_ids` is empty, the actor walks
`get_facilities` once per program in the requested
`programs` subset, paginates up to `max_pages` per program
(default 1, max 25), then dereferences each unique
REGISTRY\_ID through the detailed-facility-report endpoint.
The cross-program union is deduplicated on REGISTRY\_ID before
diffing against the previous snapshot. Buyers tracking
specific portfolios should pass explicit `registry_ids` to
skip the listing-page round trip entirely (faster + cheaper).

#### Are there PII or attorney-client risks?

No. ECHO data describes regulated facilities and federal
enforcement actions — there is no individual-PII surface. The
actor still defaults to redacting any free-text contact-name
fields that occasionally appear in case-narrative blocks; set
`redact_contact_names: false` in the input schema only if your
buyer-cohort compliance posture clears full-text retention
(rare).

#### Can I watch by state-AG cooperation status?

Not directly — the actor exposes raw federal-enforcement-case
data without secondary state-AG cooperation labels. State-AG
units typically cross-reference the actor's federal-case
output against their own state-case management system rather
than asking the actor to filter for them. If state-AG
cooperation labels become a frequent ask, file an issue and
we'll add an enrichment dimension.

#### Does this cover SDWA public-water systems?

Yes — SDWA is fully supported in `programs: ["SDWA"]`. SDWA
records have a slightly different compliance-flag structure
than CAA / CWA / RCRA (water-system serious-violator vs
compliance-monitoring-only) — the actor maps SDWA buckets into
the same `current_compliance_status` field-shape as the other
programs to keep buyer-side schema clean.

#### Is there a rate-limit risk?

The actor self-limits to 2 rps and respects EPA's robots.txt
(allows `/efservice` paths). EPA throttles aggressive callers
above ~10 rps in practice; we stay well below. Bulk runs
against 1,000+ facilities take ~10-15 minutes wall time at
2 rps depending on per-facility detail walks.

### Companion actors in this portfolio

`changewire` maintains a portfolio of federal-data diff actors. All
share the same daily-snapshot + JSONL change-record pattern, watchlist
filtering, and per-record change-type semantics. Pick the data domain
you need; mix and match across a single Apify account:

- [`changewire/clinicaltrials-protocol-diff`](https://apify.com/changewire/clinicaltrials-protocol-diff) — NIH-registered trial protocol diffs.
- [`changewire/grantsgov-opportunity-alert`](https://apify.com/changewire/grantsgov-opportunity-alert) — Daily diff of federal grant opportunities (status, deadline, award amount, eligibility).
- [`changewire/fda-orange-book-extraction`](https://apify.com/changewire/fda-orange-book-extraction) — FDA Orange Book pharmaceutical applications + patent listings, daily diff. *(currently hard-blocked at the Akamai WAF; awaiting routing fix)*
- [`changewire/noaa-storm-events-diff`](https://apify.com/changewire/noaa-storm-events-diff) — NOAA NCEI storm-event records (tornados, hail, floods) with daily incremental diff.
- [`changewire/uspto-ttab-docket-extractor`](https://apify.com/changewire/uspto-ttab-docket-extractor) — USPTO TTAB trademark docket records + filings + parties + deadlines, on-demand extraction.
- [`changewire/samgov-contracts-diff`](https://apify.com/changewire/samgov-contracts-diff) — SAM.gov federal contract opportunities, daily diff. *(currently hard-blocked on identity-tied SAM.gov API key; awaiting user-machine key generation)*

### Running locally (for contributors)

```bash
cd actors/actor-7-epa-echo-compliance
apify run --purge  ## uses .actor/input_schema.json defaults
```

State + program run:

```bash
apify run --input '{"state": "TX", "programs": ["CAA","RCRA"], "max_pages": 3}'
```

Watchlist run with violation filter:

```bash
apify run --input '{"registry_ids": ["110000334761","110000123456"], "only_in_violation": true}'
```

### Legal

ECHO data is published by EPA under the Clean Air Act (42 USC §7414),
Clean Water Act (33 USC §1318), RCRA (42 USC §6927), and Safe Drinking
Water Act (42 USC §300j-4) public-disclosure mandates; 17 USC §105
removes copyright on the underlying records. Actor honors EPA's
robots.txt for `/efservice` paths, sets a non-spoofed identifying
User-Agent, and defaults to 2 rps to stay below ECHO's aggressive-
caller throttle. No PII in output: ECHO data describes regulated
facilities, not individuals. Free-text contact-name fields that
occasionally appear in case narratives are redacted on ingest by
default.

# Actor input Schema

## `registry_ids` (type: `array`):

Specific EPA REGISTRY\_ID values to track. If empty, falls through to state + program filters.

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

Two-letter US state code (e.g. TX, CA). Empty = no state filter (use cautiously: full national crawl may exceed page budget).

## `programs` (type: `array`):

Subset of CAA / CWA / RCRA / SDWA. Case-insensitive. Empty = all programs.

## `only_in_violation` (type: `boolean`):

If true, drops facilities whose 12-quarter noncompliance count is 0 before diffing.

## `enable_enforcement_cases` (type: `boolean`):

If true, fetch /case\_rest\_services.get\_cases for every facility. Adds ~1 request per facility — set false for fast surveys.

## `max_pages` (type: `integer`):

Number of get\_facilities pages to walk when no explicit registry\_ids provided. Each page is up to page\_size rows.

## `page_size` (type: `integer`):

ECHO accepts up to ~500 per page; default 200 is a polite middle ground.

## `change_types` (type: `array`):

Subset of: compliance\_status\_change, violation\_added, inspection\_added, enforcement\_case\_added, enforcement\_case\_resolved, permit\_change. Empty = emit all.

## `snapshot_key` (type: `string`):

Key-value store key holding the previous snapshot. Default: 'echo-snapshot-latest'.

## `echo_base_url` (type: `string`):

Override the ECHO REST base. Default https://echo.epa.gov/efservice. Useful for offline test mirrors.

## `rate_limit_rps` (type: `integer`):

EPA ECHO does not publish a public RPS limit but throttles aggressive callers. Default 2 is polite.

## Actor input object example

```json
{
  "registry_ids": [],
  "state": "",
  "programs": [
    "CAA",
    "CWA"
  ],
  "only_in_violation": false,
  "enable_enforcement_cases": true,
  "max_pages": 1,
  "page_size": 200,
  "change_types": [
    "compliance_status_change",
    "violation_added",
    "inspection_added",
    "enforcement_case_added",
    "enforcement_case_resolved",
    "permit_change"
  ],
  "snapshot_key": "",
  "echo_base_url": "",
  "rate_limit_rps": 2
}
```

# 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("changewire/epa-echo-compliance").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("changewire/epa-echo-compliance").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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 changewire/epa-echo-compliance --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=changewire/epa-echo-compliance",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "EPA ECHO Compliance Diff API — CAA, CWA, RCRA, SDWA Enforcement",
        "description": "Daily diff feed for EPA ECHO. Emits JSONL records for every change to facility compliance status, violations, inspections, federal enforcement cases, or CAA/CWA/RCRA/SDWA permit listings. Replaces $30-200k/yr enterprise EHS-watch (Enhesa, Enviance) at $20-500/mo. For ESG analysts and EJ litigators.",
        "version": "0.1",
        "x-build-id": "myRaO1Py0IN0qAGa8"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/changewire~epa-echo-compliance/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-changewire-epa-echo-compliance",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/changewire~epa-echo-compliance/runs": {
            "post": {
                "operationId": "runs-sync-changewire-epa-echo-compliance",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/changewire~epa-echo-compliance/run-sync": {
            "post": {
                "operationId": "run-sync-changewire-epa-echo-compliance",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "registry_ids": {
                        "title": "Registry ids",
                        "type": "array",
                        "description": "Specific EPA REGISTRY_ID values to track. If empty, falls through to state + program filters.",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "state": {
                        "title": "State filter (2-letter code)",
                        "type": "string",
                        "description": "Two-letter US state code (e.g. TX, CA). Empty = no state filter (use cautiously: full national crawl may exceed page budget).",
                        "default": ""
                    },
                    "programs": {
                        "title": "Program filter",
                        "type": "array",
                        "description": "Subset of CAA / CWA / RCRA / SDWA. Case-insensitive. Empty = all programs.",
                        "default": [
                            "CAA",
                            "CWA"
                        ],
                        "items": {
                            "type": "string"
                        }
                    },
                    "only_in_violation": {
                        "title": "Only emit facilities currently in violation",
                        "type": "boolean",
                        "description": "If true, drops facilities whose 12-quarter noncompliance count is 0 before diffing.",
                        "default": false
                    },
                    "enable_enforcement_cases": {
                        "title": "Walk enforcement-case detail",
                        "type": "boolean",
                        "description": "If true, fetch /case_rest_services.get_cases for every facility. Adds ~1 request per facility — set false for fast surveys.",
                        "default": true
                    },
                    "max_pages": {
                        "title": "Max pages of cross-program facility lookup",
                        "minimum": 1,
                        "maximum": 25,
                        "type": "integer",
                        "description": "Number of get_facilities pages to walk when no explicit registry_ids provided. Each page is up to page_size rows.",
                        "default": 1
                    },
                    "page_size": {
                        "title": "Page size for facility lookup",
                        "minimum": 50,
                        "maximum": 500,
                        "type": "integer",
                        "description": "ECHO accepts up to ~500 per page; default 200 is a polite middle ground.",
                        "default": 200
                    },
                    "change_types": {
                        "title": "Change types to emit",
                        "type": "array",
                        "description": "Subset of: compliance_status_change, violation_added, inspection_added, enforcement_case_added, enforcement_case_resolved, permit_change. Empty = emit all.",
                        "default": [
                            "compliance_status_change",
                            "violation_added",
                            "inspection_added",
                            "enforcement_case_added",
                            "enforcement_case_resolved",
                            "permit_change"
                        ],
                        "items": {
                            "type": "string"
                        }
                    },
                    "snapshot_key": {
                        "title": "Snapshot key",
                        "type": "string",
                        "description": "Key-value store key holding the previous snapshot. Default: 'echo-snapshot-latest'.",
                        "default": ""
                    },
                    "echo_base_url": {
                        "title": "ECHO base URL override",
                        "type": "string",
                        "description": "Override the ECHO REST base. Default https://echo.epa.gov/efservice. Useful for offline test mirrors.",
                        "default": ""
                    },
                    "rate_limit_rps": {
                        "title": "Polite rate limit (requests per second)",
                        "minimum": 1,
                        "maximum": 5,
                        "type": "integer",
                        "description": "EPA ECHO does not publish a public RPS limit but throttles aggressive callers. Default 2 is polite.",
                        "default": 2
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
