# NRC ERNS Chemical Spill & Release Incident Screener by Address (`malonestar/erns-nrc-release-incident-screener`) Actor

Screen any US property against the National Response Center (ERNS) release-report corpus, 2005-present. Tiered coordinate / address / city / county matching with an explicit match class on every row, because only ~19% of NRC records carry a coordinate. Materials, CAS, evacuations, injuries, damage.

- **URL**: https://apify.com/malonestar/erns-nrc-release-incident-screener.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 $6.60 / 1,000 result 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

## NRC / ERNS Chemical Spill & Release Incident Screener

Screen any US property against the **National Response Center (NRC) release-report corpus** —
the database formerly published as **ERNS**, the Emergency Response Notification System, and one
of the federal standard environmental record sources named in **ASTM E1527-21 §8.2.1**.

Every CERCLA §103 / CWA §311 reportable release call taken by the NRC watch floor since
**calendar year 2005** — oil, chemicals, natural gas, pipeline and rail releases, vessel
discharges, unknown sheens — joined across all ten relational sheets NRC publishes and matched to
your property with an **honest, labelled match ladder**.

***

### The one thing that makes this different

**Only about 19% of NRC records carry a usable coordinate.**

Measured live on 2026-08-11: **4,051 of 21,533 CY2025 incidents (18.81%)** carry a numeric
degrees/minutes/seconds position, and of those only **3,992** carry a clean quadrant pair. CY2026
runs at 22.34%. The other four out of five records are **address, nearest-city or county only**.

A point-proximity screener built the obvious way therefore answers
*"no reported releases near this property"* for **four out of five records in the database**, on a
Phase I, while the run reports SUCCESS and bills you.

So this actor screens on a **four-tier ladder**, and labels every single row with which tier
matched it:

| Tier | What it means | `distance_miles` |
|---|---|---|
| `coordinate` | NRC published a coordinate and it is inside your radius | measured |
| `address` | No coordinate; the NRC street address matches your property address | `null` |
| `city` | No coordinate; NRC's nearest city matches your property city | `null` |
| `county` | No coordinate; NRC's county matches your property county | `null` |

**A city or county hit is never presented as a radius answer** — `distance_miles` stays `null`
and `match_basis` says so in words.

And every property gets an `asset_summary` row carrying the disclosure that makes a zero
meaningful:

- `records_in_asset_county_total` — everything NRC holds in your county for the years read
- `records_without_coordinates_in_county` — how many of those a radius search can **never** reach
- `coordinate_coverage_pct_in_county`
- `radius_search_possible` / `county_tier_possible`

***

### Who it is for

- **Phase I ESA consultants** — ERNS is a named ASTM E1527-21 standard federal source. EDR bills
  for it; this returns it as structured data.
- **Environmental counsel and insurers** — release history, materials, CAS, quantities, water
  impact, evacuations, injuries, fatalities and damage claims per incident.
- **Spill-response and industrial-services firms** — incident density and material mix by county.
- **Compliance teams** — `delta` mode watches the live current-year file for newly reported
  incidents in your states.

***

### Scope limit you must read

**The NRC FOIA corpus begins at calendar year 2005.** `CY2000.xlsx` and `CY1990.xlsx` return an
HTTP 302 redirect to an error page, not data. ASTM E1527-21 asks for historical records back to
first developed use or 1940; **this source cannot satisfy that window**, and every row says so in
`corpus_limitation`. This is a screening tool, not a determination.

