# EPA SDWIS Water Systems & Violations Scraper (`usta/epa-sdwis-water-systems`) Actor

Scrape every active US public water system from EPA SDWIS: PWSID, name, town, population served, service connections, water source, owner type, and its count of safe-drinking-water violations, health-based ones flagged. Filter by state, county, type and size. Facilities only, no personal data.

- **URL**: https://apify.com/usta/epa-sdwis-water-systems.md
- **Developed by:** [US Tech Automations](https://apify.com/usta) (community)
- **Categories:** Business, Developer tools, Agents
- **Stats:** 2 total users, 1 monthly users, 50.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 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.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#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

Scrape every active US public water system out of the EPA's own Safe Drinking Water
Information System (SDWIS) and get back one clean row per system, each carrying its count
of safe-drinking-water violations. Filter by state, county or town, system type and
minimum population served. The EPA SDWIS Water Systems & Violations Scraper reads
Envirofacts live on every run, so a row is as fresh as the EPA's own database, and it runs
on Apify's servers with no API key, no proxy setup and no account at the EPA.

It returns **facilities only**. A public water system is identified by its EPA-issued
PWSID, which is an organisation identifier. Operator names, emails, phone numbers and
street addresses are dropped on the way in. Town and state are kept.

### What each row contains

| Field | What it is |
|---|---|
| `pwsid` | The EPA's own public water system ID |
| `pws_name` | Name of the water system |
| `state` | Two-letter state code |
| `city` | Town the system serves |
| `population_served` | People served |
| `service_connections` | Number of service connections |
| `water_source` | Ground water, surface water, purchased, or under surface influence |
| `owner_type` | Local, state, federal, tribal, private or mixed |
| `violations_total` | Every SDWA violation on record for this system |
| `violations_health_based` | The subset that the EPA flags as health-based |
| `source_url` | The exact EPA Envirofacts URL this row came from |

### How to use it

1. Press **Try for free** on this page.
2. Set **State** to the two-letter code you want, for example `AZ`.
3. Optionally set **County** to keep only systems whose county or town matches.
4. Pick a **System type**: community (CWS), non-transient non-community (NTNCWS), or
   transient non-community (TNCWS).
5. Set **Minimum people served** to skip the tiny systems, and **Max water systems** to
   cap the size of the run.
6. Press **Start**. Export the dataset as JSON, CSV or Excel, or pull it from the API.

### Input example

```json
{
  "state": "AZ",
  "county": "",
  "systemType": "CWS",
  "populationMin": 500,
  "maxItems": 50,
  "timeoutSeconds": 120
}
```

### Output example

```json
{
  "pwsid": "090400001",
  "pws_name": "Topawa Intertie",
  "state": "AZ",
  "city": "Sells",
  "population_served": 1318,
  "service_connections": 292,
  "water_source": "Ground water",
  "owner_type": "Native American",
  "violations_total": 1,
  "violations_health_based": 0,
  "source_url": "https://data.epa.gov/efservice/WATER_SYSTEM/PWSID/090400001/JSON"
}
```

### How much does it cost to scrape EPA SDWIS?

Pay per result: **$0.005 for each water system returned**. There is no start fee and no
monthly charge, so a run that returns nothing costs nothing. 100 systems cost $0.50.
1,000 systems, the hard ceiling for a single run, cost $5.00. Apify's own platform usage
is included in that price.

### Where the data comes from

EPA Envirofacts SDWIS REST service, the `WATER_SYSTEM` and `VIOLATION` tables, at
`https://data.epa.gov/efservice`. This is US Government public-domain data. Every run
reads it live; nothing is cached or resold from a snapshot.

### Frequently asked questions

#### Does it return contact details for the water utility?

No. This Actor returns facilities and their violation counts only. Names, emails, phone
numbers and street addresses are dropped before a row is written.

#### Can a run run away and cost me a lot?

No. `maxItems` is capped at 1,000 and every run carries a timeout you set yourself, so the
most a single run can cost is $5.00.

#### Why does it take about a second per system?

Each system needs a second call to the EPA's `VIOLATION` table to count its violations.
That is what turns a plain facility list into a compliance view.

#### Can I schedule it?

Yes. Use Apify's scheduler to re-run it daily, weekly or monthly and diff the datasets to
see which systems picked up new violations.

#### Can I call it from my own code or from an AI agent?

Yes. Run it through the Apify API, any Apify client library, or the Apify MCP server.

### More from US Tech Automations

We publish machine-readable US public-records feeds at
https://ustechautomations.com/feeds — building permits, licence rosters, federal awards
and more.

### Limits and disclaimers

Not affiliated with, endorsed by, or connected to the United States Environmental
Protection Agency. This Actor reads published public-domain data and does nothing else. It
is not legal, engineering, health or professional advice. Only actively operating systems
(`PWS_ACTIVITY_CODE=A`) are returned. If the EPA's service is down or slow, a run returns
what it managed to read within your timeout and no more.

# Actor input Schema

## `state` (type: `string`):

Which state's water systems to read, e.g. AZ.

## `county` (type: `string`):

Keep only systems whose county or town matches this. Blank means the whole state.

## `systemType` (type: `string`):

CWS = community, TNCWS = transient non-community, NTNCWS = non-transient non-community.

## `populationMin` (type: `integer`):

Keep only systems serving at least this many people. 0 means all.

## `maxItems` (type: `integer`):

Stop after this many systems. Hard ceiling is 1000, so a run cannot run away.

## `timeoutSeconds` (type: `integer`):

Stop collecting after this many seconds so a run cannot hang.

## Actor input object example

```json
{
  "state": "AZ",
  "systemType": "CWS",
  "populationMin": 0,
  "maxItems": 10,
  "timeoutSeconds": 60
}
```

# Actor output Schema

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

All items the run produced, one per record, as JSON.

## `results_csv` (type: `string`):

No description

# 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 = {
    "state": "AZ",
    "systemType": "CWS",
    "maxItems": 10,
    "timeoutSeconds": 60
};

// Run the Actor and wait for it to finish
const run = await client.actor("usta/epa-sdwis-water-systems").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 = {
    "state": "AZ",
    "systemType": "CWS",
    "maxItems": 10,
    "timeoutSeconds": 60,
}

# Run the Actor and wait for it to finish
run = client.actor("usta/epa-sdwis-water-systems").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 '{
  "state": "AZ",
  "systemType": "CWS",
  "maxItems": 10,
  "timeoutSeconds": 60
}' |
apify call usta/epa-sdwis-water-systems --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,usta/epa-sdwis-water-systems"
        }
    }
}

```

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/e3oDHVvlGp63sgmy3/builds/IKIukYkIQKMaZFjJ5/openapi.json
