# EPA Nonattainment Air Permit Screener — NSR/PSD Siting Risk (`malonestar/epa-nonattainment-air-permit-screener`) Actor

EPA nonattainment air-permit screener. Batch point-in-polygon check of coordinates against 11 EPA Clean Air Act NAAQS pollutant standards: returns nonattainment/maintenance status, area name, classification (Marginal-Extreme), Federal Register citation, and NSR/PSD permitting-risk flag per site.

- **URL**: https://apify.com/malonestar/epa-nonattainment-air-permit-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 $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 a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use 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 Nonattainment Air Permit Screener — NSR/PSD Siting Risk

**EPA Clean Air Act nonattainment screener.** Give it a list of coordinates and it runs a server-side point-in-polygon check against **11 EPA National Ambient Air Quality Standards (NAAQS) pollutant layers** — Ozone (1997/2008/2015 standards), Lead, SO2, PM2.5 (three standards), PM10, CO, and NO2 — and returns, for each point: whether it sits inside an active **Nonattainment** area, whether it's in an EPA-tracked **Maintenance** area, the matched **area name(s)**, the **classification severity** (Marginal → Moderate → Serious → Severe → Extreme), the **Federal Register designation citation**, and a plain-English **NSR/PSD permitting-risk flag**.

A hit on `nsr_psd_permitting_flag` means a new or modified major stationary source at that location — an industrial plant, power plant, or large data-center backup-generation fleet — faces Clean Air Act **Nonattainment New Source Review (NNSR)**: mandatory emission offsets plus Lowest Achievable Emission Rate (LAER) controls, instead of the lighter Prevention of Significant Deterioration (PSD) review that applies in attainment areas. It is one of the largest hidden cost and schedule risks in industrial and energy-project siting, and today it's checked one pollutant, one area map, at a time by hand.

One clean, billable row per asset — even for a clear site outside every nonattainment/maintenance area (a useful, valid result).

### Who it's for
- **Air-permit / NSR-PSD consultants** — screen a shortlist of candidate sites for nonattainment exposure before scoping a permit application.
- **Industrial, power-plant, and data-center siting teams** — flag the single biggest hidden air-permitting cost gate before committing to a location.
- **EPCs and project developers** — batch-screen a multi-site portfolio for NAAQS classification and offset requirements.
- **Phase-I ESA / environmental due-diligence** — add a definitive nonattainment/maintenance data point to a site assessment.
- **GIS & data teams / AI agents** — enrich any coordinate list with authoritative EPA nonattainment status without hosting the polygon layers yourself.

