Government — entry 026 of 90

EPA

Verified Aug 2026

EPA's Envirofacts platform exposes the agency's environmental compliance and monitoring databases — air quality, drinking water systems, the Toxics Release Inventory, hazardous waste, and Superfund sites — through a keyless REST API at `data.epa.gov/efservice`. Any of the underlying tables can be queried by building a URL from the table name, filter columns, and an output format, with results returned as JSON, XML, CSV, or Excel. EPA's Developer Central site separately documents smaller topic widgets built on the same data.

EPA's Envirofacts platform exposes environmental compliance and monitoring data — including the Toxics Release Inventory (TRI) — through a keyless REST API whose query grammar lives entirely in the URL path rather than query-string parameters: /{table}/{column}/{operator-or-value}/.../rows/{start}:{end}/{format}. A live GET this run against /tri_facility/state_abbr/DE/rows/0:0/JSON returned a genuine 200, 1,507 B single-facility record at content-type: application/json, with access-control-allow-origin reflecting the request's own Origin header rather than a wildcard — confirmed live by an explicit Origin: https://greatapis.com probe, which got that exact value echoed back.

environmentgovernment-dataus-federalopen-datapublic-health
AuthenticationNone requiredCall it straight away — no key, no signup.
HTTPSSupportedTraffic is encrypted in transit.
CORSEnabledCallable directly from browser JavaScript.
PricingFreeNo paid tier — free for the documented use case.
FormatsJSON, XML, CSV, ExcelResponses can be requested as JSON or XML or CSV or Excel.

GreatAPIs Score

Score94out of 100
Authentication25/25No authentication required
Pricing20/20Free to use
Docs14/20Documentation URL provided
Formats15/15Supports 4 response formats
Freshness20/20Verified within 6 months

Embed this badge

Scored 94 on greatapis.com
<a href="https://greatapis.com/api/epa/"><img src="https://greatapis.com/badge/epa.svg" alt="Scored 94 on greatapis.com"></a>

Auth quickstart

  1. No API key, signup, or credit card required — a live GET this run against /tri_facility/state_abbr/DE/rows/0:0/JSON returned real facility data on the first anonymous try. Filters chain as path segments in {column}/{value} pairs (or a word operator like BEGINNING in place of an exact value — confirmed live against /tri_facility/state_abbr/BEGINNING/D/rows/0:1/JSON), not as ?column=value query parameters.
Stored keyNo key stored

Your key is stored only in this browser (localStorage) and sent directly to the API — never to greatapis.

Look up a Toxics Release Inventory facility by state

GEThttps://data.epa.gov/efservice/tri_facility/state_abbr/DE/rows/0:0/JSON

200 application/json

[
  {
    "tri_facility_id": "19701CMPST3310W",
    "facility_name": "COMPOSITES USA INC",
    "street_address": "3310 WRANGLE HILL RD, BLDG 111",
    "city_name": "BEAR",
    "county_name": "NEW CASTLE",
    "state_abbr": "DE",
    "zip_code": "197011846",
    "region": "3",
    "fac_closed_ind": "1",
    "pref_latitude": 39.5886,
    "pref_longitude": 75.691859,
    "epa_registry_id": "110001135227",
    "asgn_public_contact": "JOHN F. MCCLOSKEY",
    "asgn_public_phone": "4102872700",
    "...": "34 more fields omitted (mail_*, parent_co_*, asgn_technical_*, frs_id, bia_code, etc, mostly null) -- full record is 1,507 B"
  }
]

/tri_facility/state_abbr/DE/count/JSON returned 200 with [{"TOTALQUERYRESULTS": 168}] — Delaware has 168 TRI facilities in total; a larger state proves the same grammar scales, live this run /tri_facility/state_abbr/CA/count/JSON returned [{"TOTALQUERYRESULTS": 5110}].

Try it

Developer reference

Base URLhttps://data.epa.gov/efservice
Key endpoints
  • GET/{table}/rows/{start}:{end}/JSON
  • GET/{table}/{column}/{value}/JSON
  • GET/{table}/count/JSON

Gotchas & limits

  • rows/{start}:{end} is an inclusive range on both ends, not the usual exclusive-end slice — confirmed live this run: rows/0:0 returns exactly 1 record, rows/0:1 returns 2 records, and rows/0:2 returns 3. Code ported from a language where slice ends are exclusive (Python's list[0:1], for instance) will silently fetch one extra row per request.
  • Omitting /rows/ entirely returns the whole table, with no default page size or cap of any kind — confirmed live this run: /tri_facility/state_abbr/CA/JSON (no /rows/ segment) returned all 5,110 California facilities in a single 7.7 MB response, matching CA's own /count/JSON total exactly. Code that assumes an unpaginated request is safely small by default can pull megabytes it didn't ask for.
  • /count/JSON wraps its single number in an array containing one object, not a bare integer — confirmed live above: the literal response body is [{"TOTALQUERYRESULTS": 168}], so the count itself is body[0].TOTALQUERYRESULTS, one level deeper than a naive body.count or body[0] read would expect.
  • An unrecognised table name is a real HTTP 404 with a descriptive JSON error, not a silent empty result — confirmed live this run against /not_a_table/rows/0:1/JSON, which returned exactly {"error": "not_a_table/rows/0:1: The table is not available."} (62 B), echoing the full requested path back inside the message.