# EPA TRI Toxic Release Rollup by Radius - Site Screener (`malonestar/epa-tri-facility-release-rollup`) Actor

Rolls up EPA Toxics Release Inventory facilities and their reported on-site chemical releases within a radius of any US coordinate. Air, water, underground injection and land totals per facility, plus carcinogen, PBT and PFAS breakdowns, for Phase I ESA and environmental due diligence.

- **URL**: https://apify.com/malonestar/epa-tri-facility-release-rollup.md
- **Developed by:** [Kyle Maloney](https://apify.com/malonestar) (community)
- **Categories:** Real estate, Developer tools, Agents
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.40 / 1,000 tri release rows

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
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 TRI Toxic Release Rollup by Radius - Site Screener

Give it a coordinate. It returns every EPA **Toxics Release Inventory** facility near
that point and rolls up what each one actually reported releasing on site - air,
surface water, underground injection and land - for a chosen reporting year, with
carcinogen, PBT, PFAS, metal and Clean Air Act HAP breakdowns.

Other TRI actors on the Store return a flat facility list. This one does the
transform: a **radius rollup** with a per-facility release profile, a data-quality
contract on every number, and a screen level you can gate a workflow on.

### Who it is for

- **Phase I ESA / environmental due diligence** - what is being released into the
  air, water and ground next to the parcel, and by whom.
- **Real estate and lending** - a fast, defensible read on industrial neighbours
  before an acquisition or a loan.
- **EHS and corporate sustainability** - benchmark a site against everything within
  a few miles of it.
- **Journalists and community groups** - who releases what, near here.
- **AI agents** - clean, chainable output over MCP.

### Example input

```json
{
  "assets": [
    { "lat": 39.8033, "lon": -104.9456, "label": "Commerce City, CO - refinery corridor parcel" },
    { "lat": 29.7266, "lon": -95.2431, "label": "Houston Ship Channel site" }
  ],
  "radiusMiles": 3,
  "reportingYear": 2023,
  "includeCountyTier": true,
  "maxFacilitiesPerAsset": 25
}
```

`assets` is required. Nothing else is: every other field has a **prefill** but no
schema default, because an Apify input-schema default is injected server side into
every run and would silently narrow a query the caller never made. When a fallback
is applied it is published on the row (`radius_miles_defaulted`,
`reporting_year_source`).

### Output

Two row types per run.

- **`asset_summary`** - one per site. Totals across every matched facility, both
  match-tier counts, the coordinate-coverage measurement, and a gated screen level.
- **`facility`** - one per matched TRI facility. Identity, distance, forms filed,
  the full release breakdown, hazard classes and per-source status.

146 declared fields, listed in full at the end of this page. Every one is nullable,
because a dataset-schema type mismatch silently drops the entire push while the run
still reports SUCCEEDED.

### What this actor counts, and what it deliberately does not

The headline field is **`onsite_release_total_lbs`**.

It is **EPA Form R Part II Section 5** - releases that occur **on site**. That is
exactly what the `tri_release_qty` table documents itself as, and it is the number a
proximity screen should be about: what is going into the environment *at this
location*.

It is **not** Section 6 off-site transfers. A transfer is a shipment *away* from the
site; only part of it counts as a release in EPA's national rollup, and the
disposal-versus-treatment-versus-recycling split is encoded across roughly forty
waste-management codes. Folding it into a proximity screen would be wrong twice -
wrong partition and wrong place. Recycling and energy recovery are not releases at
all and are likewise excluded.

Section 8.8 **one-time releases** are reported in their own field and are **never
added** to the headline: EPA already counts them inside Section 5.

Every component ships alongside the total, and the run fails rather than publish a
row where `release_component_sum_lbs` does not equal `onsite_release_total_lbs`.

### The data-quality contract

TRI offers many ways to publish a confident wrong number. Each one is handled
explicitly and reported on the row.

| Situation | What TRI publishes | What this actor emits |
|---|---|---|
| Filer marked a medium **not applicable** | `release_na = 1`, no quantity - about **77%** of all Section 5 rows | counted in `release_rows_not_applicable`, contributes **nothing**. Not a zero. |
| Filer checked a **range band** instead of a number | a range code and a **null** quantity (every such row measured) | counted in `release_rows_range_only` with `range_only_lower_bound_lbs` / `range_only_upper_bound_lbs`. A real release, magnitude bounded, never zero. |
| Filer reported a genuine **zero** | `release_na = 0`, quantity `0` | a quantity of `0`. |
| EPA publishes **neither** | `release_na = 0`, no quantity, no range | counted in `release_rows_unexplained_null`. Never coerced to `0`. |
| Facility filed only **Form A** | a certification statement and **zero** rows in `tri_release_qty` | `form_a_certification_only = true`, `release_total_basis = form_a_certification_no_quantities`, screen `LOW` - never `NONE`. |
| A source did not answer | - | every total is `null`, `release_screen_level` is `INCONCLUSIVE`, and `sources_failed` names what broke. |

`onsite_release_total_is_complete` tells you in one boolean whether the number you
are looking at resolved cleanly from end to end.

### Coordinates, and the county match tier

**EPA publishes no latitude or longitude for a large share of TRI facilities.**
Measured 2026-08-17: **417 of 765** Colorado facility records carry no preferred
coordinate, and among the Colorado facilities that actually filed a 2023 form it is
**196 of 259**. A radius screen built only on coordinates would silently miss most
of the active reporters and hand you a clean answer.

So there are two match tiers, and both ship:

- **`coordinate`** - a published preferred coordinate inside the radius.
  `distance_miles` is measured.
- **`county`** - no usable coordinate, but the facility is in the same county as the
  site. `distance_miles` is **null**. It is never guessed from a county centroid,
  which would be a confident wrong distance.

Every `asset_summary` row publishes `facilities_state_without_coordinates` and
`coordinate_coverage_pct`, so a coordinate gap can never read as an absence of
facilities. Set `includeCountyTier: false` for a faster and strictly less complete
answer.

The packed `fac_latitude` / `fac_longitude` columns are **not** used as a fallback.
Of the 347 Colorado records carrying both, 59 disagree with the preferred coordinate -
including rows where the latitude column holds a longitude, rows reading `0`, and
rows that decode to 78 degrees north in Colorado.

EPA also stores `pref_longitude` **unsigned**. It is negated for the western
hemisphere and left positive for Guam, CNMI and American Samoa; the rule applied is
published in `coordinate_longitude_sign_applied`.

A radius near a state line reaches into the neighbouring state, and TRI can only be
filtered one state at a time, so neighbouring states whose bounding box falls within
the radius are read as well. Every state read is named in `states_searched`.

### What runs before you are charged

A live drift gate runs on every execution, **before a single row is pushed**. If it
fails, the run fails, emits nothing and bills nothing.

- **Every filter column is asserted present in the live table.** An efservice filter
  naming a column the table does not have is **silently ignored and the full national
  table is returned** - measured: `tri_facility/bogus_column/CO/COUNT` returns 64,990,
  identical to unfiltered. That is how a national answer gets labelled as a local one.
- **Filters are post-validated on the returned rows**, not merely accepted. A
  state-filtered count must also be strictly less than the unfiltered count, and an
  invalid state code must return zero.
- **A positive canary** - a named refinery's 2023 filing - must still return a form
  count, row count, release total and media set inside their measured bands.
- **The chained form-to-release read is reconciled against a direct single-table
  read** of the same document, because a chained efservice filter has been measured
  to silently drop rows elsewhere in EPA's service.
- **Closed vocabularies** are checked. An unrecognised `release_na` fails the run,
  because that value is what separates "not applicable" from "zero". An unrecognised
  medium code does **not** fail: it is bucketed into `other_unmapped_media_lbs` and
  named on the row, so no quantity is ever dropped.
- **Row-count floors** on the facility table, the form table and the chemical
  dictionary.
- **Truncation** is guarded by efservice's own exact `COUNT`.

### Reporting years

TRI runs from 1987 to the newest published cycle. Omit `reportingYear` and the actor
resolves the newest year EPA actually serves, publishing it as `reporting_year` with
`reporting_year_source = auto_newest_served`.

The release-medium vocabulary is **not stable across years**, which is a real trap:
the modern surface-impoundment classes (`SI 5.5.3A`, `SI 5.5.3B`) do not appear at
the head of the table at all, and 1987-1995 forms use combined underground-injection
and landfill classes that modern forms do not. All three eras are mapped, and
anything unrecognised is bucketed rather than lost.

### Use as an MCP tool

This actor is callable by AI agents through Apify's MCP server.

```json
{
  "mcpServers": {
    "apify": {
      "command": "npx",
      "args": ["-y", "@apify/actors-mcp-server", "--actors", "malonestar/epa-tri-facility-release-rollup"],
      "env": { "APIFY_TOKEN": "your-apify-token" }
    }
  }
}
```

Worked prompt: *"For 39.8033, -104.9456 within 3 miles, list the TRI facilities and
tell me which single chemical accounts for the most on-site release, and whether any
of the totals are incomplete."*

Billing is unchanged when called as a tool, and a run that cannot answer fails
without billing.

### Chains well with

- [EPA Contaminated Site Screener](https://apify.com/malonestar/epa-contaminated-site-screener) - Superfund, RCRA, USTs and ECHO enforcement at the same coordinate. The natural pairing on a Phase I.
- [Site Due Diligence Bundle](https://apify.com/malonestar/site-due-diligence-bundle) - twenty environmental layers for one coordinate in one call.
- [EPA RCRA Hazardous Waste Generator Rollup](https://apify.com/malonestar/epa-rcra-hazwaste-generator-rollup) - what the same facilities generate as hazardous waste.
- [EPA Drinking Water Quality Screener](https://apify.com/malonestar/epa-drinking-water-quality-screener) - SDWA violations and PFAS occurrence for the serving water system.
- [EPA GHGRP Emitter Screener](https://apify.com/malonestar/epa-ghgrp-emitter-screener) - greenhouse-gas emissions for large emitters.

### FAQ

**Is TRI the same as an emissions inventory?**
No. TRI is self-reported by facilities that cross activity and chemical thresholds.
A facility with no TRI form may still emit; TRI silence is not evidence of absence,
and this actor says so on the row rather than implying otherwise.

**Why is a facility showing zero releases?**
Read `release_total_basis`. `reported_section_5_quantities` with a `0` means the
filer genuinely reported zero. `form_a_certification_no_quantities` means they
certified under threshold and published no numbers at all. `source_unavailable`
means a read failed and every total is `null`.

**Why is `distance_miles` empty on some rows?**
Those are county-tier matches: EPA publishes no coordinate for that facility. The
distance is unknown and is not guessed.

**Does it cover off-site transfers?**
Not in the headline, deliberately. See "What this actor counts" above.

**How current is the data?**
It reads EPA Envirofacts live on every run. `reporting_year_forms_national` tells you
how many forms EPA serves for the cycle you asked for.

### Source

EPA Envirofacts efservice, TRI subject area - `tri_facility`, `tri_reporting_form`,
`tri_release_qty`, `tri_chem_info`. County and state resolution via the FCC block API
with the Census geocoder as an independent fallback. No API key required.

### All output fields

All 146 declared fields. Every field is nullable.

| Field | Type | Description |
|---|---|---|
| `row_type` | string | Either "asset\_summary" (one per site) or "facility" (one per matched TRI facility). |
| `asset_label` | string | The label supplied for this site, or its coordinate pair. |
| `asset_latitude` | number | Latitude of the site as supplied. |
| `asset_longitude` | number | Longitude of the site as supplied. |
| `asset_state_code` | string | Two-letter state or territory code resolved from the site coordinate. |
| `asset_county_fips` | string | Five-digit county FIPS resolved from the site coordinate; the key for the county match tier. |
| `asset_county_name` | string | County name resolved from the site coordinate. |
| `geo_resolver` | string | Which service resolved the county: "fcc" or "census". |
| `geo_resolver_status` | string | ok or unavailable. |
| `radius_miles` | number | Radius used for the coordinate match tier. |
| `radius_miles_defaulted` | boolean | True when the caller did not supply a radius and the actor used 5 miles. |
| `reporting_year` | number | The reporting year rolled up. |
| `reporting_year_source` | string | "caller" or "auto\_newest\_served". |
| `reporting_year_is_newest_served` | boolean | True when the actor resolved this as the newest reporting year EPA serves. Null when the caller named a year. |
| `reporting_year_forms_national` | number | How many TRI forms EPA serves nationally for this reporting year. A row-count floor that proves the cycle exists. |
| `states_searched` | string | Every state read from tri\_facility for this site: the resolved state plus any neighbour whose bounding box is within the radius. |
| `data_source` | string | EPA Envirofacts efservice, TRI subject area. |
| `data_retrieved_at` | string | UTC timestamp of the run. |
| `facilities_state_universe` | number | Total tri\_facility records read across every searched state. |
| `facilities_state_with_coordinates` | number | How many of those publish a usable preferred coordinate. |
| `facilities_state_without_coordinates` | number | How many publish none. These can only be matched at the county tier. |
| `coordinate_coverage_pct` | number | Share of the searched universe carrying a usable coordinate. |
| `coordinate_coverage_note` | string | Why the coordinate gap is published rather than hidden. |
| `facility_list_truncated` | boolean | True if a state facility read hit its page cap. False means the read was complete against the exact upstream count. |
| `facilities_in_radius` | number | Facilities with a coordinate inside the radius. |
| `facilities_county_tier` | number | Facilities with no coordinate that sit in the site county. |
| `county_tier_candidates_total` | number | How many county-tier facilities existed before the per-site cap. |
| `county_tier_selection_order` | string | How county-tier facilities were ordered and capped. |
| `facilities_emitted` | number | Facility rows actually produced for this site. |
| `facilities_capped_at` | number | maxFacilitiesPerAsset in force for this run. |
| `facilities_with_forms` | number | How many matched facilities filed any TRI form in the reporting year. |
| `facilities_form_a_only` | number | Matched facilities that filed only Form A certifications, which publish no quantities. |
| `facilities_source_incomplete` | number | Matched facilities whose form or release read did not answer. |
| `nearest_facility_name` | string | Name of the nearest coordinate-tier facility. |
| `nearest_facility_id` | string | TRI facility id of the nearest coordinate-tier facility. |
| `nearest_facility_distance_miles` | number | Distance to the nearest coordinate-tier facility. County-tier facilities are excluded because their distance is unknown. |
| `nearest_facility_onsite_release_lbs` | number | On-site Section 5 releases reported by the nearest facility. |
| `match_tier` | string | "coordinate" (distance measured) or "county" (no published coordinate, same county as the site). |
| `match_tier_basis` | string | Plain-English statement of what the tier means for this row. |
| `distance_miles` | number | Great-circle distance from the site to the facility. NULL on a county-tier row: the distance is unknown and is never guessed from a county centroid. |
| `tri_facility_id` | string | EPA TRI Facility Identification number. |
| `facility_name` | string | Facility name as filed. |
| `street_address` | string | Facility street address. |
| `city_name` | string | City the facility reports on its TRI form. |
| `county_name` | string | Facility county. |
| `state_abbr` | string | Facility state, post-validated against the state that was requested. |
| `zip_code` | string | Facility ZIP code. |
| `state_county_fips_code` | string | Facility state+county FIPS; the join key for the county match tier. |
| `epa_region` | string | EPA region number. |
| `epa_registry_id` | string | EPA Facility Registry Service id, for cross-linking to other EPA programs. |
| `frs_id` | string | FRS id as carried on the TRI record. |
| `parent_co_name` | string | Parent company as filed. |
| `standardized_parent_company` | string | EPA-standardized parent company name. |
| `foreign_parent_co_name` | string | Foreign parent company as filed. |
| `facility_closed` | boolean | TRI closure indicator. NULL when TRI publishes no indicator, never false. |
| `federal_facility` | boolean | True when TRI records the site as federally owned or operated. |
| `federal_agency` | string | Owning federal agency, where recorded. |
| `public_contact_name` | string | Public technical contact named on the form. |
| `public_contact_phone` | string | Public contact phone number. |
| `public_contact_email` | string | Public contact email. |
| `facility_latitude` | number | TRI preferred latitude. |
| `facility_longitude` | number | TRI preferred longitude, SIGNED. EPA stores this column unsigned; it is negated for the western hemisphere and left positive for Guam, CNMI and American Samoa. |
| `coordinate_available` | boolean | False when EPA publishes no usable preferred coordinate for this facility. |
| `coordinate_unavailable_reason` | string | Why no coordinate could be used. |
| `coordinate_source` | string | Always tri\_facility.pref\_latitude/pref\_longitude. The packed DDMMSS fac\_latitude/fac\_longitude columns are deliberately NOT used as a fallback: of the 347 Colorado records carrying both, 59 disagree with the preferred coordinate, including rows where the latitude column holds a longitude. |
| `coordinate_longitude_sign_applied` | string | "west" or "east": which hemisphere rule was applied to the unsigned source value. |
| `coordinate_accuracy_meters` | number | EPA positional accuracy estimate. |
| `coordinate_collection_method` | string | EPA coordinate collection method code. |
| `coordinate_description_category` | string | What the coordinate describes (plant entrance, centroid and so on). |
| `coordinate_horizontal_datum` | string | EPA horizontal datum code. |
| `forms_total` | number | Distinct TRI documents filed by this facility for the reporting year. |
| `forms_form_r` | number | Full Form R filings, which carry quantities. |
| `forms_form_a` | number | Form A Certification Statements. A Form A filer certifies they are under the reporting thresholds and reports NO quantities; such a document has zero rows in tri\_release\_qty, so it must never be read as zero releases. |
| `forms_unknown_type` | number | Documents whose form type code is not recognised. Counted, never assumed to be Form R. |
| `form_type_codes_raw` | string | The raw form\_type\_ind values seen, so a new code is visible on the row. |
| `forms_partial_facility` | number | Forms covering only part of the site. Two forms for one chemical are legitimate in that case and are summed. |
| `forms_inactive_status` | number | Documents whose active\_status is not 1. |
| `duplicate_form_groups` | number | Chemicals with more than one document for the year. Surfaced rather than silently double-counted. |
| `form_a_certification_only` | boolean | True when the facility filed only Form A certifications, so no quantity exists to report. |
| `chemicals_reported` | number | Distinct TRI chemicals covered by the filings. |
| `chemical_names` | string | Semicolon-separated list of the chemicals filed. |
| `production_ratio_max` | number | Highest production ratio across the filings, ignoring any the filer marked not applicable. |
| `one_time_release_lbs` | number | Section 8.8 non-routine or one-time release quantity. |
| `one_time_release_forms` | number | How many forms reported a one-time release. |
| `one_time_release_note` | string | States that Section 8.8 is already inside the Section 5 totals and is never added to them. |
| `onsite_release_total_lbs` | number | THE HEADLINE. Total on-site releases reported under EPA Form R Part II Section 5 for the reporting year, summed across every medium. NULL, never zero, when a contributing source did not answer. |
| `onsite_release_partition` | string | States exactly which EPA partition the headline is, and what is excluded. |
| `air_total_lbs` | number | Fugitive plus stack air releases. |
| `air_fugitive_lbs` | number | Section 5.1 fugitive or non-point air emissions. |
| `air_stack_lbs` | number | Section 5.2 stack or point-source air emissions. |
| `water_lbs` | number | Section 5.3 discharges to receiving streams. One form may carry several water rows, one per receiving stream. |
| `underground_injection_total_lbs` | number | All underground injection classes combined. |
| `underground_injection_class_i_lbs` | number | Section 5.4.1 Class I underground injection wells. |
| `underground_injection_class_iiv_lbs` | number | Section 5.4.2 Class II through V underground injection wells. |
| `underground_injection_unspecified_lbs` | number | Underground injection reported under the pre-1996 combined class, TRI code UNINJ8795. |
| `land_total_lbs` | number | All on-site land disposal classes combined. |
| `land_rcra_c_landfill_lbs` | number | Section 5.5.1A RCRA Subtitle C landfills. |
| `land_other_landfill_lbs` | number | Section 5.5.1B other on-site landfills. |
| `land_landfill_unspecified_lbs` | number | Landfill disposal reported under the pre-1996 combined class, TRI code LANDF8795. |
| `land_treatment_lbs` | number | Section 5.5.2 land treatment or application farming. |
| `land_surface_impoundment_rcra_c_lbs` | number | Section 5.5.3A RCRA Subtitle C surface impoundments. TRI codes this SI 5.5.3A and it does not appear at the head of the table, so a vocabulary built from one sample loses it. |
| `land_surface_impoundment_other_lbs` | number | Section 5.5.3B other surface impoundments, TRI code SI 5.5.3B. |
| `land_surface_impoundment_unspecified_lbs` | number | Surface impoundment disposal reported under the older combined SURF IMP class. |
| `land_other_disposal_lbs` | number | Section 5.5.4 other on-site land disposal. |
| `other_unmapped_media_lbs` | number | Quantities whose environmental\_medium code is not in the known vocabulary. Bucketed here rather than dropped, so the headline stays complete when EPA adds a code. |
| `unmapped_media_values` | string | The literal medium codes that landed in the unmapped bucket. |
| `release_component_sum_lbs` | number | The sum of every published component. Must equal the headline. |
| `release_component_sum_matches_total` | boolean | Identity check: the headline is the sum of its own published parts. A false value fails the run rather than being published. |
| `release_rows_reported` | number | Section 5 rows carrying a numeric quantity. A quantity may legitimately be zero. |
| `release_rows_not_applicable` | number | Rows where the filer set release\_na = 1. NOT APPLICABLE is not zero, and roughly 77% of all Section 5 rows carry it. |
| `release_rows_range_only` | number | Rows where the filer checked a range band instead of giving a number. Every such row measured carries a null quantity: a real release with a bounded magnitude, never a zero. |
| `release_rows_unexplained_null` | number | Rows the filer did not mark not applicable and for which EPA publishes neither a quantity nor a range. Counted, never coerced to zero. |
| `release_rows_invalid` | number | Rows carrying a value this actor refuses to interpret, such as an unrecognised release\_na. |
| `range_only_lower_bound_lbs` | number | Sum of the lower bounds of the range-reported rows. Null when there are none. |
| `range_only_upper_bound_lbs` | number | Sum of the upper bounds of the range-reported rows. Null when there are none or a band is unbounded. |
| `range_only_chemicals` | string | Chemicals whose quantity is only bounded. |
| `release_basis_codes` | string | Which estimation methods the filer declared. TRI uses bare letters on legacy forms and numbered method codes on modern ones. |
| `onsite_release_total_is_complete` | boolean | True only when every Section 5 row resolved to a quantity: no range-only, unexplained or invalid rows. |
| `release_total_basis` | string | How the headline was produced. reported\_section\_5\_quantities: the filer gave numbers. form\_r\_all\_media\_not\_applicable: a Form R was filed and every medium was marked not applicable, so the total is a reported zero. form\_a\_certification\_no\_quantities: only Form A certifications were filed and no quantity exists, so the total is null. no\_forms\_filed: the facility filed nothing for this year and the total is null. source\_unavailable: a read failed and every total is null. |
| `carcinogen_release_lbs` | number | On-site releases of chemicals EPA flags as OSHA carcinogens. NULL when the chemical dictionary did not answer. |
| `carcinogen_chemicals` | number | How many released chemicals are flagged carcinogens. |
| `pbt_release_lbs` | number | On-site releases of persistent bioaccumulative toxic chemicals. |
| `pbt_chemicals` | number | How many released chemicals are flagged PBT. |
| `pfas_release_lbs` | number | On-site releases of chemicals EPA flags as PFAS. |
| `pfas_chemicals` | number | How many released chemicals are flagged PFAS. |
| `metal_release_lbs` | number | On-site releases of chemicals EPA flags as metals or metal compounds. |
| `metal_chemicals` | number | How many released chemicals are flagged metals. |
| `clean_air_act_release_lbs` | number | On-site releases of chemicals also listed as Clean Air Act hazardous air pollutants. |
| `clean_air_act_chemicals` | number | How many released chemicals carry the Clean Air Act flag. |
| `chemicals_not_in_dictionary` | number | Released chemicals with no entry in tri\_chem\_info, so no hazard class could be assigned. |
| `non_pound_unit_chemicals` | number | Released chemicals whose TRI unit of measure is not pounds. TRI publishes dioxin and dioxin-like compounds in grams. |
| `non_pound_unit_note` | string | Explains the mixed unit of measure. |
| `top_chemical_name` | string | Largest single contributor to the on-site total. |
| `top_chemical_id` | string | TRI chemical id of the largest contributor. |
| `top_chemical_lbs` | number | Quantity attributed to the largest contributor. |
| `top_chemicals` | string | The five largest contributors with their quantities. |
| `release_screen_level` | string | NONE, LOW, MODERATE, HIGH or INCONCLUSIVE. Gated on coverage: it can never read NONE while a contributing source was unavailable, and never NONE for a Form A only filer. |
| `release_screen_flags` | string | Semicolon-separated flags behind the screen level. |
| `release_screen_basis` | string | Plain-English statement of what the screen level was computed from. |
| `tri_facility_status` | string | ok, unavailable or not\_applicable for the facility master read. |
| `tri_reporting_form_status` | string | ok, unavailable or not\_applicable for the form read. |
| `tri_release_qty_status` | string | ok, unavailable or not\_applicable for the Section 5 read. not\_applicable means the facility filed only Form A, which has no release rows by design. |
| `tri_chem_info_status` | string | ok or unavailable for the chemical dictionary. When unavailable, every hazard-class field is null rather than zero. |
| `sources_failed` | string | Which upstream reads did not answer for this row. |
| `data_complete` | boolean | True only when every source this row depends on answered. |
| `data_issues` | string | Row-level anomalies worth a human look, such as an unrecognised basis code. |

# Actor input Schema

## `assets` (type: `array`):

One entry per property or project site: {"lat": 39.8033, "lon": -104.9456, "label": "Commerce City parcel"}. US longitudes are NEGATIVE. Maximum 25 sites per run.

## `radiusMiles` (type: `integer`):

Radius around each site for the coordinate match tier. Maximum 25. If omitted the actor uses 5 miles and marks radius\_miles\_defaulted = true on every row, so the radius is never implicit. There is deliberately no schema default: an Apify default is injected server-side into every run.

## `reportingYear` (type: `integer`):

TRI reporting year to roll up (1987 onwards). If omitted the actor resolves the newest reporting year EPA actually serves and publishes it as reporting\_year with reporting\_year\_source = auto\_newest\_served. Deliberately has no schema default so a stale cycle can never be pinned server-side.

## `includeCountyTier` (type: `boolean`):

EPA publishes no latitude/longitude for a large share of TRI facilities (measured 2026-08-17: 417 of 765 Colorado facility records, and 196 of the 259 Colorado facilities that filed a 2023 form). When true, such facilities are still returned if they sit in the same county as the site, at match\_tier = county with a null distance. Turning this off makes the answer faster and strictly less complete.

## `maxFacilitiesPerAsset` (type: `integer`):

Caps how many facilities are priced out per site. Coordinate-tier matches are taken first, nearest first, then county-tier matches ordered by facility id. The full candidate counts are published on the asset\_summary row so a capped answer is never mistaken for a complete one.

## Actor input object example

```json
{
  "assets": [
    {
      "lat": 39.8033,
      "lon": -104.9456,
      "label": "Commerce City, CO - refinery corridor parcel"
    }
  ],
  "radiusMiles": 3,
  "reportingYear": 2023,
  "includeCountyTier": true,
  "maxFacilitiesPerAsset": 12
}
```

# Actor output Schema

## `results` (type: `string`):

The default dataset.

# 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 = {
    "assets": [
        {
            "lat": 39.8033,
            "lon": -104.9456,
            "label": "Commerce City, CO - refinery corridor parcel"
        }
    ],
    "radiusMiles": 3,
    "reportingYear": 2023,
    "includeCountyTier": true,
    "maxFacilitiesPerAsset": 12
};

// Run the Actor and wait for it to finish
const run = await client.actor("malonestar/epa-tri-facility-release-rollup").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 = {
    "assets": [{
            "lat": 39.8033,
            "lon": -104.9456,
            "label": "Commerce City, CO - refinery corridor parcel",
        }],
    "radiusMiles": 3,
    "reportingYear": 2023,
    "includeCountyTier": True,
    "maxFacilitiesPerAsset": 12,
}

# Run the Actor and wait for it to finish
run = client.actor("malonestar/epa-tri-facility-release-rollup").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 '{
  "assets": [
    {
      "lat": 39.8033,
      "lon": -104.9456,
      "label": "Commerce City, CO - refinery corridor parcel"
    }
  ],
  "radiusMiles": 3,
  "reportingYear": 2023,
  "includeCountyTier": true,
  "maxFacilitiesPerAsset": 12
}' |
apify call malonestar/epa-tri-facility-release-rollup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,malonestar/epa-tri-facility-release-rollup"
        }
    }
}

```

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/I3afC3zBktvbkSbrl/builds/H29ADWbEzaEQey5gU/openapi.json
