# EPA ECHO Enforcement & Compliance Scraper (`crawlerbros/epa-echo-enforcement-scraper`) Actor

Scrape EPA ECHO enforcement case data and facility compliance status. Search cases by state, statute (Clean Air Act, Clean Water Act, RCRA, etc.), civil/criminal type, and date filed. Search facilities by name, ZIP, or state for inspections, violations, and penalties. No API key required.

- **URL**: https://apify.com/crawlerbros/epa-echo-enforcement-scraper.md
- **Developed by:** [Crawler Bros](https://apify.com/crawlerbros) (community)
- **Categories:** Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#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

## EPA ECHO Enforcement & Compliance Scraper

Scrape enforcement and compliance data from the EPA's [ECHO (Enforcement and Compliance History Online)](https://echo.epa.gov) database — the U.S. government's public system of record for environmental enforcement actions and facility compliance status under the Clean Air Act, Clean Water Act, RCRA, Safe Drinking Water Act, and more. Free, official EPA data — no API key, login, or cookies required.

### What data can you scrape?

- **Enforcement cases** — civil and criminal actions brought by EPA, DOJ, and states: case status, statute cited, penalties assessed, settlement dates, and outcomes
- **Facility compliance status** — inspection counts, violation history, high-priority violator flags, and penalty history for any EPA-regulated facility
- Coverage across all 50 US states, DC, and US territories (Puerto Rico, Guam, US Virgin Islands, American Samoa, Northern Mariana Islands)

### Three search modes

#### 1. Search Enforcement Cases (`searchCases`)

Search EPA/state/DOJ enforcement actions by state or EPA region plus any combination of:

- **Statute** — Clean Air Act, Clean Water Act, RCRA (hazardous waste), Safe Drinking Water Act, EPCRA, CERCLA/Superfund, FIFRA, TSCA
- **Case category** — Judicial (filed in court) or Administrative (EPA-issued order)
- **Civil or Criminal**
- **Case status** — Complaint Filed, Final Order Issued, Closed, Concluded, and more
- **Date filed range**
- **EPA Region** — filter to one of the 10 EPA administrative regions instead of (or alongside) a single state
- **NAICS / SIC industry code** — restrict to defendants/facilities in a specific industry
- Federal-facility-only, cross-media-case-only, and tribal-land-only toggles

#### 2. Search Facility Compliance Status (`searchFacilities`)

Look up any EPA-regulated facility by name, ZIP code, SIC industry code, or "major facility" status within a state, and get its full compliance snapshot: air/water/hazardous-waste/drinking-water compliance status, 5-year inspection and violation counts, penalty history, and Toxic Release Inventory data where reported.

#### 3. Look Up Facilities by Registry ID (`byRegistryIds`)

Already have EPA FRS Registry IDs (e.g. from a previous `searchFacilities` run, or from another dataset)? Fetch those exact facilities directly — no state or name search needed. Accepts one or more IDs in a single run.

### Output fields

#### Enforcement case records

| Field | Description |
|---|---|
| `caseNumber` | EPA ICIS case identifier |
| `caseName` | Defendant / facility name on the case |
| `caseCategory` / `caseCategoryCode` | Judicial or Administrative - Formal, and its short EPA code (`JDC`/`AFR`) |
| `caseStatus` / `caseStatusCode` | Current case milestone (e.g. Final Order Issued, Closed) and its short EPA code |
| `civilCriminalType` | Civil or Criminal |
| `primaryLaw` | Statute cited (CAA, CWA, RCRA, etc.) |
| `primarySection` | Specific section of the law cited |
| `courtDocketNumber`, `dojDocketNumber` | Court / DOJ identifiers (when applicable) |
| `leadAgency` | EPA or delegated state agency |
| `activityId` | EPA ICIS internal activity identifier for the case |
| `prosecutionSummaryId` | EPA ICIS internal prosecution summary identifier (when applicable) |
| `enforcementOutcome` | Final resolution of the case |
| `primaryNaicsCode` | Defendant's industry classification |
| `dateFiled`, `dateLodged`, `settlementDate`, `dateClosed` | Case milestones (ISO dates) |
| `federalPenaltyUsd` | Federal penalty assessed/agreed to |
| `stateLocalPenaltyUsd` | State or local penalty amount |
| `sepCostUsd` | Supplemental Environmental Project cost |
| `costRecoveryUsd` | Superfund cost recovery awarded |
| `complyingActionValueUsd` | Value of required remediation/compliance actions |
| `settlementCount` | Number of settlements in the case |
| `onTribalLand` | true if the case involves tribal land |
| `caseReportUrl` | Link to the public ECHO enforcement case report |

#### Facility compliance records

| Field | Description |
|---|---|
| `registryId` | EPA Facility Registry Service (FRS) ID |
| `facilityName`, `streetAddress`, `city`, `state`, `zipCode`, `county` | Facility identity and location |
| `latitude` | GPS latitude |
| `sicCodes`, `naicsCodes` | Industry classification codes |
| `isActive` | Whether EPA currently lists the facility as active |
| `federalFacility` | true if federally owned/operated |
| `overallComplianceStatus` | Cross-program compliance summary |
| `airComplianceStatus`, `waterComplianceStatus`, `hazardousWasteComplianceStatus`, `drinkingWaterComplianceStatus` | Per-program compliance status |
| `significantNoncompliance` | true if flagged for significant noncompliance |
| `airHighPriorityViolator` | true if flagged as a Clean Air Act high-priority violator |
| `quartersInNoncomplianceLast3yr` | Number of quarters out of the last 12 with noncompliance |
| `inspectionCount5yr`, `airEvaluationCount5yr`, `waterInspectionCount5yr`, `hazardousWasteInspectionCount5yr` | 5-year inspection counts |
| `daysSinceLastInspection`, `lastInspectionDate` | Most recent inspection |
| `penaltyCount5yr`, `lastPenaltyDate`, `airPenaltiesUsd5yr` | Penalty history |
| `airLastPenaltyAmountUsd`, `airLastPenaltyDate` | Most recent Clean Air Act penalty amount and date |
| `airFormalActionCount`, `drinkingWaterFormalActionCount`, `lastFormalActionDate` | Formal enforcement action counts |
| `airLastFormalActionDate`, `waterLastFormalActionDate` | Most recent per-program (Clean Air Act / Clean Water Act) formal enforcement action date |
| `waterComplianceTrackingStatus` | Clean Water Act compliance tracking status (`On` / `Off` / `Partial`) |
| `triReleasesTransfersLbs`, `triOnSiteReleasesLbs` | Toxic Release Inventory data (when the facility reports) |
| `regulatedUnderCleanAirAct`, `reportsToxicReleaseInventory` | Program-membership flags |
| `populationDensity`, `watershedName`, `fipsCode` | Geographic/demographic context |
| `detailedFacilityReportUrl` | Link to the public ECHO Detailed Facility Report |

Every record also includes `recordType` and `scrapedAt`. Fields that EPA has no data for are simply omitted — no `null`, empty string, or placeholder values are ever returned.

### Input parameters

| Parameter | Type | Applies to | Description |
|---|---|---|---|
| `mode` | Select | all | `searchCases`, `searchFacilities`, or `byRegistryIds` |
| `state` | Select | searchCases, searchFacilities | US state/territory (optional for searchFacilities — narrows a nationwide search; either this or `epaRegion` required for searchCases) |
| `epaRegion` | Select | searchCases | One of the 10 EPA administrative regions — alternative or addition to `state` |
| `naicsCode` | Text | searchCases | NAICS industry code (full 6-digit or a shorter prefix for a broader sector) |
| `sicCode` | Text | searchCases, searchFacilities | SIC industry code (full 4-digit or a shorter prefix for a broader group) |
| `lawCited` | Select | searchCases | Restrict to one statute |
| `caseCategory` | Select | searchCases | Judicial or Administrative |
| `civilCriminalIndicator` | Select | searchCases | Civil or Criminal |
| `caseStatus` | Select | searchCases | Case milestone/status |
| `dateFiledFrom` / `dateFiledTo` | Text (MM/DD/YYYY) | searchCases | Date-filed range |
| `federalFacilityOnly` | Boolean | searchCases | Federal facilities only |
| `crossMediaOnly` | Boolean | searchCases | Cross-media cases only |
| `tribalLandOnly` | Boolean | searchCases | Tribal-land cases only |
| `facilityName` | Text | searchFacilities | Partial facility/company name |
| `zipCode` | Text | searchFacilities | 5-digit ZIP code |
| `majorFacilitiesOnly` | Boolean | searchFacilities | EPA-designated major facilities only |
| `activeOnly` | Boolean | searchFacilities | Currently active facilities only |
| `registryIds` | Array of text | byRegistryIds | One or more EPA FRS Registry IDs (required for this mode) |
| `maxItems` | Integer | all | Maximum records to return (1–2000) |

> **Note:** `searchFacilities` requires `facilityName`, `zipCode`, `sicCode`, or `majorFacilitiesOnly=true` in addition to `state` — EPA's API rejects unrestricted state-wide facility scans because the result set (hundreds of thousands of rows in large states) exceeds its query limit. `searchCases` requires `state`, `epaRegion`, or both.
>
> **Note:** EPA's own API caps a single query at 1,000 matching rows for `searchCases` and 5,000 for `searchFacilities`, regardless of `maxItems`. If a state/statute combination has more matches than that, narrow further with `lawCited`, `caseCategory`, or `dateFiledFrom`/`dateFiledTo` (for cases) or `facilityName`/`zipCode` (for facilities) to reach additional rows.
>
> **Note:** `facilityName` is matched by EPA's own ECHO database, not by this actor — it can occasionally return a facility whose *current* displayed name doesn't visibly contain your search term (e.g. searching `chevron` can surface a `7-ELEVEN` gas-station location). This happens because EPA's FRS registry links facilities to prior/associated business names (e.g. a rebranded gas station) that aren't exposed as a separate field in the API response, so this actor can't label or strip those out without discarding legitimate matches. If you need an exact-name-only result set, filter the actor's output on `facilityName` after the run.

#### Example: Clean Air Act enforcement cases in Texas since 2020

```json
{
  "mode": "searchCases",
  "state": "TX",
  "lawCited": "CAA",
  "dateFiledFrom": "01/01/2020",
  "dateFiledTo": "12/31/2023",
  "maxItems": 100
}
```

#### Example: criminal enforcement cases nationwide (by state loop) with penalties

```json
{
  "mode": "searchCases",
  "state": "CA",
  "civilCriminalIndicator": "CR",
  "maxItems": 50
}
```

#### Example: compliance status for all Chevron facilities in California

```json
{
  "mode": "searchFacilities",
  "state": "CA",
  "facilityName": "chevron",
  "maxItems": 100
}
```

#### Example: all major regulated facilities in Rhode Island

```json
{
  "mode": "searchFacilities",
  "state": "RI",
  "majorFacilitiesOnly": true,
  "maxItems": 200
}
```

#### Example: enforcement cases across all of EPA Region 9 (AZ, CA, HI, NV + territories)

```json
{
  "mode": "searchCases",
  "epaRegion": "09",
  "lawCited": "CWA",
  "maxItems": 100
}
```

#### Example: enforcement cases against meat-packing plants (SIC 2011) in Texas

```json
{
  "mode": "searchCases",
  "state": "TX",
  "sicCode": "2011",
  "maxItems": 50
}
```

#### Example: look up specific facilities by known Registry ID

```json
{
  "mode": "byRegistryIds",
  "registryIds": ["110002899908", "110065720410"]
}
```

### Use cases

- **ESG / compliance due diligence** — screen a company's facilities for environmental violations and penalties before an investment or acquisition
- **Legal & regulatory research** — track enforcement case outcomes and penalty trends by statute or state
- **Environmental justice research** — cross-reference facility violation history with community impact data
- **Competitive/industry intelligence** — monitor a sector's (NAICS-coded) compliance record across facilities
- **Journalism** — investigate polluters, penalty history, and enforcement trends in a given region
- **Supply chain risk** — check whether a vendor's manufacturing facilities carry active violations

### FAQ

**What is EPA ECHO?** ECHO (Enforcement and Compliance History Online) is the EPA's public database consolidating compliance and enforcement data for facilities regulated under federal environmental laws. It's the same data EPA itself publishes at [echo.epa.gov](https://echo.epa.gov).

**Do I need an API key?** No. This actor uses EPA's public, unauthenticated REST API.

**Why does `searchFacilities` need more than just a state?** EPA's facility database contains hundreds of thousands of regulated entities in large states. The API itself rejects a request if the estimated result set is too large, so a name, ZIP code, or the major-facility toggle is required to narrow it — this mirrors the same requirement on EPA's own [Facility Search](https://echo.epa.gov/facilities/facility-search) tool.

**What does "major facility" mean?** EPA designates certain larger regulated sources (e.g. large power plants, refineries) as "major facilities" subject to more frequent inspection and reporting.

**How current is the data?** ECHO is updated regularly by EPA (typically weekly for most compliance data); this actor always reflects whatever EPA's live API returns at run time.

**Can I search multiple states at once?** Use `epaRegion` to search all states within one of EPA's 10 administrative regions in a single run (see the regional groupings in the `epaRegion` dropdown), or run the actor once per state for finer control.

**What's the difference between `naicsCode` and `sicCode`?** Both are industry classification systems; NAICS (North American Industry Classification System) is the newer, more granular standard, while SIC (Standard Industrial Classification) is the older four-digit system EPA still uses for facility records. `naicsCode` filters `searchCases` by defendant industry; `sicCode` works on both `searchCases` and `searchFacilities` — EPA's facility-search endpoint doesn't support a NAICS filter (it's a case-search-only field). Both accept a short prefix (e.g. `"31"` or `"20"`) to match a whole industry sector, or the full code for an exact match.

**Are civil and criminal cases both covered?** Yes — use `civilCriminalIndicator` to filter to one or leave it blank for both.

**Why does a case with `lawCited: "CWA"` sometimes show `primaryLaw: "RCRA"` in the output?** Many enforcement cases involve violations of more than one environmental statute (a "cross-media" case). EPA's `lawCited` filter matches any case that cites the given statute, not only cases where it's the *primary* law — but ECHO's API only exposes a single `primaryLaw` field per case (the statute EPA classifies as the lead violation), not the full list of laws cited. So a `lawCited: "CWA"` search can correctly return a case whose `primaryLaw` is `RCRA` if that case also involved a Clean Water Act violation. This mirrors exactly what EPA's own [ECHO Enforcement Case Search](https://echo.epa.gov/enforcement-case-search) shows for the same filter.

# Actor input Schema

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

searchCases: EPA/state enforcement cases (civil & criminal, penalties, statutes). searchFacilities: facility-level compliance status, inspections, and violations. byRegistryIds: exact lookup of known facilities by EPA FRS Registry ID.

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

US state or territory. Optional for searchFacilities (narrows a nationwide search — still requires facilityName/zipCode/sicCode/majorFacilitiesOnly to keep result sets manageable). For searchCases, either this or `epaRegion` is required (EPA's API requires a state, region, or similarly narrow filter to keep result sets under its size limit). Not used by byRegistryIds.

## `registryIds` (type: `array`):

One or more EPA FRS Registry IDs (e.g. from a prior searchFacilities run's `registryId` field) to fetch directly.

## `epaRegion` (type: `string`):

Filter to one EPA administrative region instead of (or in addition to) a single state. Either `state` or `epaRegion` is required for searchCases.

## `naicsCode` (type: `string`):

Filter enforcement cases to a NAICS industry code. Accepts a full 6-digit code (e.g. '311512' for creamery butter manufacturing) or a shorter prefix to match a whole industry sector (e.g. '31' for all manufacturing).

## `sicCode` (type: `string`):

Filter to a Standard Industrial Classification code. Accepts a full 4-digit code (e.g. '2011' for meat packing plants) or a shorter prefix to match a broader industry group (e.g. '20' for food & kindred products).

## `lawCited` (type: `string`):

Restrict enforcement cases to a specific environmental statute.

## `caseCategory` (type: `string`):

Judicial (filed in court) or Administrative (EPA-issued order) enforcement action.

## `civilCriminalIndicator` (type: `string`):

Restrict to civil or criminal enforcement cases.

## `caseStatus` (type: `string`):

Restrict to a specific enforcement case milestone/status.

## `dateFiledFrom` (type: `string`):

Earliest case-filed date, format MM/DD/YYYY (e.g. 01/01/2020).

## `dateFiledTo` (type: `string`):

Latest case-filed date, format MM/DD/YYYY (e.g. 12/31/2023).

## `federalFacilityOnly` (type: `boolean`):

Only return cases involving a federally owned/operated facility.

## `crossMediaOnly` (type: `boolean`):

Only return cases spanning multiple environmental programs (air, water, waste, etc.).

## `tribalLandOnly` (type: `boolean`):

Only return cases involving facilities on tribal land.

## `facilityName` (type: `string`):

Partial facility or company name to search for (e.g. 'chevron'). Recommended alongside state to keep results manageable.

## `zipCode` (type: `string`):

5-digit US ZIP code to search within.

## `majorFacilitiesOnly` (type: `boolean`):

Only return EPA-designated 'major' facilities (larger regulated sources). Can be used alone with state (no name/ZIP needed) since it is a narrow enough filter.

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

Only return facilities currently marked active by EPA.

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

Maximum number of records to return.

## Actor input object example

```json
{
  "mode": "searchCases",
  "state": "CA",
  "registryIds": [
    "110002899908"
  ],
  "epaRegion": "",
  "naicsCode": "",
  "sicCode": "",
  "lawCited": "",
  "caseCategory": "",
  "civilCriminalIndicator": "",
  "caseStatus": "",
  "dateFiledFrom": "",
  "dateFiledTo": "",
  "federalFacilityOnly": false,
  "crossMediaOnly": false,
  "tribalLandOnly": false,
  "facilityName": "chevron",
  "zipCode": "",
  "majorFacilitiesOnly": false,
  "activeOnly": false,
  "maxItems": 25
}
```

# Actor output Schema

## `records` (type: `string`):

Dataset containing all scraped EPA ECHO enforcement cases and/or facility compliance records.

# 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 = {
    "mode": "searchCases",
    "state": "CA",
    "registryIds": [
        "110002899908"
    ],
    "epaRegion": "",
    "naicsCode": "",
    "sicCode": "",
    "lawCited": "",
    "caseCategory": "",
    "civilCriminalIndicator": "",
    "caseStatus": "",
    "dateFiledFrom": "",
    "dateFiledTo": "",
    "federalFacilityOnly": false,
    "crossMediaOnly": false,
    "tribalLandOnly": false,
    "facilityName": "chevron",
    "zipCode": "",
    "majorFacilitiesOnly": false,
    "activeOnly": false,
    "maxItems": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawlerbros/epa-echo-enforcement-scraper").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 = {
    "mode": "searchCases",
    "state": "CA",
    "registryIds": ["110002899908"],
    "epaRegion": "",
    "naicsCode": "",
    "sicCode": "",
    "lawCited": "",
    "caseCategory": "",
    "civilCriminalIndicator": "",
    "caseStatus": "",
    "dateFiledFrom": "",
    "dateFiledTo": "",
    "federalFacilityOnly": False,
    "crossMediaOnly": False,
    "tribalLandOnly": False,
    "facilityName": "chevron",
    "zipCode": "",
    "majorFacilitiesOnly": False,
    "activeOnly": False,
    "maxItems": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("crawlerbros/epa-echo-enforcement-scraper").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 '{
  "mode": "searchCases",
  "state": "CA",
  "registryIds": [
    "110002899908"
  ],
  "epaRegion": "",
  "naicsCode": "",
  "sicCode": "",
  "lawCited": "",
  "caseCategory": "",
  "civilCriminalIndicator": "",
  "caseStatus": "",
  "dateFiledFrom": "",
  "dateFiledTo": "",
  "federalFacilityOnly": false,
  "crossMediaOnly": false,
  "tribalLandOnly": false,
  "facilityName": "chevron",
  "zipCode": "",
  "majorFacilitiesOnly": false,
  "activeOnly": false,
  "maxItems": 25
}' |
apify call crawlerbros/epa-echo-enforcement-scraper --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/aeM54tY0OmDFcUgmp/builds/4BC2aflzdMBwT3j8N/openapi.json
