EPA ECHO Facility Delta Monitor
Pricing
$6.00 / 1,000 new enforcement events
EPA ECHO Facility Delta Monitor
Watches specific EPA-regulated facilities (by FRS Registry ID or name+state) via the official EPA ECHO API and reports only genuinely new enforcement cases, notices, violations, and inspections since your last check. A check with nothing new is free.
Pricing
$6.00 / 1,000 new enforcement events
Rating
0.0
(0)
Developer
Radu Furtuna
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Durable monitor for new enforcement cases, notices, violations, and inspections at specific EPA-regulated
facilities, via the official, free EPA ECHO REST API (echodata.epa.gov). No API key needed.
Why
Compliance teams, EHS managers, investors doing diligence, and journalists want to know the moment a tracked facility gets a new enforcement action, inspection, or violation notice — not have to re-read its entire multi-decade EPA history every time to spot what changed. Existing ECHO scrapers on Apify mostly do a one-off pull of the current facility snapshot; this one durably tracks what's already been seen and charges only for genuinely new events.
How it works
- Each
watchis one facility, identified either by its EPA FRS Registry ID (most reliable — find it on the facility's ECHO detailed report page or viaecho.epa.gov/facilities/facility-search) or by facility name + US state (resolved to a Registry ID automatically on first use, then cached). - Every run fetches that facility's full Detailed Facility Report (
dfr_rest_services.get_dfr) — EPA ECHO returns the complete history in one response, not a paginated window — and diffs its enforcement cases, notices, violations (with nested enforcement actions), and site-visit inspections against a durable checkpoint of event ids already seen for that watch. - Genuinely new events are pushed to the dataset and billed once each (
new-enforcement-event); checking a facility with nothing new costs nothing beyond the fixed platform run cost.
Input
{"monitorId": "my-facilities","watches": [{ "watchId": "3m-brownwood", "registryId": "110000599273" },{ "watchId": "some-refinery", "facilityName": "Example Refinery", "state": "TX" }],"notifyOn": "new_alerts","webhookUrl": "https://example.com/webhook"}
Add more watches later under the same monitorId — each watch keeps its own independent history.
Billing
Pay-per-event: new-enforcement-event — charged only for an event (enforcement case, notice, violation,
enforcement action, or inspection) genuinely new since the previous check of that watch. The first check
of a new watch establishes a baseline (no charge). Failed/blocked/unresolved checks are never charged.
Delivery guarantee: at-most-once (not exactly-once)
The right to write a row and to charge for it is granted by a single atomic primitive — one
addRequest(uniqueKey) into a dedicated, named claim-journal Request Queue
(<prefix>-<monitorId>-claims). Exactly one run ever wins that key. Claim requests are never deleted
and never handled: the queue is a permanent journal of irreversible attempts, not a work list.
- You will never be charged twice for the same event. That is the guarantee.
- It is not exactly-once. If a run wins the claim and then dies before the row reaches the dataset
(or before the charge completes), that event is lost: it closes as
dataset_unknown/charge_unknownand is never re-delivered. We deliberately prefer losing a delivery over double-charging you. - Boundary of the guarantee: it holds for as long as the named claim-journal queue exists. Anyone with account access can delete or re-create that queue through the Apify Console/API; a fresh journal starts empty, and previously delivered events could then be delivered and billed again. That is an inherent limit of any durable storage, not a defect of the protocol.
- Migration boundary: the guarantee applies from the build that introduced the claim gate onward.
Older builds of this actor must not keep running against the same
monitorId. That same build also had to shorten the durable storage name prefix (the old one, the full actor name, could not fit Apify's 63-character storage-name limit together with a 40-charactermonitorId), so a monitor that ran on an older build starts from a fresh baseline once. A baseline is never charged. coverage.claimJournalSizereports the journal's size each run (best-effort;nullif the queue's metadata could not be read, and the value lags a few seconds because Apify'stotalRequestCountis eventually consistent). Use it to watch growth, not to make decisions.
Honest limits
- Event coverage is intentionally scoped to four DFR sections that map cleanly onto "enforcement /
inspection / violation / penalty":
CaseFormalActions(formal enforcement cases with settlements/ penalties),Notices(informal and formal enforcement notices),ViolationsEnforcementActions(violations plus their nested enforcement actions — mostly Safe Drinking Water Act), andSiteVisits(inspections). We deliberately do NOT surfaceTRIHistory,RCRAWasteHistory,DmrPollLoads, air quality readings, or demographic sections — those are not enforcement/inspection/violation events. facilityName + stateresolution is honest, not fuzzy: 0 matches →facility_not_found, 2+ matches →facility_name_ambiguous:N, in both cases the watch is skipped for that run (no charge, no crash) and reported plainly incoverage.unresolvedFacilities. UseregistryIddirectly to avoid ambiguity.seenIdsper watch is capped (FIFO by discovery order, 4000 entries) — only matters for a facility with an extraordinarily long enforcement history.- We don't invent data: if the ECHO API response shape changes, or a
registryIdis invalid, the run reports it honestly (permanent_error: .../transient_error: ...) instead of silently returning zero results. - A facility record grouped under
MultipleFRSFacilitiesin the DFR response (EPA's own facility-linking) is fetched and diffed as returned by the API; we do not additionally resolve or merge sibling records.
Deviations from the original brief (see also ROADMAP.md)
The task named get_facility_info / get_enforcement_summary as example ECHO endpoints. Live testing
against echodata.epa.gov on 12.09.2026 showed:
get_facility_info(echo_rest_services.get_facility_info) is a self-contained facility-summary endpoint — it does not return itemized enforcement case / inspection / violation history.- The correct official endpoint for a facility's full itemized history in one response is
dfr_rest_services.get_dfr(the same "Detailed Facility Report" the ECHO website itself renders) — used here instead, as the endpoint that actually fits the delta-monitoring task, from the same official ECHO REST family and requiring no API key. - Facility search/resolution uses
echo_rest_services.get_facilities(registers a query, returns aQueryID) followed byecho_rest_services.get_qid(returns the actual facility rows) — this two-step shape is how the ECHO "all data" search service works; there is no single-call facility-by-name lookup.
No EPA ECHO endpoint used here required an API key or authentication at any point during development.
Author: OmniCoder (https://t.me/OmniCoder)