# EPA RCRA Hazardous Waste Generator Rollup — Tons, Codes, LQG (`malonestar/epa-rcra-hazwaste-generator-rollup`) Actor

Every RCRA hazardous waste generator in a US state, rolled up from the EPA Biennial Report: tons generated, shipped, managed and received; federal waste codes split into listed, characteristic and acute; LQG/SQG status; EPA management methods; the TSDFs used; and a cycle-over-cycle trend.

- **URL**: https://apify.com/malonestar/epa-rcra-hazwaste-generator-rollup.md
- **Developed by:** [Kyle Maloney](https://apify.com/malonestar) (community)
- **Categories:** Business, 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 results

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 RCRA Hazardous Waste Generator Rollup — Tons, Waste Codes & LQG Status

Turn the EPA **RCRAInfo Biennial Report (BR)** into a clean, per-facility **hazardous
waste generator** rollup. Pick a US state and a report cycle (every two years, 2005 through 2023) and
get one row per facility with **tons generated, shipped, managed and received**, the
**federal RCRA waste codes** it reports (split into listed / characteristic / acute),
its **generator status (LQG / SQG / VSQG)**, the **EPA management methods** its waste
goes to, the **TSDFs it ships to**, and a **cycle-over-cycle tonnage delta** — ranked
by tons generated and scored against the whole state.

The raw Biennial Report is one row per *waste-stream line*, so a single facility appears
in dozens of rows. This actor does the aggregation for you.

Keyless. Sources the official **EPA Envirofacts `BR_REPORTING`** table (67 columns).

### Who it's for

- **Environmental compliance & EHS managers** — pull your own facility's certified BR filing, check the generator status EPA has on record, and see which waste codes and management methods are attached to it.
- **Hazardous-waste transporters & TSDFs** — a prospect list of the largest generators in a state, filtered by NAICS, waste code and management method, with `primary_receiver_id` showing which competitor already has the account.
- **Phase I ESA / environmental due diligence** — establish whether a site or its neighbours are large-quantity generators, what they generate, and whether that changed between cycles.
- **ESG, supply-chain and procurement auditors** — benchmark a supplier against every other generator in its state (`share_of_state_generation_pct`) and flag acute (P-code) and listed-waste generation.
- **Insurers, lenders and researchers** — screen counterparties for LQG status, acute waste, deep-well injection and mixed radioactive waste.

### Example input

```json
{
  "states": ["Delaware"],
  "reportCycle": 2023,
  "compareToPriorCycle": true,
  "naicsPrefix": "",
  "minTons": 0,
  "maxResults": 2000
}
```

Prospecting example — the largest chemical/petroleum generators in New Jersey that
ship off-site, with the 2021 → 2023 trend:

```json
{
  "states": ["NJ"],
  "reportCycle": 2023,
  "compareToPriorCycle": true,
  "naicsPrefix": "324",
  "minTons": 50,
  "maxResults": 500
}
```

### What v1.1 fixed

This actor was hardened against live EPA data before publication. Four defects were
found that would each have billed you for a confident wrong answer:

| Defect | Evidence | Fix |
|---|---|---|
| **Fabricated waste codes.** `waste_code_group` holds EPA *group labels*, not codes. v1.0 chopped them into 4-character chunks. | 33 of Delaware's 88 handlers (37.5%) carried codes that do not exist in 40 CFR 261 — `TCMT`, `DMIX`, `UMIX`, `PMIX`, `LABP`, `TCOR`, and `ONLY` (from the literal value `"TCOR Only"`). | Codes come from `federal_waste_codes` only and must match `[DFKPU]###`. Group labels are kept in their own field. |
| **Another company's waste attributed to the generator.** `br_form = WR` rows are waste a TSDF *received* from others. | 7,722 of 10,715 Texas 2023 rows (72%) were WR, carrying 422,465 managed tons and their own waste codes. | GM / WR / XX lines are counted and totalled separately. |
| **Silent truncation.** efservice publishes a `COUNT` endpoint; v1.0 never called it, and a single transient error mid-pagination ended the loop quietly. | Texas 2023 is 50,714 rows, California 285,108. A short loop still produced a ranked "largest generators" list. | Every state is checked against the upstream-declared count and the run **fails** on any mismatch. |
| **A negative asserted without checking.** v1.0 returned `large_quantity_generator_flag: false` for facilities EPA had reported no status for. | | The flag is `null` when unknown, and `lqg_flag_basis` says which of `epa_status` / `tonnage_heuristic` / `unknown` produced it. |
| **Cycle 2021 reported as "0 tons generated" for every facility.** EPA publishes no `generation_tons` or `managed_tons` at all for the 2021 cycle; v1.0 coerced those nulls to 0. | All 1,435 Delaware 2021 rows are null generation with `shipped_tons` populated on 1,427; confirmed identical on Texas and California. A 2021 run produced a full billable dataset of zeros, ranked arbitrarily, and `minTons` removed every handler. | Tonnage is `null` when unreported. Ranking, `minTons` and share-of-state fall back to shipped tons and say so via `ranking_tons_basis`; the trend uses shipped-vs-shipped, which spans the gap. |

Also corrected: the repo previously recorded that **Rhode Island returned no data**.
It does not — RI 2023 has **85,982 rows**. That was a cold-query artefact.

### Output fields (one row per facility)

**Identity & location**

| Field | Description |
|---|---|
| `handler_id` | EPA RCRA handler ID (EPA ID number). |
| `handler_name` | Facility / handler name as filed. |
| `street_address` | Street address, assembled from the three BR street columns. |
| `city`, `state`, `zip`, `county` | Facility location. |
| `county_code` | EPA county code (state prefix + 3-digit FIPS), e.g. `DE003`. |
| `epa_region` | EPA region 1–10. |
| `primary_naics` | Primary NAICS industry code. |
| `report_cycle` | Biennial Report cycle year of this record. |
| `last_change_date` | Newest RCRAInfo `last_change` across the facility's lines (data vintage). |

**Tonnage**

| Field | Description |
|---|---|
| `total_generation_tons` | Tons of hazardous waste generated across every waste-stream line. `null` if no line reported a tonnage at all — never coerced to 0. |
| `total_shipped_tons` | Tons shipped off-site. |
| `total_managed_tons` | Tons managed on-site across **every** BR line, including waste received from others. |
| `managed_tons_own_generation` | The part of `total_managed_tons` that is the facility's own waste. |
| `managed_tons_from_received` | The part that is other companies' waste. `null` if the facility received nothing. |
| `total_received_tons` | Tons received from other companies (TSDF intake). |
| `wastewater_tons` / `nonwastewater_tons` | Generation split by EPA's wastewater flag. |
| `generation_tons_reported` | Whether EPA published a generation tonnage at all. `false` on every 2021-cycle row. |
| `ranking_tons` / `ranking_tons_basis` | The tonnage used for ranking, `minTons` and share-of-state: generation tons, or shipped tons when EPA published none. |
| `state_generation_tons_total` | Statewide generation for the cycle, computed **before** your filters. |
| `state_ranking_tons_total` / `share_of_state_ranking_tons_pct` | Same, on the ranking-tons basis — populated even in cycle 2021. |
| `share_of_state_generation_pct` | This facility's share of statewide generation, to 4 dp. |
| `state_handler_count` | Distinct handlers in the state for the cycle, pre-filter. |
| `generation_rank` | Rank by generation tons within the returned set (1 = largest). |

**Waste codes**

| Field | Description |
|---|---|
| `distinct_waste_codes` | Validated federal RCRA codes (`[DFKPU]###`) on waste this facility **generated**. |
| `n_distinct_waste_codes` | Count of the above. |
| `listed_waste_codes` | F (40 CFR 261.31), K (261.32), P (261.33(e)), U (261.33(f)) codes. |
| `characteristic_waste_codes` | D codes — ignitability, corrosivity, reactivity, toxicity (261.20–261.24). |
| `acute_waste_codes` | P codes: acutely hazardous commercial chemicals, which carry the 1 kg/month LQG threshold. |
| `has_listed_waste`, `has_characteristic_waste`, `has_acute_hazardous_waste` | `true`/`false` when the facility filed federal codes; **`null`** when it filed none. |
| `received_waste_codes` | Codes on waste **received** from others — deliberately kept out of `distinct_waste_codes`. |
| `waste_code_groups` | Raw EPA group labels (`DMIX`, `TCMT`, `LABP`, `ICR`, `F1_5` …). Classifications, not codes. |

**Generator status**

| Field | Description |
|---|---|
| `generator_status` | Most severe RCRA generator status across the facility's lines (LQG / SQG / VSQG / …). |
| `generator_status_code` | The raw EPA letter behind it (`L`/`S`/`V`/`C`/`E`/`N`). |
| `generator_status_codes` | Every distinct status letter filed, so a mixed filing is visible. |
| `large_quantity_generator_flag` | `true`/`false` from EPA's status; **`null`** when EPA reported none and tonnage is below the fallback line. |
| `lqg_flag_basis` | `epa_status`, `tonnage_heuristic`, or `unknown`. |

**Management & destination**

| Field | Description |
|---|---|
| `management_method_codes` | EPA management-method H-codes (`H040`, `H061`, `H134`, `H141` …). |
| `management_method_breakdown` | Per H-code: code, EPA category, line count, tons. Sorted by tons. |
| `top_management_method_code` / `top_management_method_category` / `top_management_method_tons` | The method handling the most tons: the EPA management-method code, its published category label, and the tons it handled. `top_management_method_category` is `null` where EPA publishes no label for the code (`H141`, `H042`) — no label is invented. |
| `dominant_management_category` | Legacy v1.0 field. EPA leaves this blank on 53.6% of Delaware rows; prefer the H-code fields. |
| `offsite_receiver_ids`, `n_offsite_receivers`, `primary_receiver_id`, `receiver_states` | The TSDFs this generator ships to. |

**Line types, characteristics & provenance**

| Field | Description |
|---|---|
| `n_waste_streams` | Total BR lines filed, all form types. |
| `n_generated_waste_streams` / `n_received_waste_streams` / `n_no_activity_lines` | GM / WR / XX split. |
| `is_receiving_facility` | True when the facility filed at least one Waste Received line. |
| `filed_no_activity` | True when *every* line was a no-activity filing. |
| `mixed_radioactive_waste_flag`, `acute_waste_reported_flag`, `priority_chemical_flag`, `state_only_waste_flag` | `null` when EPA reported nothing on that dimension. |
| `waste_minimization_codes`, `source_codes`, `form_codes` | EPA waste-minimization, waste-origin and physical-form codes. |
| `br_status` | `ok`, or `ok_unverified_count` when the COUNT probe itself was unavailable. |
| `state_rows_expected` / `state_rows_retrieved` | Upstream-declared vs actually-retrieved row counts. Equal on every emitted row. |
| `latest_cycle_detected`, `newer_cycle_available` | Live freshness probe, so a stale cycle is never served as current. |
| `source_url`, `checked_at`, `disclaimer` | Provenance. |

**Cycle-over-cycle (when `compareToPriorCycle` is on)**

| Field | Description |
|---|---|
| `prior_cycle`, `prior_cycle_generation_tons`, `prior_cycle_generator_status` | The previous biennial cycle. |
| `generation_tons_delta`, `generation_pct_change` | Change versus the prior cycle. `null` across the 2021 gap, because EPA published no 2021 generation tonnage. |
| `prior_cycle_shipped_tons`, `shipped_tons_delta`, `shipped_tons_pct_change` | Shipped-tons trend. `shipped_tons` is populated in **every** cycle, so this is the only apples-to-apples comparison that spans 2021. |
| `generator_status_changed` | True when the status moved (e.g. SQG → LQG). |
| `new_generator_this_cycle` | True when the facility filed this cycle but not last. |
| `prior_cycle_status` | `ok`, `unavailable` (every prior field is `null`, never 0), or `not_requested`. |

### Use as an MCP tool

This actor is a clean, chainable tool for AI agents (Claude, Cursor, …) via
`mcp.apify.com`. Every output field carries a description, so an agent can answer
"who are the largest hazardous-waste generators in Harris County", "does facility X
report LQG status", or "which Delaware generators produce acute P-listed waste" and
pass the structured result into the next step. Null-vs-false is a contract here: a
`null` means the source did not answer, never that the answer was negative.

### Notes & FAQ

**Where does the data come from?** EPA Envirofacts RCRAInfo Biennial Report
(`data.epa.gov/efservice/BR_REPORTING`) — the federal hazardous-waste generation and
management dataset, filed every two years by generators and TSDFs. No API key needed.

**Which report cycles are available?** Every biennial cycle from **2005 to 2023** —
2005, 2007, 2009, 2011, 2013, 2015, 2017, 2019, 2021, 2023 — all confirmed live on
2026-07-30 (national row counts run from 1.15M in 2005 to 1.77M in 2023). 2023 is the
latest; EPA reports 0 rows for 2025, and asking for it fails the run loudly rather than
returning an empty dataset. The actor's own docs previously claimed only 2019/2021/2023.
**Note on 2021:** EPA publishes no generation or managed tonnage for that cycle
anywhere in the country, only shipped tonnage. Rows from a 2021 run carry
`generation_tons_reported: false` and rank on shipped tons.
The actor probes EPA at start-up; if a newer cycle has appeared, every row carries
`newer_cycle_available: true` and `latest_cycle_detected`.

**Are the tonnages exact?** No. They are **self-reported** figures certified by each
handler for the cycle and aggregated across every waste-stream line. They are subject
to correction and are not an official EPA determination. This is a screening and
analytics aid, not a compliance filing.

**Does it include coordinates?** No, and none are invented. The Biennial Report has no
lat/lon, so this is a county / city / ZIP **inventory rollup**, not a proximity tool.

**How do I filter by industry?** Use `naicsPrefix` (`"324"` petroleum & coal, `"3254"`
pharmaceutical manufacturing, `"3345"` electronics). Use `minTons` to keep only larger
generators. Statewide context fields are computed **before** filtering, so
`share_of_state_generation_pct` stays honest.

**What happens if EPA is down or a state comes back short?** The run **fails** with a
message naming the state and the row-count gap, and nothing is billed. A partial rollup
would understate tonnages and misorder the ranking while looking perfectly normal, so
it is never emitted. A state that genuinely has zero BR filings for a cycle is reported
as a legitimate empty, not a failure.

**Why is a field `null` instead of `0` or `false`?** Because `null` means *not checked*
and `false` means *checked and negative*. A compliance buyer acting on "0 tons" when
the truth is "not reported" makes a real regulatory error, so this actor never converts
a missing answer into a confident one.

**How large can a state be?** California 2023 is 285,108 BR lines and Texas 50,714. The
actor pages through all of them and verifies the total against EPA's own count. Turning
`compareToPriorCycle` off halves the fetch on very large states.

### Pricing

Pay-per-result: **$8 per 1,000 records** returned, with graduated discounts on paid
Apify plans. You pay only for the generator records emitted — a failed run emits
nothing and bills nothing. Use `maxResults` to cap spend.

### Related actors

- **[EPA Contaminated Site Screener](https://apify.com/malonestar/epa-contaminated-site-screener)** — Superfund/SEMS, RCRA corrective action, ECHO enforcement and UST proximity for a coordinate. The Phase I ESA companion to this inventory rollup.
- **[EPA GHGRP Emitter Screener](https://apify.com/malonestar/epa-ghgrp-emitter-screener)** — greenhouse-gas emissions by facility, parent company and year.
- **[State Tank & Spill Registry Screener](https://apify.com/malonestar/state-tank-spill-registry-screener)** — UST/LUST tanks and reported spills from state registries.
- **[Site Due Diligence Bundle](https://apify.com/malonestar/site-due-diligence-bundle)** — 20 environmental and regulatory layers for one coordinate in a single call.

### Column coverage — the input that populates each conditional field

Every declared field is populated by at least one of these four inputs, all run live
against EPA on 2026-07-30 (556 rows, **zero dead columns** across the set):

| Input | What it proves |
|---|---|
| `{"states":["Delaware"],"reportCycle":2023,"compareToPriorCycle":true}` — 88 rows | The default. Generation tonnage, waste-code classes, management methods, statewide share, shipped-tons trend vs 2021. |
| `{"states":["Texas"],"reportCycle":2023,"compareToPriorCycle":false}` — 50,714 BR rows | `is_receiving_facility`, `total_received_tons`, `managed_tons_from_received`, `received_waste_codes`. Clean Harbors Deer Park files 6 generation lines and **6,228 received-waste lines**. |
| `{"states":["Delaware"],"reportCycle":2021,"minTons":0.5}` — 67 rows | The 2021 tonnage gap: `generation_tons_reported:false`, `ranking_tons_basis:"shipped_tons"`, `share_of_state_ranking_tons_pct`, and `newer_cycle_available:true`. |
| `{"states":["Delaware"],"reportCycle":2019,"compareToPriorCycle":true}` — 101 rows | Generation-tonnage delta vs 2017 (BASF Color & Effects: 1,071 → 1,490 tons, +39.05%). |

# Actor input Schema

## `states` (type: `array`):

US states to roll up, as 2-letter codes or full names (e.g. \["Delaware"] or \["DE","NJ"]). Each state is queried independently and handlers are ranked within their state.

## `reportCycle` (type: `integer`):

RCRA Biennial Report cycle year. The report is filed every two years and EPA Envirofacts serves 2005 through 2023 (all verified live 2026-07-30; 2025 is not published yet and the run fails loudly rather than returning an empty dataset). 2023 is the latest. NOTE: EPA publishes no generation or managed tonnage at all for the 2021 cycle nationwide - 2021 rows carry generation\_tons\_reported=false and rank on shipped tons.

## `naicsPrefix` (type: `string`):

Optional. Keep only handlers whose primary NAICS code starts with this prefix (e.g. "324" for petroleum/coal, "3254" for pharma manufacturing). Leave blank for all industries.

## `minTons` (type: `number`):

Optional. Only emit handlers whose total hazardous-waste generation for the cycle is at least this many US short tons. Default 0 (no minimum).

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

Maximum number of generator rollup records to return (top handlers by total generation tons). You are billed per record returned.

## `compareToPriorCycle` (type: `boolean`):

Fetch the previous biennial cycle (2023 vs 2021) as well and add per-facility tonnage delta, percent change, generator-status change and a new-this-cycle flag. Doubles the upstream fetch, so turn it off for very large states (California 2023 alone is 285,108 rows). When the prior-cycle fetch fails, every prior-cycle field is null rather than 0.

## Actor input object example

```json
{
  "states": [
    "Delaware"
  ],
  "reportCycle": 2023,
  "minTons": 0,
  "maxResults": 2000,
  "compareToPriorCycle": true
}
```

# 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 = {
    "states": [
        "Delaware"
    ],
    "reportCycle": 2023,
    "maxResults": 2000,
    "compareToPriorCycle": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("malonestar/epa-rcra-hazwaste-generator-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 = {
    "states": ["Delaware"],
    "reportCycle": 2023,
    "maxResults": 2000,
    "compareToPriorCycle": True,
}

# Run the Actor and wait for it to finish
run = client.actor("malonestar/epa-rcra-hazwaste-generator-rollup").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 '{
  "states": [
    "Delaware"
  ],
  "reportCycle": 2023,
  "maxResults": 2000,
  "compareToPriorCycle": true
}' |
apify call malonestar/epa-rcra-hazwaste-generator-rollup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=malonestar/epa-rcra-hazwaste-generator-rollup",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/Z0ttQVkZYSdgIklI7/builds/1aTJEuZj9yjAgeei5/openapi.json
