# Gulf Coast EPA Compliance & Enforcement Monitor (ECHO) (`maniginam/gulf-coast-epa-compliance-monitor`) Actor

Search EPA ECHO facilities and enforcement cases in Louisiana, Texas, Mississippi, Alabama, and Florida by parish or county, program, violation, penalty, and industry, or monitor new enforcement on a schedule.

- **URL**: https://apify.com/maniginam/gulf-coast-epa-compliance-monitor.md
- **Developed by:** [Gina Martiny](https://apify.com/maniginam) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.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

## Gulf Coast EPA Compliance & Enforcement Monitor (ECHO)

Search EPA compliance and enforcement records for facilities in Louisiana, Texas, Mississippi, Alabama, and Florida by parish or 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) data services 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 or state civil or criminal 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.

Louisiana alone has about 39,900 active facilities in ECHO, 2,663 with formal enforcement, $71.3 million in penalties on record, and 18,500 enforcement cases.  ECHO refuses any single query over 100,000 rows, so the Actor splits large searches by state, program, and, when needed, county, pages through each one, and removes duplicates for you.

It uses EPA's official public REST services, 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 parish or county.
- 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 the Gulf Coast.
- Industrial-services sales teams looking for facilities with current violations, new orders, or fresh penalties.

### Input

| Field | Type | Description |
| --- | --- | --- |
| `recordType` | choice | `facilities` (default) or `enforcementCases`. |
| `states` | list of states | Any of `LA`, `TX`, `MS`, `AL`, and `FL`.  Default `["LA"]`. |
| `counties` | list of names | Counties or parishes, for example `East Baton Rouge`, `St. James`, or `Harris`.  "Saint" or "St.", with or without "Parish" or "County", in any case.  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: ECHO's own case-activity date filter, see the note below.  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, the date range is passed to ECHO's case search, which matches cases with enforcement activity in that range.  Most matches have a filing, settlement, or closing date inside it, but ECHO also counts activity that is not in the output, so check the dates before relying on them.  ECHO cannot filter enforcement cases by 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, Louisiana chemical plants and refineries in two parishes with a formal enforcement action in the last three years:

```json
{
  "recordType": "facilities",
  "states": ["LA"],
  "counties": ["East Baton Rouge", "Calcasieu"],
  "naicsCodes": ["325", "324"],
  "formalEnforcementWithinYears": 3,
  "maxResults": 500
}
```

### Output

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

```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": 0,
  "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:** $5.00 per 1,000 facilities delivered to your dataset ($0.005 each).
- **Enforcement case record:** $5.00 per 1,000 cases delivered to your dataset ($0.005 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 parishes usually delivers a handful of new or changed records, so it costs pennies a month.
- 500 Louisiana facilities with recent formal enforcement cost about $2.50.
- Every active Louisiana facility, about 39,900 records, costs about $199.
- Every Louisiana enforcement case, about 18,500 records, costs about $93.

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.  ECHO answers each 1,000-row page in about 3 to 4 seconds, and the Actor waits at least a second between requests to respect EPA's servers.  A small filtered run finishes in well under a minute and costs well under $0.01 in Apify compute on top of the event charges.  A full pull of every active Louisiana facility is about 40 pages, a few minutes at 512 MB, on the order of $0.01 to $0.05 in compute, plus about $199 in facility-record events.

### Data source and freshness

Data comes from EPA's Enforcement and Compliance History Online (ECHO) REST services at `https://echodata.epa.gov/echo/`, published by the U.S. Environmental Protection Agency.  EPA refreshes most ECHO data about weekly, and state-reported data can trail what a state agency shows on its own site.  Facility records come from the ECHO all-data facility search, and cases from the ECHO enforcement case search, including state-lead cases that states report to EPA.

When a whole state is too large for one ECHO query, for example every Texas facility including inactive ones, the Actor searches that state one county at a time.  Facilities that ECHO cannot place in a county can be missed in that mode, so add a program or another filter if you need a complete count.

If ECHO is down or refuses a request, the run fails with a short message and keeps every record it already delivered.  Run it again later.

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.

### FAQ

**Do I need an EPA account or API key?**  No.  The Actor reads the same public services that power the ECHO website.

**Why did my search fail with "too many records"?**  ECHO refuses any single query over 100,000 rows.  The Actor splits state-wide facility searches by county on its own, but a single county or a case search can still be too large.  Add a program, date range, or NAICS code and run it again.

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

**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.

# Actor input Schema

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

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

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

Gulf Coast states to search.  Louisiana is the default.

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

County or parish names, such as East Baton Rouge, St. James, or Harris.  Spelling is forgiving: "Saint" or "St.", with or without "Parish" or "County".  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).

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

Cases: enforcement activity on or after this day, as ECHO defines it.  Facilities: last formal action on or after this day.  YYYY-MM-DD.

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

Cases: enforcement activity on or before this day, as ECHO defines it.  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": [
    "LA"
  ],
  "counties": [],
  "programs": [],
  "activeOnly": true,
  "violationStatus": "any",
  "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": [
        "LA"
    ],
    "maxResults": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("maniginam/gulf-coast-epa-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": ["LA"],
    "maxResults": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("maniginam/gulf-coast-epa-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": [
    "LA"
  ],
  "maxResults": 100
}' |
apify call maniginam/gulf-coast-epa-compliance-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,maniginam/gulf-coast-epa-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/7nN05l4CpBOMG6EAx/builds/0aarKac0nD00BC4Pz/openapi.json
