EPA Drinking Water Quality Screener - Violations, Lead & PFAS
Pricing
from $6.60 / 1,000 drinking water screening results
EPA Drinking Water Quality Screener - Violations, Lead & PFAS
Screen any US coordinate for the public water system serving it: SDWA health-based violations, Lead & Copper Rule 90th-percentile results and UCMR5 PFAS detections. Built for Phase I ESA and real-estate due diligence. Never reports a clear result from a source that did not answer.
Pricing
from $6.60 / 1,000 drinking water screening results
Rating
0.0
(0)
Developer
Kyle Maloney
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Give it a coordinate. It tells you which public water system serves that address, what the Safe Drinking Water Act violation record looks like, what the lead and copper 90th-percentile tap results are, and whether PFAS was detected there under EPA's UCMR5 monitoring programme.
Built for Phase I ESA consultants, environmental due-diligence teams, real-estate underwriters and water-utility analysts who need a defensible drinking-water screen per property, in bulk, from authoritative federal sources.
The design rule that matters most: this actor will not tell you a system is
clean on a source it never successfully read. null means not checked.
false means checked, and the answer is negative. The two are never mixed, and
the overall verdict is gated so it can never read LOW while any contributing
EPA source was unavailable.
Who it is for
| Persona | What they get |
|---|---|
| Phase I ESA consultant | A per-property drinking-water screen with the serving PWSID, health-based violation history, lead action-level exceedances and PFAS results - plus an explicit NO_SERVICE_AREA answer when the site is probably on a private well. |
| Real-estate / lender due diligence | Bulk screening of a portfolio in one run, with a water_quality_screen_level and a plain-English screen_reason per address. |
| Environmental / water engineer | Lead and copper 90th percentiles dated to their actual monitoring period, treatment-technique violations, formal enforcement history and per-analyte PFAS concentrations. |
| PFAS litigation and remediation | UCMR5 detections per system with maximum concentrations, the analytes involved, a Hazard Index screen, and honest non-detect accounting. |
| AI agents / MCP | A clean, chainable tool: coordinates in, one fully-typed row per location out, with a per-source status on every row so an agent can tell "no finding" from "no data". |
Data sources
| Leg | Source | What it provides |
|---|---|---|
| Service area | EPA ORD Community Water System Service Area Boundaries (ArcGIS, ~44,600 polygons) | Point-in-polygon lookup of the serving PWSID |
| Water system | EPA Envirofacts SDWIS WATER_SYSTEM + GEOGRAPHIC_AREA | System name, type, population served, source water, owner, counties served, utility contact |
| Violations | EPA Envirofacts SDWIS VIOLATION + ENFORCEMENT_ACTION | Full SDWA violation history, health-based rollup, formal enforcement |
| Lead and copper | EPA Envirofacts SDWIS LCR_SAMPLE_RESULT joined to LCR_SAMPLE | 90th-percentile lead and copper results, dated to their monitoring period |
| PFAS | EPA UCMR5 occurrence dataset (1,928,117 results, 10,299 systems, 29 PFAS analytes plus lithium) | Detections, maximum concentrations, non-detect counts, Hazard Index screen |
| Code decoding | EPA Envirofacts SDWIS REF_CODE_VALUE | EPA's own 2,171-row code table, fetched every run so labels cannot go stale |
No API key. Every source is public and keyless.
Example input
{"assets": [{ "lat": 43.0125, "lon": -83.6875, "label": "Flint MI - lead action level exceedance" },{ "lat": 40.9793, "lon": -74.1165, "label": "Ridgewood NJ - PFAS detections" },{ "lat": 39.7392, "lon": -104.9903, "label": "Denver CO - control" }],"includeViolations": true,"includeLead": true,"includePfas": true,"violationYears": 10}
You can also screen systems directly by PWSID, with or without coordinates:
{ "pwsids": ["MI0002310", "NJ0251001"] }
What that example actually returns (verified live 2026-08-04)
| Location | System | Verdict | Findings |
|---|---|---|---|
| Flint MI | MI0002310 FLINT, CITY OF | MODERATE | 1 health-based violation (Lead and Copper treatment technique), lead 90th 0.0060 mg/L in the period ending 2025-12-31, 0.020 mg/L historically - above the 0.015 action level - PFAS tested and none detected |
| Ridgewood NJ | NJ0251001 RIDGEWOOD WATER | HIGH | 249 PFAS detections, PFOA 0.0314 ug/L and PFOS both above the 2024 NPDWR MCL screen, Hazard Index 0.494, 1 health-based violation |
| Denver CO | CO0116001 DENVER WATER BOARD | LOW | 128 violations, none health-based, lead 0.0031 mg/L, PFAS tested and none detected, lithium 10.8 ug/L (lithium is not a PFAS) |
Three things that are easy to get wrong, and how this actor handles them
1. A non-detect is not zero, and neither is "not tested"
97.1% of UCMR5 results (1,872,227 of 1,928,117) are non-detects. EPA writes them
as AnalyticalResultsSign = '<' with an empty value: the analyte was looked
for and not found above the minimum reporting level. Publishing that as 0
would be a fabricated clean measurement.
All four states are distinguishable on the row:
| Situation | pfas_system_tested | pfas_detected | pfas_nondetect_count |
|---|---|---|---|
| Tested, PFAS found | true | true | count of non-detects |
| Tested, nothing above the reporting level | true | false | count of non-detects |
| Not a UCMR5 sampling system | false | null | 0 |
| The PFAS source failed | null | null | null |
pfas_min_reporting_level_ugl tells you what a non-detect actually rules out.
2. Lithium is a UCMR5 analyte and is NOT a PFAS
UCMR5 monitors 30 analytes: 29 PFAS and lithium. Denver detected lithium at
10.8 ug/L and no PFAS at all. Counting lithium as a PFAS detection would report
Denver as a PFAS-affected system. Lithium has its own fields
(lithium_max_ugl, lithium_detected) and can never enter pfas_detected,
pfas_detection_count, the MCL screen or the Hazard Index. A live drift
assertion re-checks exactly this against Denver on every run.
3. A lead 90th percentile with no date is worse than no number
LCR_SAMPLE_RESULT has no date column. The monitoring period lives in a
separate table, LCR_SAMPLE, joined on sample_id. Without that join you can
publish a 2016 result as a system's current lead level. This actor performs the
join, reports lead_90th_period_start and lead_90th_period_end, and states the
join outcome in lead_period_join_status (complete, partial, failed).
lead_90th_mg_l is the most recent monitoring period.
lead_90th_max_ever_mg_l and lead_periods_exceeding_action_level carry the
history - Flint's latest period is 0.0060 mg/L while its worst on record is
0.020 mg/L, and both matter to a buyer.
A reported 0 is a real EPA value meaning "at or below the laboratory detection
limit at the sampled taps" (26-37% of state LCR rows carry it). It is preserved,
never nulled, and flagged with lead_90th_reported_zero so nobody reads it as
proof that no lead is present.
What each screening level means
| Level | Meaning |
|---|---|
HIGH | An unresolved health-based violation, a current lead action-level exceedance, a PFAS result above the 2024 NPDWR MCL screen, or a Hazard Index above 1. |
MODERATE | A health-based violation on record, a historical lead exceedance, lead at or above the 0.010 mg/L trigger level, a copper exceedance, a Tier 1 public notification, or a PFAS detection below the MCL screen. |
LOW | Every requested EPA source answered and none reported any of the above. |
NO_SERVICE_AREA | No EPA-mapped community water system service area covers the location. The site is likely served by a private well, or the boundary is not mapped. This is an answer, not a clear: private wells are not regulated under SDWA and warrant their own investigation. |
INCONCLUSIVE | At least one requested EPA source did not answer. Real findings are still reported; absence of a finding on a failed source is not evidence of absence. |
This is a screening tool, not a regulatory determination. Verify anything material against the primacy agency and the system's Consumer Confidence Report.
About the PFAS MCL comparison
pfas_mcl_screen_exceeded compares the highest UCMR5 detection against the MCL
values published in EPA's 2024 PFAS National Primary Drinking Water Regulation
(89 FR 32532): PFOA 0.004, PFOS 0.004, PFHxS 0.010, PFNA 0.010, HFPO-DA 0.010
ug/L, plus a Hazard Index over PFHxS, PFNA, HFPO-DA and PFBS.
It is a screening comparison only. UCMR5 reports single entry-point samples;
NPDWR compliance is determined by the primacy agency on a running annual average.
This actor never states that a system is in violation of a PFAS MCL. The basis is
restated on every row in pfas_mcl_screen_basis.
Output fields
One row per screened location. Every field below is emitted on every row;
an unavailable source yields null, never an absent key and never false.
Location and screening verdict
| Field | Type | Description |
|---|---|---|
asset_label | string | Your label for this location, echoed back so results can be joined to your own records. |
asset_lat | number | Latitude (WGS84) that was screened. Null for a direct PWSID lookup. |
asset_lon | number | Longitude (WGS84) that was screened. Null for a direct PWSID lookup. |
asset_index | number | Zero-based position of this location in your input array. |
lookup_mode | string | How the water system was identified: 'coordinate' (point-in-polygon against EPA service-area boundaries) or 'pwsid' (you supplied the PWSID directly). |
water_quality_screen_level | string | Overall screening level: HIGH, MODERATE, LOW, NO_SERVICE_AREA or INCONCLUSIVE. INCONCLUSIVE whenever any requested EPA source failed - the verdict can never read LOW on data that was never checked. A screening indicator, not a regulatory determination. |
screen_flags | array | Every screening flag raised, for example UNRESOLVED_HEALTH_BASED_VIOLATION, LEAD_ACTION_LEVEL_EXCEEDED, PFAS_ABOVE_2024_NPDWR_MCL_SCREEN. |
screen_reason | string | Plain-English explanation of the screening level, naming the failed sources when the result is INCONCLUSIVE. |
sources_ok | number | How many of the requested EPA sources answered successfully for this location. |
sources_requested | number | How many EPA sources were requested for this location (boundary, water system, violations, lead, PFAS). |
sources_failed | array | Names of the EPA sources that did not answer. An empty array means every requested source answered. |
sources_not_attempted | array | EPA sources that were never attempted because an upstream lookup failed - for example the violation, lead and PFAS legs when the service-area lookup itself went down. Distinct from sources_failed and from a source that genuinely had nothing to look up. |
data_complete | boolean | True only when every requested EPA source answered and none was skipped by an upstream failure. False means a finding may be missing. |
Serving water system (service-area boundary)
| Field | Type | Description |
|---|---|---|
pwsid | string | EPA Public Water System Identification number of the system serving this location (2-letter primacy agency code plus 7 characters). |
service_area_pws_name | string | Water system name as published on the EPA service-area boundary layer. |
service_area_primacy_agency | string | Primacy agency (usually the state) on the boundary record. |
service_area_population_served | number | Population served, as published on the boundary record. |
service_area_service_connections | number | Service connection count, as published on the boundary record. |
service_area_type | string | Type of service area polygon, for example Residential Area or Municipality. |
service_area_feature_type | string | How EPA describes the polygon, for example 'Service Area Boundary'. |
service_area_data_provider | string | Who supplied the boundary to EPA. |
service_area_data_provider_type | string | Category of the boundary data provider, for example Private Company or State Agency. |
service_area_data_source_url | string | Source URL EPA cites for this boundary. |
service_area_model_method | string | Modelling method used to derive the boundary, when EPA records one. |
service_area_modification_method | string | How EPA modified the supplied boundary, when applicable. |
service_area_method_details | string | Free-text detail on the boundary derivation method. |
service_area_verification_status | string | EPA's verification status for the boundary, when recorded. |
service_area_confirmed | string | Whether the boundary has been confirmed, as recorded by EPA. |
service_area_km2 | number | Area of the service-area polygon in square kilometres. Useful for judging boundary precision. |
service_area_vintage | string | Date the boundary was originally created. Boundaries vary widely in age - some date to 2004. |
echo_detailed_facility_report_url | string | Link to EPA ECHO's Detailed Facility Report for this system. |
boundary_error | string | Error message if the service-area lookup failed. Null on success. |
service_area_found | boolean | True if a mapped EPA service area covers this location, false if none does, null if the boundary lookup failed. False and null are different answers. |
service_areas_matched | number | How many mapped service areas cover this location. More than one is legitimate (a wholesaler plus its purchaser, or overlapping vintages). |
all_pwsids | array | Every PWSID whose mapped service area covers this location, primary first. |
all_pws_names | array | Names of every matching water system, in the same order as all_pwsids. |
boundary_status | string | Outcome of the service-area lookup: 'ok' or 'unavailable'. |
boundary_source | string | Attribution for the service-area boundary layer. |
boundary_layer_national_count | number | Total service-area records in EPA's national layer at run time, measured live as a truncation and drift guard. |
Water system record (SDWIS)
| Field | Type | Description |
|---|---|---|
pws_name | string | Water system name from EPA SDWIS. |
pws_type_code | string | SDWIS water system type code (CWS, NTNCWS, TNCWS, NP, U). |
pws_type | string | Decoded water system type, for example 'Community water system'. |
pws_activity_code | string | SDWIS activity code (A active, I inactive, and others). |
pws_activity | string | Decoded activity status, for example 'Active'. |
pws_is_active | boolean | True if SDWIS marks the system active, false if not, null if the water system record was not retrieved. |
pws_deactivation_date | string | Date the system was deactivated in SDWIS, if applicable. |
primacy_agency_code | string | Two-character primacy agency code (usually the state). |
primacy_agency | string | Decoded primacy agency name. |
primacy_type | string | Whether primacy is held by the State, EPA or a Tribe. |
epa_region | string | EPA region number for the system. |
state_code | string | State code recorded on the SDWIS water system record. |
population_served_count | number | Population served, from SDWIS. May differ from the boundary layer's figure. |
service_connections_count | number | Number of service connections, from SDWIS. |
owner_type_code | string | SDWIS owner type code (F, L, M, N, P, S). |
owner_type | string | Decoded owner type, for example 'Local government' or 'Private'. |
primary_source_code | string | SDWIS primary water source code (GW, GWP, SW, SWP, GU, GUP). |
primary_source | string | Decoded primary water source, for example 'Surface water purchased'. |
gw_sw_code | string | Ground water / surface water code. |
gw_sw | string | Decoded ground water or surface water designation. |
is_wholesaler | boolean | True if the system sells water to other systems, false if not, null if not recorded. |
is_school_or_daycare | boolean | True if the system serves a school or daycare, false if not, null if not recorded. |
is_grant_eligible | boolean | True if EPA records the system as grant eligible. |
source_water_protection | boolean | Whether a source-water protection programme is recorded for the system. |
outstanding_performer | string | EPA 'outstanding performer' designation, when recorded. |
pws_address | string | Mailing address of the water system's administrative office. |
pws_city | string | City of the water system's administrative office. |
pws_zip | string | ZIP code of the water system's administrative office. |
pws_admin_name | string | Administrative contact recorded in SDWIS for the utility. |
pws_org_name | string | Organisation contact name recorded in SDWIS for the utility. |
pws_phone | string | Utility contact phone number published in SDWIS. |
pws_email | string | Utility contact email published in SDWIS. |
season_begin_date | string | Start of the operating season for seasonal systems. |
season_end_date | string | End of the operating season for seasonal systems. |
counties_served | array | Counties the system serves, from SDWIS GEOGRAPHIC_AREA. |
cities_served | array | Cities the system serves, from SDWIS GEOGRAPHIC_AREA. |
zip_codes_served | array | ZIP codes the system serves, from SDWIS GEOGRAPHIC_AREA. |
geographic_area_types | array | Kinds of geographic area recorded for the system (County, City, ZIP Code, Tribal, Indian Reservation). |
geographic_area_rows | number | Number of GEOGRAPHIC_AREA rows retrieved for the system. |
water_system_record_found | boolean | True if SDWIS holds a WATER_SYSTEM record for this PWSID, false if the lookup succeeded and returned none, null if the lookup failed. |
water_system_rows_expected | number | Row count EPA's own COUNT endpoint reported for the WATER_SYSTEM query, used as an exact truncation guard. |
geographic_area_status | string | Outcome of the GEOGRAPHIC_AREA lookup: 'ok' or 'unavailable'. |
system_error | string | Error message if the water system lookup failed. Null on success. |
system_status | string | Outcome of the water system leg: 'ok', 'unavailable', 'skipped' or 'not_applicable'. |
Safe Drinking Water Act violations
| Field | Type | Description |
|---|---|---|
violations_total | number | Total Safe Drinking Water Act violations on record for this system, all years and all categories. |
violations_health_based | number | Violations EPA itself flags as health-based (is_health_based_ind = Y): MCL, MRDL and treatment-technique violations. Not derived here. |
violations_health_based_in_window | number | Health-based violations whose compliance period falls inside your violationYears window. Null when the window is disabled. |
violations_health_based_unresolved | number | Health-based violations SDWIS still marks Open. This is the flag most Phase I reviewers care about. |
violations_health_based_known_unreturned | number | Health-based violations with SDWIS compliance status 'Known' - recorded but not returned to compliance. Reported separately from Open rather than lumped in, because EPA distinguishes them. |
violations_health_based_returned_to_compliance | number | Health-based violations SDWIS marks Returned to Compliance. |
violations_unresolved_total | number | All violations, health-based or not, SDWIS still marks Open. |
violations_by_category | object | Violation counts keyed by SDWIS violation_category_code (MCL, MRDL, TT, MR, MON, RPT, Other). |
violations_by_compliance_status | object | Violation counts keyed by SDWIS compliance_status_code (O Open, K Known, R Returned to Compliance, I System Inactive). |
violations_mcl | number | Maximum Contaminant Level violations. |
violations_mrdl | number | Maximum Residual Disinfectant Level violations. |
violations_treatment_technique | number | Treatment technique violations. |
violations_monitoring_reporting | number | Monitoring and reporting violations (categories MR, MON and RPT combined). |
violations_lead_copper_rule | number | Violations under the Lead and Copper Rule family (rule family 350). |
violations_public_notification_tier1 | number | Violations that carried a Tier 1 public notification - the most urgent tier, requiring notice within 24 hours. |
violations_earliest_date | string | Earliest violation compliance-period date on record. |
violations_latest_date | string | Most recent violation compliance-period date on record. |
health_based_contaminants | array | Distinct contaminants named on the system's health-based violations, decoded using EPA's own REF_CODE_VALUE table. Null when EPA publishes no description for the codes involved - see health_based_contaminant_codes, which is always populated. |
health_based_contaminant_codes | array | Raw SDWIS contaminant codes on the system's health-based violations. Always populated when health-based violations exist. EPA's reference table omits several modern codes (5200, the Revised Lead and Copper Rule contaminant, is one), so the raw code is authoritative and the decoded name may be null. |
health_based_contaminants_undecoded | number | How many of the health-based contaminant codes EPA publishes no description for. Non-zero is normal and reflects a gap in EPA's own reference table, not a gap in the violation record. |
most_recent_health_based_violation | object | The most recent health-based violation as a full object (type, category, contaminant, rule, compliance status, dates, public notification tier). |
most_recent_health_based_violation_date | string | Compliance-period date of the most recent health-based violation. |
health_based_violation_details | array | Up to maxViolationDetails individual health-based violation records, newest first, each fully decoded. |
health_based_violation_details_truncated | boolean | True when more health-based violations exist than were included in the details array. Counts above are never truncated. |
enforcement_actions_total | number | Number of formal SDWIS enforcement actions on record for this system. |
most_recent_enforcement_date | string | Date of the most recent formal enforcement action. |
most_recent_enforcement_type | string | Decoded type of the most recent formal enforcement action. |
most_recent_enforcement_type_code | string | Raw SDWIS enforcement action type code for the most recent action. |
violations_rows_expected | number | Row count EPA's own COUNT endpoint reported for the violation query, used as an exact truncation guard. |
violations_rows_retrieved | number | Violation rows actually retrieved. A shortfall against violations_rows_expected fails the leg rather than publishing a rollup over a subset. |
enforcement_status | string | Outcome of the enforcement lookup: 'ok', 'unavailable' or 'skipped'. |
violations_error | string | Error message if the violation lookup failed. Null on success. |
violations_status | string | Outcome of the violations leg: 'ok', 'unavailable', 'skipped' or 'not_applicable'. |
Lead and copper (LCR 90th percentile)
| Field | Type | Description |
|---|---|---|
lead_90th_mg_l | number | Lead and Copper Rule 90th-percentile tap result. Value for the MOST RECENT monitoring period, in mg/L. Reported by the water system under 40 CFR 141 Subpart I. |
lead_90th_period_start | string | Start of the monitoring period the reported lead 90th percentile belongs to. EPA publishes the result and the period in separate tables; this actor joins them, because an undated 90th percentile can present a decade-old result as current. |
lead_90th_period_end | string | End of the monitoring period the reported lead 90th percentile belongs to. |
lead_90th_result_sign_code | string | EPA result sign code on the lead result. 'L' means the value is an upper bound (below minimum detection limits), 'E' means exactly equal to the value reported. Null means EPA published no sign, which is the case on every row observed to date. |
lead_90th_below_detection | boolean | True when the lead result sign code is 'L' and the reported number is therefore an upper bound rather than a measurement. Null when EPA published no sign code. |
lead_90th_reported_zero | boolean | True when EPA published the lead 90th percentile as exactly 0. That is EPA's encoding of 'at or below the laboratory detection limit at the sampled taps' - it is a reported value, not a missing one, and it is not proof that no lead is present. Between 26% and 37% of state LCR rows carry it. |
lead_action_level_mg_l | number | The federal lead action level, 0.015 mg/L. Constant, included so a row is self-describing. |
lead_action_level_exceeded | boolean | True if the most recent lead 90th percentile exceeds 0.015 mg/L, false if it does not, null if the lead leg did not answer or the system has no reported lead result. |
lead_90th_max_ever_mg_l | number | Highest lead 90th percentile on record for this system across all monitoring periods. |
lead_90th_max_ever_period_end | string | End of the monitoring period in which the highest lead 90th percentile was reported. |
lead_periods_exceeding_action_level | number | How many distinct monitoring periods exceeded the lead action level. A system can be compliant today and have exceeded repeatedly in the past. |
lead_monitoring_periods | number | Number of distinct lead monitoring periods on record. |
lead_results_reported | number | Number of lead 90th-percentile results retrieved for this system. |
copper_90th_mg_l | number | Copper 90th-percentile tap result for the most recent monitoring period, in mg/L. |
copper_90th_period_start | string | Start of the monitoring period the reported copper 90th percentile belongs to. |
copper_90th_period_end | string | End of the monitoring period the reported copper 90th percentile belongs to. |
copper_90th_result_sign_code | string | EPA result sign code on the copper result ('L' below detection, 'E' exactly equal). Null when EPA published no sign. |
copper_90th_below_detection | boolean | True when the copper result is an upper bound rather than a measurement. Null when EPA published no sign code. |
copper_90th_reported_zero | boolean | True when EPA published the copper 90th percentile as exactly 0 - a reported value meaning at or below detection, not a missing one. |
copper_action_level_mg_l | number | The federal copper action level, 1.3 mg/L. Constant, included so a row is self-describing. |
copper_action_level_exceeded | boolean | True if the most recent copper 90th percentile exceeds 1.3 mg/L, false if it does not, null if the leg did not answer or no result exists. |
copper_90th_max_ever_mg_l | number | Highest copper 90th percentile on record for this system. |
copper_90th_max_ever_period_end | string | End of the monitoring period in which the highest copper 90th percentile was reported. |
copper_periods_exceeding_action_level | number | How many distinct monitoring periods exceeded the copper action level. |
copper_monitoring_periods | number | Number of distinct copper monitoring periods on record. |
copper_results_reported | number | Number of copper 90th-percentile results retrieved for this system. |
lcr_results_total | number | Total Lead and Copper Rule sample-result rows retrieved for this system (lead and copper combined). |
lead_period_join_status | string | How well the lead and copper results joined to their monitoring periods: 'complete', 'partial', 'failed', 'no_results' or 'not_attempted'. Anything but 'complete' means some reported values are undated. |
lead_period_join_matched | number | How many result rows were successfully matched to a monitoring period. |
lcr_rows_expected | number | Row count EPA's own COUNT endpoint reported for the lead and copper query, used as an exact truncation guard. |
lcr_sample_period_error | string | Error message if the monitoring-period lookup failed while the results themselves succeeded. |
lead_error | string | Error message if the lead and copper leg failed. Null on success. |
lead_status | string | Outcome of the lead and copper leg: 'ok', 'unavailable', 'skipped' or 'not_applicable'. |
PFAS (UCMR5)
| Field | Type | Description |
|---|---|---|
pfas_ucmr5_vintage | string | Publication timestamp of the UCMR5 occurrence file actually used, read from its Last-Modified header. The download URL contains '2023-08' but EPA replaces the file in place, so the path is NOT the vintage. |
pfas_ucmr5_source_url | string | URL of the EPA UCMR5 occurrence dataset used for the PFAS screen. |
pfas_ucmr5_national_rows | number | Total UCMR5 result rows indexed on this run, measured live as a truncation guard. |
pfas_ucmr5_national_systems | number | Total public water systems present in UCMR5 on this run. |
pfas_mcl_screen_basis | string | Plain statement of what the PFAS MCL comparison is and is not: a screening comparison against EPA's 2024 PFAS NPDWR values, not a compliance determination, because UCMR5 reports single entry-point samples rather than the running annual average used for compliance. |
pfas_system_tested | boolean | True if this system appears in UCMR5 at all, false if it does not (most small systems were not in the UCMR5 sample), null if the PFAS leg failed. False and null are different answers. |
pfas_results_count | number | Number of UCMR5 result rows for this system, detections and non-detects combined. |
pfas_analytes_tested | number | Number of distinct UCMR5 analytes this system was tested for (up to 30: 29 PFAS plus lithium). |
pfas_sample_points | number | Number of distinct entry-point sample locations UCMR5 holds for this system. |
pfas_nondetect_count | number | Number of UCMR5 results reported as non-detect - the analyte was looked for and not found above the minimum reporting level. A non-detect is NOT zero and NOT untested. |
pfas_min_reporting_level_ugl | number | Lowest minimum reporting level applied to this system's UCMR5 samples, in micrograms per litre. A non-detect only rules out concentrations above this level. |
pfas_first_collection_date | string | Earliest UCMR5 sample collection date for this system. |
pfas_last_collection_date | string | Most recent UCMR5 sample collection date for this system. |
pfas_detected | boolean | True if any PFAS analyte was detected, false if the system was tested and no PFAS was detected, null if the PFAS leg did not answer. Lithium is a UCMR5 analyte but is NOT a PFAS and never sets this true. |
pfas_detection_count | number | Number of individual PFAS detections (excluding lithium) across all of this system's UCMR5 samples. |
pfas_analytes_detected | array | Names of the PFAS analytes detected at this system, alphabetically. Lithium is deliberately excluded. |
pfas_max_analyte | string | PFAS analyte with the highest detected concentration at this system. |
pfas_max_ugl | number | Highest single PFAS concentration detected at this system, in micrograms per litre. |
pfoa_max_ugl | number | Highest detected PFOA concentration, in micrograms per litre. The 2024 NPDWR MCL is 0.004 ug/L. |
pfos_max_ugl | number | Highest detected PFOS concentration, in micrograms per litre. The 2024 NPDWR MCL is 0.004 ug/L. |
pfhxs_max_ugl | number | Highest detected PFHxS concentration, in micrograms per litre. The 2024 NPDWR MCL is 0.010 ug/L. |
pfna_max_ugl | number | Highest detected PFNA concentration, in micrograms per litre. The 2024 NPDWR MCL is 0.010 ug/L. |
hfpo_da_max_ugl | number | Highest detected HFPO-DA (GenX chemicals) concentration, in micrograms per litre. The 2024 NPDWR MCL is 0.010 ug/L. |
pfbs_max_ugl | number | Highest detected PFBS concentration, in micrograms per litre. PFBS has no individual MCL; it is one of the four Hazard Index analytes. |
pfas_hazard_index_screen | number | Unitless Hazard Index computed over PFHxS, PFNA, HFPO-DA and PFBS against their published health-based water concentrations. Null when the system was not tested for any Hazard Index analyte. |
pfas_hazard_index_screen_exceeded | boolean | True if the Hazard Index screen exceeds 1.0, false if it does not, null if it could not be computed. |
pfas_mcl_screen_exceedances | array | PFAS analytes whose highest detected concentration exceeds the corresponding 2024 NPDWR MCL. A screening comparison only - see pfas_mcl_screen_basis. |
pfas_mcl_screen_exceeded | boolean | True if any detected PFAS exceeds its 2024 NPDWR MCL on this screening comparison, false if none does, null if the PFAS leg did not answer. |
lithium_max_ugl | number | Highest detected lithium concentration, in micrograms per litre. Lithium is a UCMR5 analyte and is NOT a PFAS; it is reported separately so it can never inflate a PFAS count. |
lithium_detected | boolean | True if lithium was detected, false if the system was tested for lithium and none was detected, null if lithium was not among the analytes tested or the leg did not answer. |
pfas_error | string | Error message if the PFAS leg failed. Null on success. |
pfas_status | string | Outcome of the PFAS leg: 'ok', 'unavailable', 'skipped' or 'not_applicable'. |
Provenance and source integrity
| Field | Type | Description |
|---|---|---|
retrieved_at | string | UTC timestamp when this run queried EPA. |
sdwis_source | string | Attribution for the tabular data (EPA Envirofacts SDWIS). |
actor_version | string | Version of this actor that produced the row. |
drift_status | string | Result of this run's live source-integrity checks: 'ok' or 'degraded'. A 'failed' status fails the run before any row is emitted. |
drift_measurements | string | JSON string of every value measured by the live drift checks on this run (national record counts, canary results, UCMR5 vintage and vocabulary), so source health is auditable. |
Total: 186 declared fields, every one of them documented above.
Fields that are legitimately null on a healthy run
Some fields are null because the answer is genuinely absent, not because something failed. Each has a recorded input that populates it.
| Field(s) | Why null, and what populates it |
|---|---|
boundary_error, system_error, violations_error, lead_error, pfas_error, lcr_sample_period_error | Null is good news - the source answered. Populate with {"simulateOutage": "lead"} or any other leg name. |
copper_90th_*, copper_action_level_exceeded, copper_periods_exceeding_action_level | Most systems report lead only. Populate with {"pwsids": ["RI1583829"]} - 8 copper results to 2 lead. |
health_based_contaminants | EPA's own REF_CODE_VALUE table omits several modern codes, so the decoded name can be null while health_based_contaminant_codes is populated. See below. |
lead_90th_result_sign_code, lead_90th_below_detection, and the copper equivalents | EPA publishes no result sign on any LCR row observed to date. The vocabulary (E equal, L below detection) is asserted live, so the day it starts appearing the value is carried through rather than ignored. |
pws_deactivation_date, season_begin_date, season_end_date, outstanding_performer | Only populated for deactivated, seasonal or EPA-designated systems. |
hfpo_da_max_ugl, pfna_max_ugl and the other per-analyte maxima | Null when that analyte was never detected at the system. pfas_analytes_tested confirms it was looked for. |
sources_failed, sources_not_attempted | Empty arrays on a healthy run. |
zip_codes_served, cities_served | Many states report only the county in SDWIS GEOGRAPHIC_AREA. |
A known gap in EPA's own reference table
REF_CODE_VALUE is EPA's authoritative code decoder and this actor fetches it on
every run - but it is materially incomplete for newer SDWIS codes. Measured live
on 2026-08-04 across Michigan's 63,401 violations: 18 alphanumeric violation
codes are absent from it (3A alone appears 22,955 times), along with 8
contaminant codes including 5200 and rule code 351, the Revised Lead and
Copper Rule codes.
This actor never invents a label. The raw code is always emitted next to the
decoded name, health_based_contaminants_undecoded counts how many labels EPA
does not publish, and every undecoded code is listed in the run log. The one
documented exception is violation_category_code MON and RPT, which live
data carries and EPA's table omits; those two are supplied locally and the
substitution is stated in the source.
Live source-integrity checks
Offline tests cannot see a silently degraded upstream, because the fixtures were shaped from the degraded data. Every run therefore performs 23 live drift assertions before a single billable row is emitted, and logs every measured value. A critical failure fails the run and bills nothing.
Among them:
- The boundary layer resolves, still exposes all 20 fields this actor requests, holds a national record count in band (44,615 measured), and answers in WGS84.
- Positive canary: the point 43.0125, -83.6875 must resolve to
MI0002310. - Negative control: a point in the open Pacific must match no service area. This is what catches a query that silently matches nothing.
- The legacy SDWIS table names must still answer, with national counts in band
(
WATER_SYSTEM434,040,VIOLATION2,098,792,LCR_SAMPLE_RESULT279,257). MI0002310must still show a health-based violation and a lead result at or above the action level.NC0392373must still show PFHxS at or above 0.05 ug/L.- The lithium trap:
CO0116001must still show lithium detected and zero PFAS detections. - UCMR5 must hold 30 analytes, all classified, with a closed sign vocabulary and units that decode to the micro sign rather than U+FFFD.
- The UCMR5 vintage must come from the file's
Last-Modifiedheader. The download URL contains2023-08, but EPA replaces the file in place - the copy served on 2026-08-04 was last modified 2026-02-12. The real vintage is published on every row inpfas_ucmr5_vintage.
Failure behaviour
| Situation | What happens |
|---|---|
No usable assets or pwsids | The run FAILS with a message naming the field and an example value. Nothing emitted, nothing billed. |
| A coordinate is out of range | That location is rejected, named in the log, and no row is emitted for it - you are not billed for an unusable input. Other locations still run. |
| One EPA source is down | The row is still emitted with that leg's fields null, <leg>_status set to unavailable, and the verdict forced to INCONCLUSIVE. |
| Every source is down for every location | Buffered rows are discarded before any push and the run FAILS. Zero rows, zero billable events. |
| A live drift assertion fails critically | The run fails before the first push. Zero rows, zero billable events. |
| Upstream answered and there is genuinely nothing | SUCCEEDED with an honest row - for example NO_SERVICE_AREA, or a system with zero violations. |
You can prove any of this yourself with the simulateOutage input.
Performance and the PFAS cache
UCMR5_All.txt is 308,819,534 bytes uncompressed. It is streamed and filtered
rather than materialised, and the per-system index is cached in a named
key-value store keyed to the file's Last-Modified vintage. The first run in a
new vintage rebuilds the index in about 40 seconds; later runs reuse it. EPA
republishing the file invalidates the cache automatically - no manual refresh is
needed, though refreshPfasCache: true forces one.
Set includePfas: false to skip that leg entirely when PFAS is out of scope.
Use as an MCP tool
This actor is callable by AI agents through mcp.apify.com. Every output field
carries a description, and the per-source status fields let an agent distinguish
"EPA reports no health-based violations" from "the violation source did not
answer" - a distinction most tools collapse, and one that matters a great deal
in an environmental screen.
Pricing
Pay per result: $12 per 1,000 rows, one row per screened location. Graduated paid-plan discounts apply (Bronze 20%, Silver 30%, Gold 45%, Platinum 60%, Diamond 70%).
A run that fails loudly emits zero rows and bills zero result events.
Related actors
Same buyer, same due-diligence workflow:
- EPA Contaminated Site Screener - Superfund, RCRA, USTs, spills and ECHO enforcement within a radius of the same coordinate.
- Site Due Diligence Bundle - a 20-layer federation for one coordinate in one call, covering contamination, wetlands, flood, seismic and habitat.
- EPA Impaired Waters 303(d) Screener - surface-water impairments and TMDLs near the site; the surface-water counterpart to this actor's drinking-water view.
FAQ
How do I find out which water utility serves an address?
Pass the address coordinates in assets. The actor returns pwsid, pws_name,
population_served_count, the counties served, and the utility's published
contact details.
How do I check a water system for lead?
lead_90th_mg_l is the most recent 90th-percentile tap result with its
monitoring period. lead_action_level_exceeded compares it to the 0.015 mg/L
federal action level. lead_90th_max_ever_mg_l and
lead_periods_exceeding_action_level give the history, which is what a system
like Flint makes clear: currently compliant, historically not.
How do I check whether a water system has PFAS?
pfas_detected, pfas_analytes_detected and pfas_max_ugl come from EPA's
UCMR5 monitoring. pfas_mcl_screen_exceeded compares detections against the 2024
NPDWR MCL values as a screen. If pfas_system_tested is false, the system was
simply not in the UCMR5 sample - that is not a clean result.
What if my site is on a private well?
You get a NO_SERVICE_AREA row saying so explicitly. SDWA does not regulate
private wells, so a drinking-water screen from SDWIS cannot cover the site.
Does this cover every US water system?
SDWIS covers all regulated public water systems. The service-area boundary
layer maps roughly 44,600 of them, so a coordinate lookup can miss a system whose
boundary EPA has not published. Use pwsids to screen a specific system directly
when you already know it.
Is a lead result of 0 mg/L proof there is no lead?
No. EPA uses 0 to mean "at or below the laboratory detection limit at the
sampled taps". The row flags it with lead_90th_reported_zero.
Why does a row say INCONCLUSIVE when most of it looks fine?
Because at least one requested EPA source did not answer. The findings you can
see are real; what you cannot see was never checked. sources_failed names the
legs, and data_complete is false.
Can I use this for a Phase I ESA? It is designed for that screening step and reports its own data completeness on every row. It is a screening tool, not a determination - verify material findings with the primacy agency and the system's Consumer Confidence Report.
Is an API key needed? No. Every source is a public, keyless EPA endpoint.