Live API

UK Water Quality API

Postcode-level water quality data for any UK address. Single endpoint. Under 200ms. Free to start.

Live API Explorer
GET /lookup?postcode=
// Enter a UK postcode and hit Try it →

Everything you need about UK water quality

Built on official DWI annual compliance data and covering every water company in England, Wales and Scotland.

21
UK water companies
1,327
Supply zones
1.8M
Postcodes mapped
30+
Parameters returned
398
DWI notices tracked
<200ms
Response time

Built for real data needs

From property platforms to insurance underwriting — water quality data that fits your product.

🏠

PropTech & Property Search

Add water quality data to property listings and area guides. Hardness classification, PFAS status and supply zone for every UK address.

📋

Conveyancing & Legal

Include water quality in property search reports. PFAS notices, lead levels, and DWI enforcement status as structured data.

🛡️

Insurance & Risk

Price hard water risk and PFAS exposure into home insurance models. Zone-level compliance and contaminant data via a single endpoint.

💧

Home & Health Apps

Personalise filter recommendations and health guidance by location. Full parameter set with legal limits, trend data, and actionable scoring.

Start free. Scale when you need to.

No contracts, no credit card required for the free tier. Paid tiers available immediately.

Free
£0 forever
Testing and small projects
  • 100 calls/month
  • Full API access
  • All 30+ parameters
  • PFAS & DWI notices
  • Email support
Get Free Key
Hobby
Contact us
Personal and side projects
  • 1,000 calls/month
  • Full API access
  • All 30+ parameters
  • PFAS & DWI notices
  • Email support
Get in touch
Enterprise
Custom
Custom pricing, SLAs, and bulk data access for high-volume and data licensing use cases.
Get in touch

API Reference

Endpoint

GET https://api.mytapwater.co.uk/lookup?postcode={postcode}

Authentication

Pass your API key as an HTTP header. This is the only method we recommend for production.

Header (recommended)
X-API-Key: mtw_live_your_key_here
Query-parameter authentication (fallback only — click to expand)
⚠️ Fallback only. Query-parameter authentication is supported for environments where setting HTTP headers is not possible (some legacy serverless platforms, image-tag integrations). Avoid otherwise — API keys passed as query parameters are visible in CDN access logs, browser address bars, browser history, and outbound Referer headers.
Query Parameter (avoid in production)
GET /lookup?postcode=SW1A1AA&api_key=mtw_live_your_key_here

Always prefer -H 'X-API-Key: ...' over ?api_key=... in shell scripts — query parameters leak the key in shell history, process listings (ps aux), and any access logs in the request chain.

Parameters

ParameterTypeRequiredDescription
postcodestringYesUK postcode, with or without space. Case insensitive.
api_keystringCond.Required only if not using the X-API-Key header (recommended). Avoid in browser / client-side calls due to log and Referer exposure.

Response Schema

30+ water quality parameters per postcode.
Parameters counted at the individual measurement level, consistent with how UK water companies report under the Water Supply (Water Quality) Regulations — each unit conversion, individual heavy metal, sample metric and compliance flag is a distinct parameter.
Every field below is always present in the response — fields the water company doesn't publish are returned with value: null and status: "not_reported", so the response shape is stable across all 21 water companies and 1,327 zones.

Top-level & identifiers

FieldTypeDescription
postcodestringNormalised postcode with space (e.g. "SW1A 1AA")
districtstringPostcode district (e.g. "SW1A")
zone.namestringWater supply zone name (e.g. "Kentish Town")
zone.codestringInternal zone identifier (e.g. "0374")
zone.water_companystringWater company name (e.g. "Thames Water")
zone.company_idstringURL-safe slug for the water company (e.g. "thames-water")

Quality scoring

FieldTypeDescription
quality.scoreintegerOverall quality score 0–100. Weighted composite (see methodology below).
quality.score_labelstring"Excellent" (≥80), "Good" (≥60), "Fair" (≥40), "Poor" (<40)
quality.score_descriptionstringPlain-English verdict matched to the score band — full enumeration below
quality.score_description — complete enumeration