For the pre-2005 window pair this with
[`usgs-historical-topo-records-review`](https://apify.com/malonestar/usgs-historical-topo-records-review).

***

### Example input

```json
{
  "mode": "screen",
  "assets": [
    { "label": "Houston Ship Channel - Deer Park terminal",
      "lat": 29.7317, "lon": -95.1267, "city": "Deer Park", "county": "Harris", "state": "TX" },
    { "label": "Greeley County KS - rural control",
      "lat": 38.48, "lon": -101.8, "city": "Tribune", "county": "Greeley", "state": "KS" }
  ],
  "radiusMiles": 3,
  "yearsBack": 3,
  "matchTiers": ["coordinate", "address", "city", "county"],
  "includeNonRelease": true
}
```

An address works too — it is geocoded by the US Census geocoder and the county is resolved from
the FCC block API:

```json
{ "assets": [{ "label": "Site A", "street": "5900 Highway 225", "city": "Deer Park", "state": "TX" }] }
```

Delta mode:

```json
{ "mode": "delta", "deltaStates": ["TX", "LA"], "deltaOnly": true }
```

***

### What it does that a flat dump does not

- **Joins all ten NRC sheets on `SEQNOS`** — `CALLS`, `INCIDENT_COMMONS`, `INCIDENT_DETAILS`,
  `MATERIAL_INVOLVED`, `MATERIAL_INV0LVED_CR` (yes, the upstream sheet name has a **zero** where
  the O belongs), plus presence flags from `INCIDENTS`, `TRAINS_DETAIL`, `DERAILED_UNITS`,
  `VESSELS_DETAIL` and `MOBILE_DETAILS`.
- **Reconstructs the coordinate honestly.** NRC packs position into eight DMS/quadrant columns.
  Quadrant letters measured live include `M`, `8`, `B`, `V`, `Q`, `2`, `N/N` and `W/W`. A parser
  that trusts the quadrant to set the sign puts a Gulf of Mexico spill in Kazakhstan. Unmappable
  quadrants **withhold** the coordinate with a stated reason; the reconstructed value is still
  published under `latitude_reconstructed` so nothing fetched is thrown away.
- **Cross-checks every coordinate against its own `LOCATION_STATE`** with a 4-degree offshore
  allowance calibrated on live data. Legitimate Gulf lease blocks filed under Louisiana survive;
  a dropped digit (LA at `-9.25` for `-90.25`), an E/W inversion (Bakersfield CA published at
  `+119` East) and Guam (which NRC publishes with `LONG_QUAD=W` although Guam is at 144.7 **East**)
  are rejected — **40 of 4,051 CY2025 rows, 1.0%**.
- **Classifies releases honestly.** `RAILROAD NON-RELEASE` is **11.3% of CY2025** and by
  definition involves no material released; it ships with `is_release: false`, never silently
  dropped and never counted as a release. **87% of NRC fatality records (914 of 1,052) are
  RAILROAD NON-RELEASE trespasser strikes** — read `any_fatalities` beside `is_release`.
- **Never publishes the CAS sentinel.** `CAS_NUMBER = "000000-00-0"` is on **91.0%** of material
  rows and means "no CAS registry number in this record". It is nulled with a stated basis; the
  usable identity is `CHRIS_CODE` + `NAME_OF_MATERIAL`.
- **`null` never means zero.** `NUMBER_EVACUATED` is the empty string on **21,227 of 21,533**
  CY2025 rows; `Number('')` is `0`, which would publish "0 people evacuated" for a field nobody
  filled in. Every count is `null` when unrecorded. `Y`/`N`/`U` becomes `true`/`false`/`null` —
  `U` is the source's own "unknown", which is not `false`.
- **The corpus is not US-only** — `LOCATION_STATE` includes `CN`, `MX`, `NI` and `XX` alongside
  `PR`, `GU`, `VI` and `AS`. Rows are labelled `location_is_us` / `location_is_us_territory`
  rather than silently dropped.
- **Caches annual files by `Last-Modified`** in a named key-value store. Historical years are
  frozen and download once ever; the current-year file is re-fetched only when NRC actually
  replaces it (files are replaced **in place**, so `data_as_of` is on every row).

### Gates that run before any billable row

The run **fails and bills nothing** rather than publishing an answer it cannot stand behind:

- **Sheet contract** — all ten expected sheets must resolve, including the zero-for-O one.
- **Column contract** — every required column per sheet; SheetJS phantom `__EMPTY` columns are
  dropped (CY2025 parses to 41 columns against 29 real ones; CY2026 is clean at 29).
- **Live drift gate** — row-count floors, the CAS sentinel rate inside a measured 60-99.5% band,
  coordinate coverage inside 5-95%, a minimum distinct-type and distinct-state count, and a
  **`RAILROAD NON-RELEASE` presence canary** (if that class vanishes, the vocabulary was recoded
  and every release rollup silently starts counting rail trespasser strikes).
- **Positive canary** — NRC report **SEQNOS 1421048**, a Houston Ship Channel sheen at Deer Park
  TX on 2025-01-14, must come back with its exact county, city, type, both date encodings,
  material, CHRIS code and coordinate, and its CAS sentinel must be nulled.
- **Negative control** — a synthetic impossible state/county must match **zero** incidents,
  proving the tier filters actually filter.
- **Partial-window guard** — if more than a third of the requested years could not be read, the
  run fails rather than reporting an absence it never verified.
- **Payload shape guard** — content-type plus zip magic bytes, so an HTML error page served at
  HTTP 200 can never be parsed into a clean empty answer.
- **Rows are buffered and gated before `pushData`.**

### Use as an MCP tool

This actor is callable by AI agents over [mcp.apify.com](https://mcp.apify.com).

```json
{
  "mcpServers": {
    "apify": {
      "command": "npx",
      "args": ["-y", "@apify/actors-mcp-server", "--actors", "malonestar/erns-nrc-release-incident-screener"],
      "env": { "APIFY_TOKEN": "<your token>" }
    }
  }
}
```

Worked agent prompt: *"Screen 29.7317, -95.1267 in Deer Park, Harris County TX against the
National Response Center release corpus for the last five years. Tell me how many incidents
matched by coordinate versus county, how many county records carry no coordinate at all, and
which of the matches involved a material that reached water."*

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

### Chain it with

- [`epa-contaminated-site-screener`](https://apify.com/malonestar/epa-contaminated-site-screener) — SEMS, RCRA, ECHO
- [`state-tank-spill-registry-screener`](https://apify.com/malonestar/state-tank-spill-registry-screener) — state UST/LUST and spill registries
- [`usgs-historical-topo-records-review`](https://apify.com/malonestar/usgs-historical-topo-records-review) — the pre-2005 historical window
- [`site-due-diligence-bundle`](https://apify.com/malonestar/site-due-diligence-bundle) — 20 layers in one call

### Pricing

Pay per result: **$12 per 1,000 rows**, with graduated paid-plan discounts. One `asset_summary`
row per property plus one row per matched incident. A run that fails a gate emits nothing and
bills nothing.

***

### Output fields

Every one of the **149** declared fields, in full. `asset_summary` rows carry the asset and coverage fields; `incident` rows carry those plus the incident fields.

#### Row identity and asset context

| Field | Type | Description |
|---|---|---|
| `record_type` | string or null | asset\_summary for the per-asset roll-up row, or incident for one matched NRC release report. |
| `asset_label` | string or null | The label you supplied for this property (or Asset N when none was given). |
| `asset_latitude` | number or null | Latitude used for the radius search, either supplied or geocoded. Null when the property could not be located. |
| `asset_longitude` | number or null | Longitude used for the radius search. Null when the property could not be located. |
| `asset_street` | string or null | Street line supplied for the property. |
| `asset_city` | string or null | City used for the city match tier. |
| `asset_county` | string or null | County used for the county match tier, supplied or resolved from the FCC block API. |
| `asset_state` | string or null | Two-letter state used for every non-coordinate tier. |
| `asset_location_status` | string or null | How well the property was located: coordinate\_and\_county, coordinate\_only, county\_only, city\_only, or unresolved. An unresolved asset is never reported as clear. |
| `asset_location_basis` | string or null | Exactly how the location was derived, including any geocoder or FCC lookup that failed. |
| `asset_geocode_source` | string or null | Which geocoder produced the coordinate, or null when you supplied it. |
| `search_radius_miles` | number or null | Radius in miles applied to the coordinate tier only. |
| `match_tiers_enabled` | string or null | Which of coordinate, address, city, county were enabled on this run. |

#### Asset summary: match counts and the coverage disclosure

| Field | Type | Description |
|---|---|---|
| `incidents_matched_total` | number or null | Total NRC incidents matched to this property across every enabled tier. |
| `incidents_emitted` | number or null | How many of those were emitted after maxIncidentsPerAsset. |
| `incidents_truncated` | boolean or null | True when the per-asset cap suppressed matches. Never read a capped answer as a complete one. |
| `matches_tier_coordinate` | number or null | Matches made by measured distance. NRC publishes a usable coordinate on only about 19 percent of records. |
| `matches_tier_address` | number or null | Matches made on a street address because NRC published no coordinate. |
| `matches_tier_city` | number or null | Matches made on nearest city. NOT a radius answer. |
| `matches_tier_county` | number or null | Matches made on county. NOT a radius answer. |
| `non_release_incidents_suppressed` | number or null | How many RAILROAD NON-RELEASE style records were withheld because includeNonRelease was false. Always reported, never silently dropped. |
| `records_in_asset_county_total` | number or null | Every NRC record in this property county for the years read, matched or not. The honest denominator. |
| `records_without_coordinates_in_county` | number or null | How many of those carry no coordinate and therefore can NEVER be reached by a radius search. This is the number that makes a coordinate-only zero meaningless. |
| `coordinate_coverage_pct_in_county` | number or null | Percentage of county records that carry a usable coordinate. |
| `radius_search_possible` | boolean or null | False when the property has no coordinate, so no distance-based answer exists for it. |
| `county_tier_possible` | boolean or null | False when neither a county nor a state could be resolved for the property. |

#### How this incident matched

| Field | Type | Description |
|---|---|---|
| `match_tier` | string or null | Which tier matched this incident: coordinate, address, city or county. |
| `match_basis` | string or null | Plain-language statement of why this incident matched, including whether a distance could be measured at all. |
| `distance_miles` | number or null | Great-circle miles from the property. Populated ONLY on coordinate-tier matches; null on every other tier because no coordinate exists to measure from. |

#### Incident identity, timing and class

| Field | Type | Description |
|---|---|---|
| `seqnos` | string or null | NRC incident sequence number. The identity used for the SEQNOS join and for delta tracking. |
| `corpus_year` | number or null | Which annual NRC file this record came from. |
| `incident_datetime` | string or null | Incident date and time in UTC ISO form, parsed from the string encoding in INCIDENT\_COMMONS. |
| `incident_datetime_basis` | string or null | Which encoding was used, or why the value could not be read: mdy\_string, excel\_serial, iso\_string, unparseable, not\_recorded. |
| `incident_year` | number or null | Calendar year of the incident. |
| `report_received_datetime` | string or null | When the National Response Center took the call, parsed from the Excel serial encoding in CALLS. |
| `report_received_datetime_basis` | string or null | Which encoding was used for the call timestamp. |
| `incident_dtg` | string or null | Whether the incident time is when it OCCURRED or was DISCOVERED. |
| `type_of_incident` | string or null | NRC incident class: FIXED, VESSEL, UNKNOWN SHEEN, RAILROAD NON-RELEASE, MOBILE, PIPELINE, STORAGE TANK, RAILROAD, PLATFORM, AIRCRAFT, CONTINUOUS. |
| `is_release` | boolean or null | True when the incident class describes an actual or suspected release. FALSE for RAILROAD NON-RELEASE, which is 11.3 percent of the file. NULL for a class outside the measured vocabulary. |
| `release_basis` | string or null | Why is\_release holds the value it does. |
| `potential_release_only` | boolean or null | NRC POTENTIAL\_FLAG. True marks a POTENTIAL release, not a confirmed one. |
| `incident_cause` | string or null | NRC cause code, e.g. EQUIPMENT FAILURE, OPERATOR ERROR, DERAILMENT, UNKNOWN. |
| `incident_location_label` | string or null | NRC free-text location label. |
| `description_of_incident` | string or null | The caller narrative as recorded by the NRC watchstander. |

#### Location as NRC recorded it

| Field | Type | Description |
|---|---|---|
| `location_address` | string or null | NRC free-text location address. Frequently a pointer such as SEE LAT AND LONG rather than a street. |
| `location_street1` | string or null | First street line as recorded. |
| `location_street2` | string or null | Second street line as recorded. |
| `location_nearest_city` | string or null | Nearest city as reported by the caller. Backs the city match tier. |
| `location_state` | string or null | Two-letter state or territory. Includes CN, MX, NI and XX: the corpus is NOT US-only. |
| `location_county` | string or null | County as reported. Populated on 100 percent of records, which is why the county tier exists. UNKNOWN appears on roughly 1 percent and never matches a real county. |
| `location_zip` | string or null | ZIP code as reported. |
| `location_is_us` | boolean or null | False for CN, MX, NI and XX records. Null when no state was recorded. |
| `location_is_us_territory` | boolean or null | True for PR, GU, VI, AS, MP and similar. |
| `distance_from_city` | number or null | Reported distance from the nearest city. Null when not recorded, never 0. |
| `distance_from_city_units` | string or null | Units for distance\_from\_city. |
| `direction_from_city` | string or null | Reported bearing from the nearest city. |
| `location_section` | string or null | PLSS section as reported. |
| `location_township` | string or null | PLSS township as reported. |
| `location_range` | string or null | PLSS range as reported. |

#### Coordinate reconstruction and positional honesty

| Field | Type | Description |
|---|---|---|
| `latitude` | number or null | Decimal latitude, populated ONLY when the DMS and quadrant reconstructed AND passed the state cross-check. Null on roughly four of five records. |
| `longitude` | number or null | Decimal longitude under the same contract as latitude. |
| `latitude_reconstructed` | number or null | The reconstructed latitude even when it was rejected, so nothing fetched is silently discarded. |
| `longitude_reconstructed` | number or null | The reconstructed longitude even when it was rejected. |
| `positional_class` | string or null | How the position was derived: dms\_reported\_state\_verified, dms\_reported\_offshore\_of\_state, dms\_reported\_state\_not\_checked, no\_coordinate\_reported, coordinate\_unusable, or coordinate\_contradicts\_state. |
| `positional_basis` | string or null | Plain-language explanation of the positional class. |
| `coordinate_rejected_reason` | string or null | Why a reconstructed coordinate was withheld: quadrant\_unmappable, dms\_components\_invalid, state\_bbox\_mismatch, outside\_wgs84\_envelope or null\_island. Null on a healthy record. |
| `lat_quadrant_raw` | string or null | The raw LAT\_QUAD letter. Garbage values such as M, 8, B, V and 2 occur and are never guessed into a sign. |
| `lon_quadrant_raw` | string or null | The raw LONG\_QUAD letter. |
| `state_bbox_gap_degrees` | number or null | Degrees between the reconstructed point and its own state bounding box. 0 inside, up to 4 for legitimate offshore, beyond 4 the coordinate is rejected. |

#### Reporting party

| Field | Type | Description |
|---|---|---|
| `call_type` | string or null | NRC call type code. |
| `call_source` | string or null | How the report reached the NRC. |
| `responsible_company` | string or null | Company named as responsible by the caller. Often null on unknown-source releases. |
| `responsible_org_type` | string or null | Organisation type of the responsible party. |
| `responsible_city` | string or null | City of the responsible party. |
| `responsible_state` | string or null | State of the responsible party. XX means unknown. |
| `responsible_zip` | string or null | ZIP of the responsible party. |

#### Materials released

| Field | Type | Description |
|---|---|---|
| `materials` | array or null | Every material row joined from MATERIAL\_INVOLVED, each with chris\_code, cas\_number, cas\_basis, un\_number, name\_of\_material, amount\_of\_material, unit\_of\_measure, reached\_water, amount\_in\_water and unit\_of\_measure\_in\_water. |
| `material_count` | number or null | How many material rows were joined to this incident. |
| `material_names` | string or null | Semicolon-joined material names for easy CSV reading. |
| `material_cas_numbers` | string or null | Semicolon-joined REAL CAS numbers. The 000000-00-0 sentinel, which is on 91 percent of material rows, is excluded because it is not a CAS number. |
| `material_chris_codes` | string or null | Semicolon-joined USCG CHRIS codes, the usable material identity when no CAS exists. |
| `any_material_reached_water` | boolean or null | True when any joined material reached water, false when every material was checked and none did, null when it was not recorded. |
| `is_continuous_release_record` | boolean or null | True when the incident appears in the continuous-release sheet MATERIAL\_INV0LVED\_CR, whose upstream name contains a zero where the O belongs. |

#### Consequences

| Field | Type | Description |
|---|---|---|
| `fire_involved` | boolean or null | Fire involved. Null when NRC recorded U for unknown. |
| `fire_extinguished` | boolean or null | Fire extinguished. Null when unknown. |
| `any_evacuations` | boolean or null | Whether an evacuation occurred. |
| `number_evacuated` | number or null | People evacuated. NULL, never 0, when NRC left the field blank, which is 98.6 percent of records. |
| `who_evacuated` | string or null | Who was evacuated, e.g. EMPLOYEES, EVERYONE, NUMBER UNKNOWN. |
| `radius_of_evacuation` | string or null | Evacuation radius as recorded. |
| `any_injuries` | boolean or null | Whether injuries were reported. |
| `number_injured` | number or null | People injured, null when not recorded. |
| `number_hospitalized` | number or null | People hospitalised, null when not recorded. |
| `any_fatalities` | boolean or null | Whether a fatality was reported. NOTE: 87 percent of NRC fatality records are RAILROAD NON-RELEASE trespasser strikes involving no material release. Read this beside is\_release. |
| `number_fatalities` | number or null | Fatalities reported, null when not recorded. |
| `any_damages` | boolean or null | Whether property damage was reported. |
| `damage_amount_usd` | number or null | Damage in US dollars as reported by the caller, null when not recorded. |
| `water_supply_contaminated` | boolean or null | Whether a drinking-water supply was reported contaminated. Null on the 99 percent of records NRC marks U. |
| `waterway_closed` | boolean or null | Whether a waterway was closed. |
| `road_closed` | boolean or null | Whether a road was closed. |
| `track_closed` | boolean or null | Whether rail track was closed. |
| `air_corridor_closed` | boolean or null | Whether an air corridor was closed. |
| `major_artery_affected` | boolean or null | Whether a major traffic artery was affected. |
| `medium_affected` | string or null | Environmental medium affected: WATER, LAND, AIR or similar. |
| `additional_medium_info` | string or null | Free-text detail about the affected medium. |
| `body_of_water` | string or null | Named receiving water body. |
| `tributary_of` | string or null | Larger water body the receiving water flows to. |
| `release_secured` | boolean or null | Whether the release had been secured at the time of the report. Null when unknown. |
| `estimated_duration_of_release` | string or null | Reported duration of the release. |
| `release_rate` | string or null | Reported release rate. |
| `remedial_action_description` | string or null | Remedial action described by the caller. |
| `state_agency_on_scene` | string or null | State agency reported on scene. |
| `state_agency_notified` | string or null | State agency notified. |
| `federal_agency_notified` | string or null | Federal agency notified. |
| `sheen_size_length` | number or null | Reported sheen length, null when not recorded. |
| `sheen_size_length_units` | string or null | Units for sheen length. |
| `sheen_size_width` | number or null | Reported sheen width, null when not recorded. |
| `sheen_size_width_units` | string or null | Units for sheen width. |
| `sheen_color` | string or null | Reported sheen colour. |
| `offshore` | boolean or null | Whether NRC recorded the incident as offshore. |
| `media_interest` | string or null | Reported press interest. |
| `additional_info` | string or null | Any additional narrative recorded on the report. |

#### Per-source join status and auxiliary-sheet presence

| Field | Type | Description |
|---|---|---|
| `has_source_specific_detail` | boolean or null | True when the INCIDENTS sheet holds equipment-specific detail (tank, pipeline, platform, NPDES, grade crossing) for this SEQNOS. Not expanded into columns in v1.0. |
| `has_train_detail` | boolean or null | True when TRAINS\_DETAIL holds a row for this SEQNOS. |
| `has_derailed_units_detail` | boolean or null | True when DERAILED\_UNITS holds a row for this SEQNOS. |
| `has_vessel_detail` | boolean or null | True when VESSELS\_DETAIL holds a row for this SEQNOS. |
| `has_mobile_detail` | boolean or null | True when MOBILE\_DETAILS holds a row for this SEQNOS. |
| `incident_details_status` | string or null | ok when the INCIDENT\_DETAILS row joined, no\_matching\_row when NRC published none. Every consequence field is null in the second case, which is not checked rather than negative. |
| `calls_status` | string or null | ok when the CALLS row joined, no\_matching\_row otherwise. |

#### Delta mode

| Field | Type | Description |
|---|---|---|
| `is_new_since_last_run` | boolean or null | Delta mode only. True when this SEQNOS was absent from the named cross-run baseline, false when it was present, NULL when no usable baseline existed, which is not checked rather than new. |
| `delta_baseline_status` | string or null | ok, initialized (first run for this scope) or unavailable. |
| `delta_baseline_size` | number or null | How many SEQNOS the baseline held before this run. |

#### Run provenance, vintage and gate results

| Field | Type | Description |
|---|---|---|
| `data_source` | string or null | The upstream this row came from. |
| `corpus_first_year` | number or null | The first calendar year NRC publishes. CY2000 and earlier redirect to an error page. |
| `corpus_limitation` | string or null | States on every row that the corpus begins in 2005 and therefore cannot satisfy the ASTM E1527-21 historical window (first developed use or 1940). |
| `years_requested` | string or null | Which annual files this run asked for. |
| `years_read` | string or null | Which annual files were actually read. |
| `years_unavailable` | string or null | Which requested years could not be read. Null on a complete run. |
| `years_partial` | boolean or null | True when at least one requested year was unavailable, so an absence of hits is not a verified negative for the full window. |
| `data_as_of` | string or null | Last-Modified vintage of every annual file read. NRC replaces files IN PLACE, so this is the only honest vintage. |
| `current_year_file_last_modified` | string or null | Last-Modified of the current-year file, which NRC refreshes continuously. |
| `vintage_verified_all_years` | boolean or null | False when a cached copy was served without a successful HEAD revalidation. |
| `corpus_coordinate_coverage_pct` | number or null | Percentage of all records read that carry a usable coordinate. Measured 18.81 on CY2025 and 22.34 on CY2026. |
| `drift_gate_status` | string or null | Always passed on an emitted row: the run fails and bills nothing when the gate trips. |
| `canary_status` | string or null | passed, or not\_applicable when CY2025 was outside the run scope. A failed canary fails the run. |
| `canary_basis` | string or null | What the canary check concluded. |
| `negative_control_status` | string or null | passed. A synthetic impossible state/county must match zero incidents, proving the tier filters filter. |
| `screened_at` | string or null | UTC timestamp of this run. |
| `matched_rows_total` | number or null | Total rows produced before maxResults, so a capped answer is never mistaken for a complete one. |
| `results_truncated` | boolean or null | True when maxResults capped the dataset. |

***

### FAQ

**Is ERNS the same as the National Response Center database?**
Yes. EPA's Emergency Response Notification System was the release-notification database named in
ASTM E1527; the National Response Center is where those notifications are taken and published
today. This actor reads NRC's own annual FOIA workbooks.

**Why does a property show zero coordinate matches but dozens of county matches?**
Because NRC publishes a usable coordinate on only ~19% of records. That is the whole point of the
match ladder, and `records_without_coordinates_in_county` on the `asset_summary` row tells you
exactly how many records the radius could never have reached.

**Does zero rows mean the property is clean?**
No. It means NRC holds no matching *reported* release for the years read, on the tiers you
enabled. Reporting is caller-initiated and incomplete by nature, the corpus starts in 2005, and
`years_unavailable` will tell you if part of your window could not be read. This is a screening
tool, not a determination.

**Why is `latitude` null when `latitude_reconstructed` has a value?**
The DMS reconstructed, but it failed a validation — an unmappable quadrant, an out-of-range DMS
component, or a position that contradicts its own `LOCATION_STATE` by more than the 4-degree
offshore allowance. `coordinate_rejected_reason` says which. Nothing is discarded; the value is
published, just not sold to you as a usable coordinate.

**Why do so many "fatality" records have no material released?**
Because 87% of them are `RAILROAD NON-RELEASE` trespasser strikes, which are reportable rail
events with nothing spilled. Filter on `is_release` before building any release rollup.

**How far back does it go?**
Calendar year 2005. See the scope-limit section above.

**Can I monitor for new incidents?**
Yes — `mode: "delta"` reads the live current-year file and compares against a named cross-run
baseline of `SEQNOS`. The first run for a scope stores the baseline and fails loudly rather than
billing you for the whole file labelled "new".

**Does this cover state-reported spills?**
No. NRC is the *federal* notification channel. Pair it with
`state-tank-spill-registry-screener` for NY/CA state registries and
`epa-contaminated-site-screener` for SEMS/RCRA/ECHO.

***

### Data source and licence

US Coast Guard National Response Center annual FOIA workbooks,
`https://nrc.uscg.mil/FOIAFiles/CY<yy>.xlsx`, CY05 through the current year. US federal
government work, public domain. Keyless. Files are replaced in place; the `Last-Modified` vintage
of every file read is published on every row as `data_as_of`.

# Actor input Schema

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

screen = check properties against the NRC corpus (the default when omitted). delta = monitor the current-year file for newly reported incidents using a named cross-run baseline.

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

One entry per property. Give lat + lon, or a street + city + state the US Census geocoder can resolve. Optional: label, county. Required in screen mode; ignored in delta mode. Example: \[{"label":"Deer Park terminal","lat":29.7317,"lon":-95.1267}].

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

Radius for the COORDINATE tier only. Applies to roughly 19% of NRC records - the rest carry no coordinate and are reached by the address, city and county tiers instead. Falls back to 1 mile when omitted. Max 50.

## `yearsBack` (type: `integer`):

How many calendar years back to read, newest first. The NRC corpus starts at CY2005, so a larger value is silently clamped to 2005 and the row says so. Each year is a 14-18 MB workbook, so more years means a longer run. Falls back to 10 when omitted.

## `matchTiers` (type: `array`):

Which tiers to use, in strict precedence order: coordinate, address, city, county. Disabling the non-coordinate tiers makes the screen miss roughly four out of five NRC records. All four are applied when omitted.

## `includeNonRelease` (type: `boolean`):

RAILROAD NON-RELEASE reports are 11% of the corpus and by definition involve no material released. Leave on to see them (flagged is\_release=false); turn off to suppress them - the count suppressed is always reported, never silently dropped. On when omitted.

## `maxIncidentsPerAsset` (type: `integer`):

Cap on incident rows emitted per property, nearest and highest-tier first. The true match count is always published as incidents\_matched\_total. Falls back to 500 when omitted.

## `maxResults` (type: `integer`):

Hard cap on total emitted rows across all properties. matched\_rows\_total and results\_truncated are on every row so a capped answer is never mistaken for a complete one. Falls back to 5000 when omitted.

## `deltaStates` (type: `array`):

Two-letter state or territory codes to scope the delta monitor, e.g. \["TX","LA"]. Leave empty to monitor the whole current-year file. Ignored in screen mode.

## `deltaOnly` (type: `boolean`):

Delta mode only. When on, emit ONLY incidents absent from the previous run's baseline. The first run for a scope has no baseline, so it fails loudly and stores one rather than billing you for the whole file labelled new. Off when omitted.

## `useFileCache` (type: `boolean`):

Cache each annual workbook index in a named key-value store keyed by the file's Last-Modified header. Historical years are frozen so they download once ever; the current-year file is re-downloaded only when NRC actually replaces it. On when omitted.

## Actor input object example

```json
{
  "mode": "screen",
  "assets": [
    {
      "label": "Houston Ship Channel - Deer Park",
      "lat": 29.7317,
      "lon": -95.1267,
      "city": "Deer Park",
      "county": "Harris",
      "state": "TX"
    },
    {
      "label": "Greeley County KS - rural control",
      "lat": 38.48,
      "lon": -101.8,
      "city": "Tribune",
      "county": "Greeley",
      "state": "KS"
    }
  ],
  "radiusMiles": 3,
  "yearsBack": 3,
  "matchTiers": [
    "coordinate",
    "address",
    "city",
    "county"
  ],
  "includeNonRelease": true,
  "maxIncidentsPerAsset": 200,
  "maxResults": 2000,
  "deltaStates": [],
  "useFileCache": true
}
```

# Actor output Schema

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

The default dataset of asset summaries and matched NRC incidents.

# 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": "screen",
    "assets": [
        {
            "label": "Houston Ship Channel - Deer Park",
            "lat": 29.7317,
            "lon": -95.1267,
            "city": "Deer Park",
            "county": "Harris",
            "state": "TX"
        },
        {
            "label": "Greeley County KS - rural control",
            "lat": 38.48,
            "lon": -101.8,
            "city": "Tribune",
            "county": "Greeley",
            "state": "KS"
        }
    ],
    "radiusMiles": 3,
    "yearsBack": 3,
    "matchTiers": [
        "coordinate",
        "address",
        "city",
        "county"
    ],
    "includeNonRelease": true,
    "maxIncidentsPerAsset": 200,
    "maxResults": 2000,
    "deltaStates": [],
    "deltaOnly": false,
    "useFileCache": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("malonestar/erns-nrc-release-incident-screener").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": "screen",
    "assets": [
        {
            "label": "Houston Ship Channel - Deer Park",
            "lat": 29.7317,
            "lon": -95.1267,
            "city": "Deer Park",
            "county": "Harris",
            "state": "TX",
        },
        {
            "label": "Greeley County KS - rural control",
            "lat": 38.48,
            "lon": -101.8,
            "city": "Tribune",
            "county": "Greeley",
            "state": "KS",
        },
    ],
    "radiusMiles": 3,
    "yearsBack": 3,
    "matchTiers": [
        "coordinate",
        "address",
        "city",
        "county",
    ],
    "includeNonRelease": True,
    "maxIncidentsPerAsset": 200,
    "maxResults": 2000,
    "deltaStates": [],
    "deltaOnly": False,
    "useFileCache": True,
}

# Run the Actor and wait for it to finish
run = client.actor("malonestar/erns-nrc-release-incident-screener").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 '{
  "mode": "screen",
  "assets": [
    {
      "label": "Houston Ship Channel - Deer Park",
      "lat": 29.7317,
      "lon": -95.1267,
      "city": "Deer Park",
      "county": "Harris",
      "state": "TX"
    },
    {
      "label": "Greeley County KS - rural control",
      "lat": 38.48,
      "lon": -101.8,
      "city": "Tribune",
      "county": "Greeley",
      "state": "KS"
    }
  ],
  "radiusMiles": 3,
  "yearsBack": 3,
  "matchTiers": [
    "coordinate",
    "address",
    "city",
    "county"
  ],
  "includeNonRelease": true,
  "maxIncidentsPerAsset": 200,
  "maxResults": 2000,
  "deltaStates": [],
  "deltaOnly": false,
  "useFileCache": true
}' |
apify call malonestar/erns-nrc-release-incident-screener --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,malonestar/erns-nrc-release-incident-screener"
        }
    }
}

```

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/Knj2jJmNEZdwpzaIO/builds/AyNAr0HkGEmrEJDkd/openapi.json