### Example input
```json
{
  "assets": [
    { "lat": 29.7604, "lon": -95.3698, "label": "Houston, TX" },
    { "lat": 34.0522, "lon": -118.2437, "label": "Los Angeles, CA" },
    { "lat": 39.7392, "lon": -104.9903, "label": "Denver, CO" }
  ],
  "includeMaintenance": true,
  "maxAssets": 500
}
````

Houston returns `in_nonattainment: true`, `pollutants_nonattainment: ["8-Hour Ozone (2015 Standard)"]`, `worst_classification: "Serious"`, `nsr_psd_permitting_flag: true`. Los Angeles returns hits on **two** standards — 8-Hour Ozone 2015 (`Extreme`) and Lead 2008 — with `worst_classification: "Extreme"`. A site with no nonattainment or maintenance designation returns `clear: true` and `nsr_psd_permitting_flag: false`.

### Output fields (one row per asset)

`asset_label`, `asset_lat`, `asset_lon`, `in_nonattainment`, `in_maintenance`, `nsr_psd_permitting_flag`, `worst_classification`, `pollutants_nonattainment`, `pollutants_maintenance`, `matched_area_count`, `matched_areas` (full detail array: pollutant, area name, state, status, classification, NAAQS standard, Federal Register citation, designation date, EPA region, current design value), `clear`, `note`, `error`, `disclaimer`, `source_url`, `checked_at`.

A single point can match **more than one pollutant standard** (e.g. both current and legacy Ozone standards, or Ozone plus Lead) — the flat row rolls up the worst classification and every nonattainment pollutant name, while `matched_areas` lists each match individually.

### Input options

- `assets` — array of `{ lat, lon, label }`. Required to get results.
- `pollutants` — optional array of standard keys to restrict screening (e.g. `["ozone_8hr_2015","pm25_annual_2012"]`). Leave empty to screen all 11 active standards.
- `includeMaintenance` — default `true`. Set `false` to screen only active Nonattainment designations and drop Maintenance-area matches entirely.
- `maxAssets` — safety cap on assets screened per run (default 500, max 2000).

### Use as an MCP tool

This actor is callable by AI agents (Claude, Cursor, etc.) via [mcp.apify.com](https://mcp.apify.com). Its typed input (`assets[]{lat,lon,label}`) and field-level output descriptions make it a clean, chainable "is this coordinate in an EPA nonattainment area, and does it trigger NSR/PSD review?" tool for siting, permitting, and environmental-diligence agents.

### FAQ

**Is my site in an EPA nonattainment area?** Yes — pass its coordinate and read `in_nonattainment`, `pollutants_nonattainment`, and `matched_areas`.

**Does my project need Nonattainment New Source Review?** `nsr_psd_permitting_flag` is true whenever the point is in an active Nonattainment area for any screened pollutant. New/modified major stationary sources in that area need emission offsets and LAER controls under Clean Air Act NNSR. Confirm applicability (source category, potential-to-emit) with the state permitting agency.

**What does the classification mean?** For pollutants with tiered classifications (mainly Ozone and some PM standards), `worst_classification` reports the most restrictive tier among the pollutants that hit — Marginal, Moderate, Serious, Severe, or Extreme — which drives the size and timing of the required offsets. Some pollutants (e.g. Lead) have no tiered classification; `worst_classification` is null in that case even when nonattainment is true.

**What's the difference between nonattainment and maintenance?** Nonattainment means the area currently fails the NAAQS. Maintenance means the area has attained the standard but remains under an EPA-approved maintenance plan — a redesignation-to-nonattainment risk still exists. Set `includeMaintenance: false` to screen only active nonattainment.

**How current is this data?** The EPA source layer is refreshed **weekly** from an internal OAQPS database, and it's the same authoritative source EPA's own Green Book uses. This is an **informational screen**, not an air-permit applicability determination — confirm with the state permitting agency or EPA Regional Office before any siting, design, or permitting decision.

### Source

EPA Office of Air and Radiation (OAR) Nonattainment Areas and Designations ArcGIS FeatureServer (12 layers, 11 active + 1 revoked standard excluded):
`https://services.arcgis.com/cJ9YHowT8TU7DUyn/arcgis/rest/services/Nonattainment_Areas_and_Designations/FeatureServer`

# Actor input Schema

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

List of points to screen against EPA Clean Air Act Nonattainment/Maintenance area designations. Each item is an object with numeric lat and lon (WGS84 decimal degrees) and an optional label. One billable result row is returned per asset. Example: \[{"lat":29.76,"lon":-95.37,"label":"Houston, TX"}].

## `pollutants` (type: `array`):

Optional. Restrict screening to specific NAAQS pollutant standards by key. Leave empty to screen all 11 active standards. Valid keys: ozone\_8hr\_1997, ozone\_8hr\_2008, ozone\_8hr\_2015, lead\_2008, so2\_1hr\_2010, pm25\_24hr\_2006, pm25\_annual\_1997, pm25\_annual\_2012, pm10\_1987, co\_1971, no2\_1971. Unknown keys are ignored (falls back to all standards).

## `includeMaintenance` (type: `boolean`):

When true (default), areas that have attained the standard but remain in an EPA-tracked Maintenance Plan are reported (in\_maintenance, pollutants\_maintenance) alongside active Nonattainment hits. When false, maintenance-area matches are excluded entirely and only active Nonattainment designations are reported.

## `maxAssets` (type: `integer`):

Maximum number of assets to screen from the assets list (safety cap). Extra assets beyond this are ignored.

## Actor input object example