Exactly four values are possible. Strings are fixed English (not localised) and identical across all postcodes, tiers and clients. Dashes are em-dashes (Unicode U+2014 "—"), not hyphens.

Bandscore_labelscore_description (verbatim)
score ≥ 80"Excellent""Your water quality is excellent — among the best in the UK"
60 – 79"Good""Good quality water with a few things to be aware of"
40 – 59"Fair""Water has some concerns worth addressing"
score < 40"Poor""Water has significant issues — see recommendations"

An active PFAS notice caps the underlying score at 59 (forcing "Fair" or "Poor"), but the description string itself is identical regardless of why the score sits in that band.

Hardness / minerals — parameters.hardness

FieldTypeUnitDescription
valuenumbermg/l CaCO₃Total hardness as calcium carbonate
min / maxnumbermg/l CaCO₃Annual range where reported
unitstringAlways "mg/l CaCO3"
classificationstringSix-class label — see below
labelstringHuman-readable summary (e.g. "Hard water — significant limescale expected")
value_clarkenumber°ClarkeClarke degrees (where reported)
value_germannumber°dHGerman degrees (°dH)
value_frenchnumber°fFrench degrees (°f)
value_mmolnumbermmol/lmmol/l of CaCO₃
calciumnumbermg/lCalcium content
magnesiumnumbermg/lMagnesium content
statusstring"measured" or "not_reported"
parameters.hardness.label — complete enumeration

