# US EPA ECHO Compliance & Enforcement: All States (`maniginam/us-epa-echo-compliance-monitor`) Actor

Search EPA ECHO facilities and enforcement cases in all 50 states, DC, and territories by county, program, violation, penalty, and NAICS industry, or monitor new enforcement on a schedule.

- **URL**: https://apify.com/maniginam/us-epa-echo-compliance-monitor.md
- **Developed by:** [ManiGinaM](https://apify.com/maniginam) (community)
- **Categories:** Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 facility 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?

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

Search EPA compliance and enforcement records for facilities in all 50 states, Washington DC, Puerto Rico, and the US territories by county, EPA program, violation status, penalties, industry, and date.  Turn on monitor mode and schedule it to get only new enforcement actions, new penalties, and facilities whose violation status changed since your last run.

### What does it do?

This Actor reads the U.S. Environmental Protection Agency's ECHO (Enforcement and Compliance History Online) weekly bulk data downloads and returns clean, normalized records you can drop into a spreadsheet, CRM, or due-diligence workflow.  It can return two kinds of records:

- **Facilities:** one record per EPA-regulated facility, with its address, coordinates, industry codes, Clean Air Act, Clean Water Act, RCRA, and Safe Drinking Water Act IDs, current compliance status by program, significant noncompliance flag, inspections, informal and formal enforcement actions, five-year penalty totals, and a link to the EPA Detailed Facility Report.
- **Enforcement cases:** one record per federal EPA civil enforcement case, with the statute, case status, filing, lodging, settlement, and closing dates, federal and state penalties, SEP and compliance costs, docket numbers, pollutants, and a link to the EPA case report.

ECHO lists about 3.2 million regulated facilities nationwide, active and inactive.  The Actor streams EPA's published files, applies every filter itself, and stops reading as soon as it has `maxResults` records, so there is no row limit and no search is ever too broad.

It uses EPA's official public download files, so there is no browser, no proxy, and no login involved.

### Who is it for?

- Environmental lawyers screening clients, opposing parties, and targets for violations and enforcement history.
- Environmental consultants building Phase I and compliance-audit leads by county anywhere in the US.
- Insurers and lenders' due-diligence teams checking a borrower's or insured's facilities before binding or closing.
- ESG and sustainability teams tracking supplier and portfolio compliance across every state where they operate.
- Industrial-services sales teams looking for facilities with current violations, new orders, or fresh penalties.

### How to use

1. Open the Actor in Apify Console and choose facilities or enforcement cases.
2. Pick one or more states, or leave the list empty to search the whole country.
3. Optionally narrow the search under **Filters** by county, program, violation, penalty, industry, or date.
4. Click **Start** and wait for the run to finish.
5. Open the **Output** tab to preview records, then export them as JSON, CSV, Excel, or XML.
6. To watch for new enforcement, turn on monitor mode and add a schedule.

### Input

| Field | Type | Description |
| --- | --- | --- |
| `recordType` | choice | `facilities` (default) or `enforcementCases`. |
| `states` | list of states | Two-letter codes for any of the 50 states, `DC`, `PR`, `VI`, `GU`, `AS`, and `MP`.  Empty (default) means all of them, so set `maxResults` to match. |
| `counties` | list of names | Counties, parishes, boroughs, or municipios, for example `Harris`, `Los Angeles`, `East Baton Rouge`, or `Cook`.  "Saint" or "St.", with or without "County", "Parish", or "Borough", in any case and with or without accents.  Virginia's independent cities keep "City", for example `Richmond City`.  A name is searched in every selected state that has it.  Facility searches only.  Empty means whole states. |
| `programs` | list of programs | `CAA`, `CWA`, `RCRA`, or `SDWA`.  For cases this picks the primary law.  Empty means every program. |
| `activeOnly` | true or false | Facilities only.  Skip closed and inactive facilities.  Default true. |
| `violationStatus` | choice | Facilities only.  `any` (default), `currentViolation`, or `significantViolation`. |
| `formalEnforcementWithinYears` | number | Facilities only.  Keep facilities with a formal enforcement action in the last 1 to 5 years.  0 (default) turns it off. |
| `minPenaltyUsd` | number | Facilities: five-year penalty total at or above this amount.  Cases: federal plus state penalty at or above it.  0 (default) turns it off. |
| `naicsCodes` | list of codes | NAICS codes or prefixes of 2 to 6 digits, for example `325` for chemicals or `324110` for refineries. |
| `dateFrom` | date | `YYYY-MM-DD`.  Cases: filed, lodged, settled, or closed on or after this day.  Facilities: last formal action on or after this day. |
| `dateTo` | date | `YYYY-MM-DD`, inclusive, with the same meaning as `dateFrom`. |
| `maxResults` | number | Most records to publish and charge in one run.  Default 1000, maximum 250,000. |
| `onlyNewOrChanged` | true or false | Monitor mode.  Outputs only records that are new or changed since the previous run of the same search. |

For cases, a date range matches a case whose `dateFiled`, `dateLodged`, `settlementDate`, or `dateClosed` falls inside it, and `naicsCodes` matches the case's `primaryNaicsCode`.  EPA's case files have no county, so the Actor rejects `counties` for case searches instead of silently returning a whole state.  Facility-only filters are ignored, with a warning in the log, when you search cases.

Sample input, chemical plants and refineries in Houston's Harris County, Texas, and Los Angeles County, California, with a formal enforcement action in the last three years:

```json
{
  "recordType": "facilities",
  "states": ["TX", "CA"],
  "counties": ["Harris", "Los Angeles"],
  "naicsCodes": ["325", "324"],
  "formalEnforcementWithinYears": 3,
  "maxResults": 500
}
```

### Output

Each facility or case is one dataset item.  This is a real facility record from ECHO, from Louisiana:

```json
{
  "recordType": "facility",
  "registryId": "110013921435",
  "facilityName": "ACADIA PARISH POLICE JURY - ACADIA PARISH SANITARY LANDFILL",
  "street": "611 PETAL RD",
  "city": "EGAN",
  "state": "LA",
  "zip": "70531",
  "county": "ACADIA",
  "countyFips": "22001",
  "epaRegion": "06",
  "latitude": 30.256038,
  "longitude": -92.532637,
  "naicsCodes": [
    "562111",
    "562212"
  ],
  "sicCodes": [
    "4953"
  ],
  "programs": [
    "CAA",
    "CWA"
  ],
  "active": true,
  "majorFacility": true,
  "federalFacility": false,
  "airIds": [
    "LA0000002200100143"
  ],
  "npdesIds": [
    "LA0065676"
  ],
  "rcraIds": [],
  "sdwaIds": [],
  "caaPermitTypes": "Major Emissions",
  "cwaPermitTypes": "Non-M",
  "rcraPermitTypes": null,
  "sdwaSystemTypes": null,
  "complianceStatus": "Violation Identified",
  "caaComplianceStatus": "No Violation Identified",
  "cwaComplianceStatus": "Violation Identified",
  "rcraComplianceStatus": null,
  "sdwaComplianceStatus": null,
  "currentViolation": true,
  "significantNoncompliance": false,
  "quartersInNoncompliance": 8,
  "complianceHistory3yr": "VV_V_V__VVVV",
  "inspectionCount": 2,
  "lastInspectionDate": "2025-08-21",
  "informalActionCount": 1,
  "lastInformalActionDate": "2025-09-30",
  "formalActionCount": 1,
  "lastFormalActionDate": "2026-08-06",
  "totalPenaltiesUsd": 0,
  "penaltyCount": null,
  "lastPenaltyDate": "2006-09-20",
  "lastPenaltyAmountUsd": 9000,
  "caaPenaltiesUsd": null,
  "cwaPenaltiesUsd": 0,
  "rcraPenaltiesUsd": null,
  "enforcementActionIds": [
    "LA-WECN2500447",
    "LA000A0000220010014300037"
  ],
  "detailedFacilityReportUrl": "https://echo.epa.gov/detailed-facility-report?fid=110013921435",
  "changeType": "notTracked"
}
```

And a real enforcement case record:

```json
{
  "recordType": "enforcementCase",
  "caseNumber": "06-2005-3416",
  "caseName": "Saint-Gobain Containers Inc (Ardagh Glass)-3 Facilities-CAA Consent Decree (National Case)",
  "activityId": "161265",
  "matchedState": "LA",
  "caseCategory": "Judicial",
  "caseCategoryCode": "JDC",
  "caseStatus": "Closed",
  "caseStatusCode": "CLS",
  "civilOrCriminal": "civil",
  "lead": "EPA",
  "primaryLaw": "CAA",
  "primarySection": "165",
  "laws": [
    "CAA"
  ],
  "primaryNaicsCode": "327213",
  "primarySicCode": "3221",
  "dateFiled": "2010-01-21",
  "dateLodged": "2010-01-21",
  "settlementDate": "2010-05-07",
  "dateClosed": "2026-08-27",
  "enforcementOutcome": "Final Order With Penalty",
  "federalPenaltyUsd": 230000,
  "stateLocalPenaltyUsd": 200000,
  "totalPenaltyUsd": 430000,
  "sepCostUsd": 250000,
  "complianceActionCostUsd": 20900000,
  "costRecoveryUsd": null,
  "settlementCount": 1,
  "courtDocketNumber": "2:10-CV-00121-TSZ",
  "dojDocketNumber": "90-5-2-1-06982/1",
  "pollutants": "Sulfur oxide",
  "caseReportUrl": "https://echo.epa.gov/enforcement-case-report?id=06-2005-3416",
  "changeType": "new"
}
```

You can download results as JSON, CSV, Excel, or XML, or read them through the Apify API.  The dataset has a **Facilities** view and an **Enforcement cases** view.

- Money is in US dollars as numbers.  Dates are `YYYY-MM-DD`.  Lists such as `naicsCodes` and `npdesIds` are arrays.
- Facility counts and penalty totals cover ECHO's standard five-year window.  `complianceHistory3yr` is ECHO's 12-quarter code string, oldest quarter first.
- `currentViolation` and `significantNoncompliance` reflect EPA's latest quarter.  Violations in ECHO are alleged until they are resolved through enforcement or the courts.
- `changeType` is `new` or `changed` in monitor mode and `notTracked` otherwise.
- A value the source stores in an unusable form comes through as null instead of stopping the run.

### Monitor mode

Set `onlyNewOrChanged` to true and add a schedule, for example every Monday at 7 AM Central, since ECHO refreshes about weekly.  The Actor remembers each record's enforcement history for that exact search in a named key-value store.  The first run outputs every matching record as `new`.  Later runs output only new records and records whose history moved.  Unchanged records are skipped and you are not charged for them.

- A facility counts as changed when its current violation or significant noncompliance flag flips, or when it gets a new informal action, formal action, or penalty.  A new inspection on its own does not count.
- A case counts as changed when its status, settlement date, closing date, outcome, or penalties change.
- History is keyed on every filter except `maxResults`.  State, county, program, and code order do not matter.  If you change a filter, including a date you move forward each week, the Actor starts a fresh history and reports every match as `new` again.  Keep the filters fixed for a monitor.
- Set `maxResults` high enough to cover every record you want watched.
- Two schedules running the same search at the same time share one history, and the run that finishes last wins.

### Pricing

This Actor uses pay-per-event pricing.

- **Facility record:** $4.00 per 1,000 facilities delivered to your dataset ($0.004 each).
- **Enforcement case record:** $4.00 per 1,000 cases delivered to your dataset ($0.004 each).
- **Actor start:** Apify's standard start event ($0.00005).
- There is no separate charge per dataset item.

Examples:

- A weekly monitor for a few counties usually delivers a handful of new or changed records, so it costs pennies a month.
- 100 Harris County, Texas, facilities with a current violation cost $0.40.
- 10,000 facilities across several states cost $40.
- Every federal EPA enforcement case in California, about 5,500 records, costs about $22.

You can cap any run with Apify's maximum charge setting.  The Actor stops cleanly when your limit is reached, and you are only charged for records that actually reach your dataset.

#### Estimated compute cost

Default memory is 512 MB, and the Actor stays under about 160 MB.  Facility searches stream EPA's ECHO Exporter file, about 450 MB compressed, from the top and stop once `maxResults` records are found.  A run that fills its limit early, such as 100 Harris County facilities with a current violation, reads a small part of the file and finishes in about a minute for well under $0.01 in Apify compute.  A run that has to read the whole file, such as every facility in a small state or a filter that matches few rows, takes about 2 to 4 minutes, roughly $0.01 to $0.03 in compute on top of the event charges.  Case searches download EPA's 82 MB case file once and take about a minute, well under $0.01 in compute.

### Data source and freshness

Data comes from EPA's ECHO data downloads at `https://echo.epa.gov/files/echodownloads/`, published by the U.S. Environmental Protection Agency for anyone with larger data needs.  EPA refreshes these files about weekly, and state-reported data can trail what a state agency shows on its own site.

- Facility records come from the ECHO Exporter (`echo_exporter.zip`), the same facility summary that powers ECHO's facility search.
- Case records come from the ICIS Federal Enforcement and Compliance (FE\&C) download (`case_downloads.zip`), joined across its facility, penalty, statute, milestone, pollutant, and docket tables.  This file holds federal EPA civil cases only, so state-lead cases and criminal cases are not included.

If EPA's download is down or refuses a request, the run fails with a short message and keeps every record it already delivered.  If EPA publishes a new weekly file while a run is being resumed on another server, the run stops instead of mixing two files.  Run it again later.

The Actor follows `echo.epa.gov`'s robots.txt, which allows its download folder and asks for 10 seconds between requests.  It identifies itself with a descriptive User-Agent, waits at least 10 seconds between requests, backs off and retries on errors, and resumes a dropped download where it stopped instead of starting over.

Data source: U.S. Environmental Protection Agency, Enforcement and Compliance History Online (ECHO), https://echo.epa.gov.

### Is it legal to use this data?

ECHO is a public U.S. government service, and EPA publishes this data for anyone to search and download.  The data is provided as is, without any guarantee that it is complete, current, or free of errors, and violations are alleged until resolved.  It is not legal advice, a site assessment, or an EPA determination, so confirm anything important against ECHO and the responsible state agency.  This Actor is an independent tool and is not affiliated with or endorsed by the U.S. Environmental Protection Agency or any state agency.

### API and integrations

You can start this Actor and read its results with the [Apify API](https://docs.apify.com/api/v2), the Apify Python and JavaScript clients, or the Apify CLI.  The API returns each run's dataset in the same formats as the Console.

Apify integrations can pass results on when a run finishes, for example to Google Sheets, Slack, Zapier, Make, or your own webhook.  Set them up on the Actor's **Integrations** tab, and pair them with a schedule for a hands-off feed.

### FAQ

**Do I need an EPA account or API key?**  No.  The Actor reads the public files EPA publishes on its ECHO data downloads page.

**Why are cases not filtered by county?**  EPA's case download has no county field.  Search facilities by county, then look up their `enforcementActionIds`, or filter cases by state, program, industry, and date.

**Why are there no state-lead or criminal cases?**  The Actor used to read them from ECHO's search services at `echodata.epa.gov`, but that host's robots.txt now disallows all automated access.  EPA's bulk case download, which robots.txt allows, covers federal EPA civil cases only.  Facility records still include state inspections, actions, and penalties that states report to EPA.

**What is the difference between `totalPenaltiesUsd` on a facility and `totalPenaltyUsd` on a case?**  The facility figure is everything assessed against that facility in the last five years.  The case figure is the federal plus state or local penalty in that one case, whenever it happened.

### Related Actors

- [Gulf Coast EPA Compliance Monitor (ECHO)](https://apify.com/maniginam/gulf-coast-epa-compliance-monitor): the same ECHO search, focused on Louisiana, Texas, Mississippi, Alabama, and Florida.
- [Louisiana Oil & Gas Wells & Permits (SONRIS)](https://apify.com/maniginam/louisiana-oil-gas-wells): every Louisiana oil and gas well and drilling permit from SONRIS.
- [Gulf Offshore Drilling Permits Monitor (BSEE)](https://apify.com/maniginam/gulf-offshore-drilling-monitor): new and changed BSEE offshore drilling permits in the Gulf of America.
- [Louisiana Orphan Oil & Gas Wells (SONRIS / OSR)](https://apify.com/maniginam/louisiana-orphan-wells): Louisiana's official Act 404 orphan well inventory.
- [Gulf Coast USACE Permits & Wetland Monitor](https://apify.com/maniginam/gulf-coast-usace-permits): U.S. Army Corps of Engineers permits and wetland determinations on the Gulf Coast.

### Feedback and custom requests

Found a bug, a wrong value, or a source that changed?  Open an issue in the Issues tab on this Actor's page.  Need another field, filter, or source, or a version built for your team?  Describe it in the Issues tab as a custom request.

# Changelog

This Actor's version history is a separate document: https://apify.com/maniginam/us-epa-echo-compliance-monitor/changelog.md

# Actor input Schema

## `recordType` (type: `string`):

Facilities with their compliance, inspection, enforcement, and penalty summary, or individual federal EPA civil enforcement cases.

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

States, DC, or territories to search.  Leave empty to search all of them.  There is no row limit, so even every facility in the country can be searched.

## `counties` (type: `array`):

County names, such as Harris, Los Angeles, Cook, or East Baton Rouge.  Spelling is forgiving: accents, "Saint" or "St.", and "County", "Parish", "Borough", or "Census Area" are all optional.  Virginia independent cities end in "city", like "Richmond city".  A name is searched in every selected state that has it.  Facility searches only.  Leave empty to search whole states.

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

Clean Air Act, Clean Water Act, hazardous waste (RCRA), or Safe Drinking Water Act.  For cases this picks the primary law.  Leave empty for every program.

## `activeOnly` (type: `boolean`):

Skip closed and inactive facilities.  Facility searches only.

## `violationStatus` (type: `string`):

Only facilities with a current alleged violation, or only those in significant noncompliance.  Facility searches only.

## `formalEnforcementWithinYears` (type: `integer`):

Only facilities with a formal enforcement action in the last 1 to 5 years.  0 turns this off.  Facility searches only.

## `minPenaltyUsd` (type: `integer`):

Facilities: total penalties in the last five years at or above this amount.  Cases: federal plus state penalty at or above it.  0 turns this off.

## `naicsCodes` (type: `array`):

NAICS codes or prefixes of 2 to 6 digits, for example 325 (chemicals) or 324110 (refineries).  Cases match on their primary NAICS code.

## `dateFrom` (type: `string`):

Cases: filed, lodged, settled, or closed on or after this day.  Facilities: last formal action on or after this day.  YYYY-MM-DD.

## `dateTo` (type: `string`):

Cases: filed, lodged, settled, or closed on or before this day.  Facilities: last formal action on or before this day.  YYYY-MM-DD.

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

The most records to publish and charge in this run.

## `onlyNewOrChanged` (type: `boolean`):

Remember what this exact search returned before and publish only records that are new or whose enforcement history changed.  Use it on a schedule.

## Actor input object example

```json
{
  "recordType": "facilities",
  "states": [
    "TX"
  ],
  "counties": [
    "Harris"
  ],
  "programs": [],
  "activeOnly": true,
  "violationStatus": "currentViolation",
  "formalEnforcementWithinYears": 0,
  "minPenaltyUsd": 0,
  "naicsCodes": [],
  "maxResults": 100,
  "onlyNewOrChanged": false
}
```

# Actor output Schema

## `results` (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 = {
    "states": [
        "TX"
    ],
    "counties": [
        "Harris"
    ],
    "violationStatus": "currentViolation",
    "maxResults": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("maniginam/us-epa-echo-compliance-monitor").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": ["TX"],
    "counties": ["Harris"],
    "violationStatus": "currentViolation",
    "maxResults": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("maniginam/us-epa-echo-compliance-monitor").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": [
    "TX"
  ],
  "counties": [
    "Harris"
  ],
  "violationStatus": "currentViolation",
  "maxResults": 100
}' |
apify call maniginam/us-epa-echo-compliance-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,maniginam/us-epa-echo-compliance-monitor"
        }
    }
}
```

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/D0YYnEgHQuxBfSgcO/builds/BPzEkouetpUg3Qi2K/openapi.json