```json
{
  "assets": [
    {
      "lat": 29.7604,
      "lon": -95.3698,
      "label": "Houston, TX (Ozone 2015 Nonattainment - Serious)"
    },
    {
      "lat": 34.0522,
      "lon": -118.2437,
      "label": "Los Angeles, CA (Ozone 2015 Extreme + Lead Nonattainment)"
    }
  ],
  "pollutants": [],
  "includeMaintenance": true,
  "maxAssets": 500
}
```

# 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": 29.7604,
            "lon": -95.3698,
            "label": "Houston, TX (Ozone 2015 Nonattainment - Serious)"
        },
        {
            "lat": 34.0522,
            "lon": -118.2437,
            "label": "Los Angeles, CA (Ozone 2015 Extreme + Lead Nonattainment)"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("malonestar/epa-nonattainment-air-permit-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 = { "assets": [
        {
            "lat": 29.7604,
            "lon": -95.3698,
            "label": "Houston, TX (Ozone 2015 Nonattainment - Serious)",
        },
        {
            "lat": 34.0522,
            "lon": -118.2437,
            "label": "Los Angeles, CA (Ozone 2015 Extreme + Lead Nonattainment)",
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("malonestar/epa-nonattainment-air-permit-screener").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 '{
  "assets": [
    {
      "lat": 29.7604,
      "lon": -95.3698,
      "label": "Houston, TX (Ozone 2015 Nonattainment - Serious)"
    },
    {
      "lat": 34.0522,
      "lon": -118.2437,
      "label": "Los Angeles, CA (Ozone 2015 Extreme + Lead Nonattainment)"
    }
  ]
}' |
apify call malonestar/epa-nonattainment-air-permit-screener --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "EPA Nonattainment Air Permit Screener — NSR/PSD Siting Risk",
        "description": "EPA nonattainment air-permit screener. Batch point-in-polygon check of coordinates against 11 EPA Clean Air Act NAAQS pollutant standards: returns nonattainment/maintenance status, area name, classification (Marginal-Extreme), Federal Register citation, and NSR/PSD permitting-risk flag per site.",
        "version": "1.0",
        "x-build-id": "OlHi2MOQGqpvvOsz0"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/malonestar~epa-nonattainment-air-permit-screener/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-malonestar-epa-nonattainment-air-permit-screener",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/malonestar~epa-nonattainment-air-permit-screener/runs": {
            "post": {
                "operationId": "runs-sync-malonestar-epa-nonattainment-air-permit-screener",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/malonestar~epa-nonattainment-air-permit-screener/run-sync": {
            "post": {
                "operationId": "run-sync-malonestar-epa-nonattainment-air-permit-screener",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "assets": {
                        "title": "Assets (coordinates to screen)",
                        "type": "array",
                        "description": "List of points to screen against EPA Clean Air Act Nonattainment/Maintenance area designations. Each item is an object with numeric lat and lon (WGS84 decimal degrees) and an optional label. One billable result row is returned per asset. Example: [{\"lat\":29.76,\"lon\":-95.37,\"label\":\"Houston, TX\"}]."
                    },
                    "pollutants": {
                        "title": "Pollutant standards to screen (optional filter)",
                        "type": "array",
                        "description": "Optional. Restrict screening to specific NAAQS pollutant standards by key. Leave empty to screen all 11 active standards. Valid keys: ozone_8hr_1997, ozone_8hr_2008, ozone_8hr_2015, lead_2008, so2_1hr_2010, pm25_24hr_2006, pm25_annual_1997, pm25_annual_2012, pm10_1987, co_1971, no2_1971. Unknown keys are ignored (falls back to all standards).",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "includeMaintenance": {
                        "title": "Include maintenance areas",
                        "type": "boolean",
                        "description": "When true (default), areas that have attained the standard but remain in an EPA-tracked Maintenance Plan are reported (in_maintenance, pollutants_maintenance) alongside active Nonattainment hits. When false, maintenance-area matches are excluded entirely and only active Nonattainment designations are reported.",
                        "default": true
                    },
                    "maxAssets": {
                        "title": "Max assets",
                        "minimum": 1,
                        "maximum": 2000,
                        "type": "integer",
                        "description": "Maximum number of assets to screen from the assets list (safety cap). Extra assets beyond this are ignored.",
                        "default": 500
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