The label is built from the six-class classification, with an extra suffix added when hardness_caco3 > 200 mg/l. Seven non-null strings are possible (plus null when the supplier doesn't report hardness). Strings are fixed English (not localised) and identical across all postcodes, tiers and clients. Dashes are em-dashes (Unicode U+2014 "—"), not hyphens.

ClassificationCaCO₃ range (mg/l)label (verbatim)
"Soft"< 60"Soft water"
"Moderately Soft"60 – 119"Moderately Soft water"
"Slightly Hard"120 – 179"Slightly Hard water"
"Moderately Hard"180 – 200"Moderately Hard water"
"Moderately Hard"201 – 239"Moderately Hard water — significant limescale expected"
"Hard"240 – 299"Hard water — significant limescale expected"
"Very Hard"≥ 300"Very Hard water — significant limescale expected"
(no data)nullnull

Note on "Moderately Hard": this classification spans CaCO₃ 180–239 mg/l, which straddles the 200 mg/l limescale threshold. It therefore produces two different label variants — one below 200 (no suffix) and one above 200 (with the limescale suffix). All other classifications produce a single deterministic label string.

Safety: nutrients & lead

BlockUnitLegal limitDescription
parameters.nitratemg/l50Nitrate — value/min/max/samples + status
parameters.nitritemg/l0.5Nitrite (where reported)
parameters.lead_meanµg/l10Lead — annual mean, with min/max range
parameters.lead_maxµg/l10Lead — peak reading (kept for backwards compatibility)

Heavy metals

BlockUnitLegal limitDescription
parameters.arsenicµg/l10Arsenic — most companies report max only
parameters.coppermg/l2Copper
parameters.ironµg/l200Iron
parameters.nickelµg/l20Nickel
parameters.bromateµg/l10Bromate (disinfection by-product)
parameters.manganeseµg/l50Manganese (where reported)
parameters.aluminiumµg/l200Aluminium (where reported)
parameters.sodiummg/l200Sodium (where reported)

Each block returns { value, min, max, samples, unit, legal_limit, status }.

Disinfection

BlockUnitLegal limitDescription
parameters.chlorine_meanmg/lFree / total chlorine residual (no UK PCV)
parameters.thm_meanµg/l100Total trihalomethanes (chlorination by-product)

Other physical / chemical

BlockUnitLegal limitDescription
parameters.fluoridemg/l1.5Fluoride. added: true if >0.3 mg/l (suggests artificial fluoridation)
parameters.phpH6.5–9.5Returns legal_range: "6.5-9.5" instead of legal_limit
parameters.turbidityNTU4Turbidity (where reported)
parameters.colourmg/l Pt/Co20Apparent colour (where reported)
parameters.conductivityµS/cm2500Electrical conductivity (where reported)

Pesticides

BlockUnitLegal limitDescription
parameters.pesticides_totalµg/l0.5Sum of all pesticides detected — annual mean / min / max

Roadmap: per-pesticide concentrations (atrazine, MCPA, mecoprop, glyphosate, simazine, etc.) are a planned future enhancement. About half of UK water companies publish individual pesticide breakdowns in their zone reports. Subscribe at hello@mytapwater.co.uk if this is on your roadmap.

Microbiological — coliform & ecoli

FieldTypeDescription
samplesintegerTotal samples tested in the year
failuresintegerSamples failing the <1 /100ml threshold
compliance_ratenumberPercentage of compliant samples (0–100)
unitstringAlways "/100ml"
legal_limitintegerAlways 0 (must be absent)
statusstring"pass", "fail", or "not_reported"

Compliance — compliance

FieldTypeDescription
pfas_noticebooleanTrue if any active DWI improvement notice is in force at the water-company level. Note: this flag is set when any active notice exists — see meta.enforcement_flags.pfas for the PFAS-specific flag.
pfas_completion_targetstring|nullISO date of the earliest active notice's completion target, if any
active_noticesintegerCount of active DWI notices — company-wide, not zone-specific
overall_compliancestring"compliant" if no active notices, otherwise "improvement_required"

Recommendations Paid tiers only

FieldTypeDescription
recommendations.filter_typestringOne of: "carbon_block", "reverse_osmosis", "water_softener"
recommendations.reasonstringPlain-English justification for the suggested filter
recommendations.hardness_treatmentstringOne of: "softener_required", "softener_recommended", "not_required"

Metadata — meta

FieldTypeDescription
data_yearintegerYear of the underlying DWI compliance report
last_updatedstringISO date when MyTapWater last refreshed this zone
sourcestringCitation: "DWI Annual Compliance Reports 2025"
api_versionstringAPI schema version (e.g. "1.1")
zone_populationinteger|nullPopulation served by the supply zone (where reported)
reporting_periodstring|nullFree-text period the data covers (e.g. "1 Jan 2025 to 31 Dec 2025")
dwi_assessmentstring|nullDWI's own free-text assessment for the zone, where available
enforcement_flags.pfasbooleanActive PFAS-specific notice in force for the supplying company
enforcement_flags.leadbooleanActive lead-specific notice in force
enforcement_flags.microbiologicalbooleanActive bacteriological/coliform notice in force
enforcement_flags.otherbooleanActive notice for any other parameter

Notes & conventions

Score methodology

quality.score is a weighted composite of four sub-scores: Safety 50% (nitrate, lead, THMs, arsenic, bromate), Purity 25% (chlorine residual, turbidity, aluminium), Minerals 10% (hardness + pH), Compliance 15% (tiered by enforcement severity).
Hard caps apply when enforcement is active: a PFAS notice caps the score at 59 ("Fair"), a lead or microbiological notice caps it at 72.

Hardness classification — six classes

parameters.hardness.classification returns one of six strings: "Soft" (<60 mg/l), "Moderately Soft" (60–119), "Slightly Hard" (120–179), "Moderately Hard" (180–239), "Hard" (240–299), "Very Hard" (≥300).

Status field meanings

Every measurement block returns a status string: "pass" (within legal limit), "fail" (exceeds legal limit), "measured" (reported but no UK regulatory limit applies — e.g. chlorine, hardness), "not_reported" (this water company doesn't publish this parameter). For the compliance.overall_compliance field the values are "compliant" or "improvement_required".

Data nature

All measurements are zone-level annual means / ranges from official UK Drinking Water Inspectorate (DWI) compliance reports. They are NOT real-time readings and NOT property-specific. Lead values in particular reflect the water leaving the treatment works and reaching the zone — lead picked up from individual property plumbing is not represented.

legal_limit values

All legal_limit values are the UK Prescribed Concentration Values (PCVs) from the Water Supply (Water Quality) Regulations 2018 / DWI guidance. null indicates no UK regulatory limit applies to that parameter (e.g. chlorine residual).

compliance.active_notices scope

This integer is the count of company-wide DWI improvement notices, not the number specific to your zone. Most active notices affect the entire supplier's operating area. For the PFAS-specific signal use meta.enforcement_flags.pfas.

Example response (fully populated)

GET /lookup?postcode=NW3 3SU
{
  "postcode": "NW3 3SU",
  "district": "NW3",
  "zone": { "name": "Kentish Town", "code": "0374",
            "water_company": "Thames Water", "company_id": "thames-water" },
  "quality": { "score": 47, "score_label": "Fair",
               "score_description": "Water has some concerns worth addressing" },
  "parameters": {
    "hardness":  { "value": 263, "min": null, "max": null, "unit": "mg/l CaCO3",
                   "classification": "Hard", "label": "Hard water — significant limescale expected",
                   "value_clarke": 18.4, "value_german": 14.7, "value_french": 26.3,
                   "value_mmol": 2.63, "calcium": 105.1, "magnesium": null,
                   "status": "measured" },
    "nitrate":   { "value": 28.4, "min": 23.2, "max": 34.1, "samples": 36,
                   "unit": "mg/l", "legal_limit": 50, "status": "pass" },
    "nitrite":   { "value": null, "unit": "mg/l", "legal_limit": 0.5, "status": "not_reported" },
    "lead_mean": { "value": 1.0, "min": 0.9, "max": 1.5, "unit": "µg/l",
                   "legal_limit": 10, "status": "pass" },
    "lead_max":  { "value": 1.5, "unit": "µg/l", "legal_limit": 10, "status": "pass" },
    "arsenic":   { "value": null, "min": null, "max": 1.2, "samples": null,
                   "unit": "µg/l", "legal_limit": 10, "status": "pass" },
    "copper":    { "value": null, "min": null, "max": 0.16, "samples": null,
                   "unit": "mg/l", "legal_limit": 2, "status": "pass" },
    "iron":      { "value": null, "min": null, "max": 21, "samples": null,
                   "unit": "µg/l", "legal_limit": 200, "status": "pass" },
    "nickel":    { "value": null, "min": null, "max": 1.5, "samples": null,
                   "unit": "µg/l", "legal_limit": 20, "status": "pass" },
    "bromate":   { "value": null, "min": null, "max": 5.5, "samples": null,
                   "unit": "µg/l", "legal_limit": 10, "status": "pass" },
    "manganese": { "value": null, "min": null, "max": null, "samples": null,
                   "unit": "µg/l", "legal_limit": 50, "status": "not_reported" },
    "aluminium": { "value": null, "min": null, "max": null, "samples": null,
                   "unit": "µg/l", "legal_limit": 200, "status": "not_reported" },
    "sodium":    { "value": null, "min": null, "max": null, "samples": null,
                   "unit": "mg/l", "legal_limit": 200, "status": "not_reported" },
    "chlorine_mean": { "value": 0.71, "min": null, "max": 0.98, "unit": "mg/l",
                       "legal_limit": null, "status": "measured" },
    "thm_mean":  { "value": 14, "min": 12, "max": 16, "unit": "µg/l",
                   "legal_limit": 100, "status": "pass" },
    "fluoride":  { "value": 0.13, "min": 0.12, "max": 0.16, "unit": "mg/l",
                   "added": false, "legal_limit": 1.5, "status": "pass" },
    "ph":        { "value": 7.84, "min": 7.6, "max": 8.1, "unit": "pH",
                   "legal_range": "6.5-9.5", "status": "pass" },
    "turbidity":    { "value": null, ..., "legal_limit": 4,    "status": "not_reported" },
    "colour":       { "value": null, ..., "legal_limit": 20,   "status": "not_reported" },
    "conductivity": { "value": null, ..., "legal_limit": 2500, "status": "not_reported" },
    "pesticides_total": { "value": 0.006, "min": 0, "max": 0.03,
                          "unit": "µg/l", "legal_limit": 0.5, "status": "pass" },
    "coliform":  { "value": null, "samples": 96, "failures": 0,
                   "compliance_rate": 100, "unit": "/100ml",
                   "legal_limit": 0, "status": "pass" },
    "ecoli":     { "value": null, "samples": 96, "failures": 0,
                   "compliance_rate": 100, "unit": "/100ml",
                   "legal_limit": 0, "status": "pass" }
  },
  "compliance": { "pfas_notice": true, "pfas_completion_target": null,
                  "active_notices": 30, "overall_compliance": "improvement_required" },
  "recommendations": { "filter_type": "reverse_osmosis",
                       "reason": "PFAS enforcement notice active — only RO removes PFAS",
                       "hardness_treatment": "softener_recommended" },
  "meta": { "data_year": 2025, "last_updated": "2026-03-01",
            "source": "DWI Annual Compliance Reports 2025", "api_version": "1.1",
            "zone_population": 35200, "reporting_period": "1 Jan 2025 to 31 Dec 2025",
            "dwi_assessment": "Excellent quality water with no infringements...",
            "enforcement_flags": { "pfas": true, "lead": true,
                                   "microbiological": true, "other": true } }
}

Null handling example

When a water company doesn't publish a parameter, the field is still present — with value: null and status: "not_reported". Example for a postcode in a region where the supplier doesn't test for manganese:

"manganese": {
  "value":       null,
  "min":         null,
  "max":         null,
  "samples":     null,
  "unit":        "µg/l",
  "legal_limit": 50,
  "status":      "not_reported"
}

Error Codes

CodeMeaning
200Success
401Invalid or missing API key
404Postcode not found in any supply zone
429Monthly rate limit exceeded
500Server error

Code Examples

JavaScript Python PHP
const response = await fetch(
  'https://api.mytapwater.co.uk/lookup?postcode=SW1A1AA',
  { headers: { 'X-API-Key': 'your_api_key_here' } }
);
const data = await response.json();
console.log(data.parameters.hardness.classification); // "Very Hard"
console.log(data.compliance.pfas_notice);               // true/false
console.log(data.quality.score);                        // 0–100
import requests

response = requests.get(
    'https://api.mytapwater.co.uk/lookup',
    params={'postcode': 'SW1A1AA'},
    headers={'X-API-Key': 'your_api_key_here'}
)
data = response.json()
print(data['parameters']['hardness']['classification'])  # Very Hard
print(data['compliance']['pfas_notice'])                   # True/False
$response = file_get_contents(
    'https://api.mytapwater.co.uk/lookup?postcode=SW1A1AA',
    false,
    stream_context_create(['http' => ['header' => 'X-API-Key: your_api_key_here']])
);
$data = json_decode($response, true);
echo $data['parameters']['hardness']['classification']; // Very Hard
Demo Key — try it now
mtw_live_demo00000000000000000000000000
This key is public. 100 calls/month shared across all demo users. Get your own free key →

Get Your Free API Key

Your key will be emailed instantly. No credit card required.

Your key will be emailed instantly. No credit card required.

Common questions

Is the API really free?
Yes. The free tier gives 100 calls per month with no credit card required. Perfect for testing and small projects. For higher volumes, see the Growth and Enterprise tiers above.
What data does the API return?
Hardness, nitrates, lead, chlorine, fluoride, pH, THMs, PFAS enforcement status, DWI compliance notices, overall quality score, and personalised filter recommendations — 30+ parameters per postcode, covering all 21 UK water companies.
How accurate is the data?
Data comes directly from official annual compliance reports submitted to the Drinking Water Inspectorate (DWI) by all 21 UK water companies, and from the DWI's public enforcement register. Updated annually when new reports are published.
Can I use this for commercial projects?
Yes. The API is available for commercial use on paid tiers. Email hello@mytapwater.co.uk for enterprise licensing and bulk data agreements.
How fast is the API?
Average response time is under 200ms. The API runs on Cloudflare Workers — a global edge network with data centres in 300+ cities. Requests are routed to the nearest edge node automatically.