# EPA ECHO Facility Radius Search by Address (`malekh/epa-echo-facility-radius`) Actor

Find every EPA-regulated facility within a radius of a US address, from EPA's ECHO database. Returns distance and direction, Clean Air / Clean Water / RCRA / SDWA / TRI / Superfund program IDs, compliance status, inspection dates, enforcement actions and penalty dollars.

- **URL**: https://apify.com/malekh/epa-echo-facility-radius.md
- **Developed by:** [Malek H](https://apify.com/malekh) (community)
- **Categories:** Real estate, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.01 / 1,000 results

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/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 Facility Radius Search

**Find every EPA-regulated facility within a radius of a US address — with distance, direction, compliance status, inspections, enforcement actions and penalties.**

Give it a property address and a search radius. It returns each regulated
facility inside that radius, sorted nearest first, straight from EPA's own
Enforcement and Compliance History Online (ECHO) database.

Commercial environmental database vendors sell this radius search as a
per-property report for **$250–$415**. The regulatory-records half of that
product is a query against a free federal API. This Actor is that query, done
carefully.

***

### What you get

One **summary** record per location, then one **facility** record per site.

Each facility record carries:

| | |
|---|---|
| **Where** | distance in miles and feet, compass direction, bearing, full address, coordinates |
| **What it is** | facility name, EPA Registry ID, SIC and NAICS codes, federal-facility flag |
| **Which programs** | Clean Air Act, NPDES/Clean Water Act, RCRA hazardous waste, Safe Drinking Water Act, Toxics Release Inventory, Risk Management Plan, TSCA, and Superfund/SEMS — each with its program ID |
| **Compliance** | current status overall and per program, Significant Non-Compliance flag, quarters in non-compliance over 3 years |
| **Inspections** | inspection count, date of last inspection, days since |
| **Enforcement** | informal and formal action counts, date of last formal action |
| **Penalties** | penalty count, total penalty dollars, last penalty date and amount |
| **Follow-up** | a working link to EPA's Detailed Facility Report |

The summary record gives facility counts by program, how many sites are in
Significant Non-Compliance, how many have formal actions or penalties, total
penalty dollars in the radius, Superfund/SEMS site count, how many have never
been inspected, and the nearest facility.

### Input

```jsonc
{
  "addresses": ["233 S Wacker Dr, Chicago, IL 60606"],
  "radiusMiles": "0.25",
  "maxFacilitiesPerLocation": 500,
  "onlyWithEnforcement": false
}
```

Addresses are geocoded with the **US Census Bureau geocoder**. If an address
will not match — rural parcels, new construction, unaddressed sites — pass the
point directly instead:

```jsonc
{ "coordinates": [{ "latitude": 41.8789, "longitude": -87.6366, "label": "Site A" }] }
```

Up to 25 locations per run. `radiusMiles` accepts 0.05 to 5; **0.25 is the
default** because a quarter mile is the radius commercial database reports use
for a property screen.

### Who this is for

- **Environmental consultants** assembling the regulatory-records section of a
  site assessment
- **Commercial real-estate and lending due diligence** on an acquisition or
  refinance
- **Brownfield and redevelopment teams** scoping a parcel before committing
- **EHS and compliance teams** watching regulated neighbours around a plant
- **Journalists and researchers** mapping enforcement around a neighbourhood

### What this is NOT

**This is not an ASTM E1527-21 Phase I Environmental Site Assessment.** Say so
to anyone who asks. ECHO covers federal permitting, compliance and enforcement.
A Phase I additionally requires state UST/LUST and spill registries, historical
land-use sources (fire insurance maps, city directories, aerial photography,
title records) and the opinion of an environmental professional — none of which
this Actor provides. Use it for the federal regulatory-records layer, and for
screening before you commission the full report.

**A blank compliance status is not a clean site.** ECHO leaves a field null
where a facility is not covered by that program or has no determination on
record. Only 97 of 760 facilities in a sample downtown Chicago square mile
carry an inspection date at all. Null means *not reported*, never *compliant*,
and this Actor never converts one into the other — `everInspected` and
`facilitiesNeverInspected` are there so the distinction is visible.

### How it protects you from silently wrong answers

A property screen that quietly misses facilities is worse than no screen. The
guards below all abort the run rather than return a plausible-looking short
list:

- **Completeness.** ECHO reports how many facilities are in the radius. Every
  page is read and the received row count is checked against that total. A
  partial read fails instead of reporting a cleaner property than the truth.
- **Geometry.** Every returned facility's own coordinates are re-measured
  against the search point. If any site falls outside the requested radius, the
  run fails rather than reporting a distant facility as a neighbour.
- **Freshness.** Before any search runs, a dense reference area is queried and
  its newest inspection date compared against today. A frozen snapshot that
  still answers HTTP 200 is caught here; the measured age is reported in every
  summary record so you can watch the trend yourself.
- **Format drift.** ECHO dates are text `MM/DD/YYYY` and penalties are strings
  like `"$241,562"`. Both are parsed strictly — an unrecognised format aborts
  the run instead of becoming a silent null.
- **Throttling.** EPA limits requests per IP. Throttles are retried with
  backoff, and if the limit cannot be cleared the run fails rather than
  degrading into a short list.
- **Geocoding.** The Census geocoder returns `x` = longitude and `y` =
  latitude. The resolved point is bounds-checked against US territory, so a
  swapped pair can never silently search the wrong hemisphere.

An unmatched address returns `status: "unresolved_address"` and is **not
charged**.

### Pricing

Pay per event, with platform usage costs included:

| Event | Price |
|---|---|
| Location screened | $3.00 |
| Facility record returned | $0.01 |

A typical quarter-mile property screen returning ~75 facilities costs about
**$3.75**, against $250–$415 for a commercial database report. Only locations
that actually resolve and pass every guard are charged.

### Data source and licence

- **EPA ECHO REST services** — `echodata.epa.gov` — keyless, documented,
  versioned. Facility search and paged retrieval.
- **US Census Bureau geocoder** — `geocoding.geo.census.gov` — keyless.

Both are US federal public services. ECHO data is a US Government work in the
public domain, published by EPA explicitly for public reuse. No login, no
CAPTCHA, no rate-limit circumvention, no scraping of an HTML application.

ECHO is refreshed by EPA on a rolling basis and enforcement data reaches it
with a lag of weeks to months — that lag is inherent to the source, not to this
Actor, and the summary record reports the newest inspection date it observed so
you can judge it for yourself.

Report a data error in the underlying records to EPA at
<https://echo.epa.gov/resources/general-info/report-data-error>.

# Actor input Schema

## `addresses` (type: `array`):

One-line US street addresses to screen, e.g. '233 S Wacker Dr, Chicago, IL 60606'. Each is geocoded with the US Census Bureau geocoder and then searched. Include the ZIP or the city and state — a bare street name will not match. Up to 25 locations per run.

## `coordinates` (type: `array`):

Search points given directly as \[{"latitude": 41.8789, "longitude": -87.6366, "label": "Site A"}]. Use this when you already have coordinates, or when an address will not geocode (rural parcels, new construction, unaddressed sites). Combined with any addresses above, up to 25 locations per run.

## `radiusMiles` (type: `string`):

How far around each location to search, in miles. The default of 0.25 matches the quarter-mile radius commercial environmental database reports use for a property screen. Larger radii return many more facilities and cost more — 1 mile around a downtown address typically returns several hundred.

## `maxFacilitiesPerLocation` (type: `integer`):

Safety cap on how many facility records each location may return, so a broad radius cannot run up an unexpected bill. The whole radius is always read and sorted by distance first, so the cap keeps the facilities NEAREST the property, never an arbitrary slice. The summary record tells you whether the cap was hit and how many facilities were actually in the radius.

## `onlyWithEnforcement` (type: `boolean`):

Return only facilities that are in Significant Non-Compliance, carry a current violation, or have had a formal enforcement action or a penalty. Narrows a dense urban search to the sites that actually matter for due diligence. The summary record still counts the full radius.

## Actor input object example

```json
{
  "addresses": [
    "233 S Wacker Dr, Chicago, IL 60606"
  ],
  "coordinates": [],
  "radiusMiles": "0.25",
  "maxFacilitiesPerLocation": 500,
  "onlyWithEnforcement": false
}
```

# 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 = {
    "addresses": [
        "233 S Wacker Dr, Chicago, IL 60606"
    ],
    "coordinates": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("malekh/epa-echo-facility-radius").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 = {
    "addresses": ["233 S Wacker Dr, Chicago, IL 60606"],
    "coordinates": [],
}

# Run the Actor and wait for it to finish
run = client.actor("malekh/epa-echo-facility-radius").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 '{
  "addresses": [
    "233 S Wacker Dr, Chicago, IL 60606"
  ],
  "coordinates": []
}' |
apify call malekh/epa-echo-facility-radius --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Kw4ewACgvafVURnlO/builds/afnYFRVJvV40emczk/openapi.json
