SwimSignal

Data files

The files the site is built from are public: every spot's forecast, the storm overflows on the map and the accuracy scores. This page lists each one, what it holds and the licence it is under.

In short

The files

FileWhat it holdsSize
spots.jsonEvery spot's five-day forecast and what it rests on932 kB
alerts.jsonEach spot's level and headline, as the alerts send them39 kB
overflows.geojsonEvery storm overflow on the map, and what it is doing now6.3 MB
verification.jsonThe scores on the Accuracy page74 kB
verification_live.csvThe individual forecast and observation pairs behind the live spill scores2.4 MB
upstream/Every monitored storm overflow upstream of each spot, one file a spot, for the organisers' page673 kB
anypoint/The river network, upstream overflows and spill probabilities for forecasts at a clicked point13.7 MB
coastal.jsonOfficial English coastal and estuary directory, historical ratings and dated advice225 kB
places.jsonThe towns, villages and districts Plan a swim can start from771 kB
wales.jsonWelsh bathing waters from Natural Resources Wales: ratings and latest samples, dated59 kB
scotland.jsonScottish bathing waters from SEPA: names, latest ratings and links to SEPA's pages22 kB
ireland.jsonBathing waters in the Republic of Ireland from the EPA: ratings, latest samples and restrictions in force, dated112 kB
northern_ireland.jsonNorthern Ireland's bathing waters from DAERA: indicators with their sampling times and profile links13 kB

Sizes are this build's, before compression. The site's host, GitHub Pages, sends the files compressed.

coastal.json

An official-information snapshot, separate from SwimSignal’s inland forecasts. fetched_at, status, source, licence and credit describe the fetch. catalogue_state distinguishes live names/ratings from a dated fallback, whose source age is in catalogue_fetched_at. The fallback never contains current advice. Each entry in sites has an EA id, name, kind (coast or estuary), official profile, historical rating with its year and source, and advice.

Each site also has sample: its latest statutory sample in the EA Water Quality Archive, with taken_at (UK time with offset), ecoli and, from the same sample, enterococci (intestinal enterococci; null when the archive has none), each as value per 100 ml and qualifier (=, or < for below the detection limit), and the archive's point. Its state is ok, none (no sample since samples.since), unmapped (no archive point matched with confidence) or unavailable (the archive did not answer). The top-level samples gives the source, when it was asked (fetched_at), the window start and whether the answer is cached. The archive can lag the official profile by several days, and one sample is not a rating or today's water.

Advice states distinguish increased risk, no increased risk forecast, no forecast, no current advice and unavailability. Valid advice retains its origin, prediction time, publication time, expiry time and source. An increased-risk record in force counts whatever its origin, and stands over a normal one in force at the same time: the EA's notice after a pollution incident has no origin (null). Check expiry again when using the file: a snapshot can outlive its advice. Missing advice, a normal incident record and a historical rating do not establish clean water.

Licence. Environment Agency copyright and/or database right, Open Government Licence v3.0. Keep the credit and source links. The EA does not endorse SwimSignal.

wales.json

Natural Resources Wales's designated bathing waters, separate from SwimSignal's forecasts: SwimSignal makes no Welsh forecast. state is live when NRW's service answered the build, cached when the build used the dated snapshot kept in the repository (NRW's service refused GitHub's runners on 3 October 2026), or unavailable; fetched_at is when those sites and samples were read. Each entry in sites has NRW's id, name, kind (coast, estuary, lake or river), official profile, latest rating with its year (null for a water not yet rated) and sample: the latest in-season sample, with taken_at, ecoli and enterococci as value per 100 ml and qualifier. The file holds no pollution-risk forecasts: the coverage page asks NRW for those in the reader's browser, and none is kept. One sample is not a rating or today's water.

Licence. Contains Natural Resources Wales information © Natural Resources Wales and Database Right. All rights reserved. Open Government Licence v3.0. NRW does not endorse SwimSignal.

scotland.json

SEPA's designated bathing waters, separate from SwimSignal's forecasts: SwimSignal makes no Scottish forecast. state is live or unavailable (with error); fetched_at is when SEPA's layer was read. Each entry in sites has an id made from SEPA's location code, name, SEPA's page for it, its point (lat, lon) and its latest rating with the year, or null with unrated_year when SEPA lists it as unclassified. It holds no samples, signs or advice: those are on SEPA's page for each water, under SEPA's own conditions.

Licence. Contains public sector information licensed under the Open Government Licence v2.0: SEPA bathing water points. SEPA does not endorse SwimSignal.

ireland.json

The bathing waters the Environmental Protection Agency (EPA) lists for the Republic of Ireland, separate from SwimSignal's forecasts: SwimSignal makes no forecast in Ireland. state is live when every part was current for this build, partial when a part was kept from an earlier build or could not be read, or unavailable (with error). locations, samples and alerts each give their own state (ok, kept or unavailable) and when they were read (fetched_at). The list of waters is read once a day and the samples at most every three hours; restrictions are read on every build and never kept.

Each entry in sites has the EPA's id, name (as the EPA gives it, Irish accents kept), county, designated (false for a water the council monitors that is not a designated bathing water), its EPA page on beaches.ie, its latest rating with the year (null when none is listed, with unrated_year when the EPA lists it as not classified), season_restriction (a restriction for the whole season), alerts (restrictions in force: type in the EPA's words, since, cause and notice; null when the list could not be read) and sample: the latest sample this year, with date, season (in, 22 May to 15 September, or out, voluntary and not used for a rating), ecoli and enterococci as value per 100 ml and qualifier, and status, the EPA's word for that sample. A sample's state is ok, none (none listed for samples.season) or unavailable. One sample is not a rating or today's water.

Licence. Contains bathing water data from the Environmental Protection Agency (Ireland), Bathing Water Open Data API, licensed under CC BY 4.0. SwimSignal shows some of the fields and picks each water's latest sample. The EPA does not endorse SwimSignal.

northern_ireland.json

The bathing waters of the Department of Agriculture, Environment and Rural Affairs (DAERA) in Northern Ireland, separate from SwimSignal's forecasts: SwimSignal makes no forecast in Northern Ireland. state is live, kept (DAERA did not answer, so the copy from an earlier build, at most three days old, with fetched_at its time) or unavailable (with error). Each entry in sites has an id made from DAERA's site code, name, kind (coast or inland), candidate, inactive, DAERA's profile of the water, the indicator (DAERA's code and its words for it) and sampled_at, the sampling time DAERA gives with the indicator (UK time with its offset). It holds no rain figures and no samples.

Licence. Contains public sector information licensed under the Open Government Licence v3.0: DAERA bathing water monitoring points. DAERA does not endorse SwimSignal.

spots.json

Every spot's forecast, as the map and the spot pages show it. A spot's level (low, moderate, high or very high) is worked out from these fields by the page's own rules, levels.js: the worst of the spill forecast, the E. coli estimate from May to September, a "poor" rating and an algae check from the last 14 days. A day's label is the spill forecast's level alone.

generated_at
When the forecast was issued, UK time with its offset (ISO 8601)
build
The build's own checks: how many spots got a forecast, how many rows each water company's live feed returned, and any warnings
lead_skill
How good a forecast for each of the five days has been, as a share of the same-day forecast's skill: spill and water, five numbers each
credits
The data credits (below)
n, version
How many spots the file holds, and the version number of SwimSignal's code
spots
One entry for each spot, with the fields that follow
id, name, kind, river, source, lat, lon
From the spot list: its id (its page is spot/<id>/), its name, river or lake, the river's name, its source (designated for an Environment Agency bathing water, openstreetmap for a mapped swim spot), and where it is, in degrees (WGS84). Inclusion does not confirm permission or safe access
notes, query
The spot list's note on the spot, and the point and time the forecast was made for (lat, lon, issued_at)
assumptions, ecoli_scope
The model's settings for this forecast (flow speeds, die-off, how far upstream it looks, the models' versions), and where the E. coli estimate has been tested: which kind of water and which months
days
One entry for each day: date; risk, the exposure index as a fraction (0.4 is 40 on the site's 0–100 scale), and label, its level; expected_spilling_overflows, how many of the overflows upstream are expected to spill; p_ecoli_gt900, the E. coli estimate, the chance a sample would show over 900 E. coli per 100 ml; in_validated_season, false from October to April, when that estimate is untested; rain_48h_mm, the rain at the spot in the 48 hours to midday, and rain_48h_coverage, the share of those hours with rain data; data_status, whether the rain forecast was complete, and rain_missing_share, the share of the day's spill forecast that rests on days without rain data
now
Right now, from the live feeds: risk and label, as for a day; how many overflows upstream are discharging (discharging_upstream), stopped lately and still counted (recent_upstream: a spill counts until assumptions.recent_spill_hours after its water has passed the spot) and report live (monitored_upstream: discharging, stopped lately, or not discharging on a current feed), and any company feed that is down (feed_down) or has not updated within 6 hours (feed_stale, with its last update, and stale_upstream, how many of its overflows here), and how many do not report because their company has no live feed (no_feed_upstream). While right now's risk is moderate or worse, or an overflow upstream is discharging, where its risk comes from (source) and, when spills whose water has not reached the spot yet hold at least half of it, the largest of them (arriving, with arrives_at, when its water should get there). Where right now's risk is moderate or worse, when it should be back to low risk if no new spill starts (clears_at, and what sets that time, clears_by)
upstream_summary
How many monitored overflows can reach the spot (overflows) and how many of them report live
contributors
Up to 10 of those overflows, the ones that matter most to the spot: the company's id and name for it, its distance and travel time, weight, the chance that a spill there affects the spot after travel time, die-off and dilution, and its chance of spilling on each day (p_spill_days); its status and data_state as in overflows.geojson, and feed_updated_at, when its company's feed last updated (UTC)
location
Where the spot sits on the river network: the watercourse, whether it is treated as a river or a lake, and how far it was moved to reach the network
classification, algae
At bathing waters: the Environment Agency's rating, with earlier years and a link to its page, and its sampler's latest algae check
lab_sample
At bathing waters: the Environment Agency's latest E. coli lab sample that SwimSignal holds, when it was taken (UK time with its offset, taken_at), the count per 100 ml (ecoli) and, where the count is a limit, qualifier (< or >). Null where there is no sample this season. The EA publishes samples days after they are taken
river_state
The nearest Environment Agency level gauge: its latest reading, its usual range and how old the reading is. Not part of the level
flow_state, flood_alerts
Separate from pollution risk: high or rising fast when a recent gauge on the spot's river meets those thresholds, otherwise null; and nearby Environment Agency flood alerts and warnings. An empty alert list means none were found; null means the check failed. These do not change the pollution level
water_temp
Where there is a recent Environment Agency sensor on the same river: the temperature in Celsius (temp_c), its time, age and quality, the station and its link, and its distance and direction from the spot. This is a reading at that sensor, not an estimate of the temperature at the swim spot. Absent where no suitable sensor was found
weather
Each day's high, sunrise and sunset, from Open-Meteo
error
Instead of a forecast, where there is none: the reason

Licence. SwimSignal's forecasts, made from all the sources the credits list. The ratings, algae checks, river levels, flood alerts and water temperatures are the Environment Agency's (OGL v3.0); the rainfall figures stay under CC BY-SA 4.0. OpenStreetMap spot records are kept in a separate source file under ODbL 1.0.

alerts.json

Each spot's level and headline, worked out by the same rules as the page, for the alert service. The smallest file, for anyone who wants only the answer.

generated_at, credits
As in spots.json
spots
Keyed by spot id: name; level, one of low, moderate, high and very high, or "no overflows", "not covered" or "no forecast"; rank, 0 for low to 3 for very high and −1 without a level; headline, as the list shows it ("High risk today: sewage spills"); action, what to do at that level, the line under the answer on the spot's page; url, the spot's page; best, only where the level changes from day to day: the lowest level of the four days after today, the first date with it, and the words the weekly note uses ("Saturday, low risk")

Licence. SwimSignal's levels and headlines, made from the same sources as spots.json.

overflows.geojson

Every storm overflow on the map, as a GeoJSON point (longitude, latitude), with what its water company's live feed last said about it. The largest file.

site_id, company, site_name
The water company's id and name for the overflow
receiving_watercourse
The river or stream it discharges into, as the company names it
status
1 discharging; 0 not discharging; −1 monitor offline; −2 no live feed: the overflow is in the Environment Agency's annual returns but in no live feed SwimSignal reads, as all of Dŵr Cymru Welsh Water's are; −3 unknown, because the company's feed returned nothing at the last poll (the spill times are from the poll before)
has_live
Whether the overflow is in its company's live feed
data_state
Whether its status is current: live, a status from a feed that updated in the 6 hours before the poll; stale, a status from a feed that did not, or gives no time, so "not discharging" may be out of date; offline, no status (−1, −3, or −2 at a company that has a live feed); no_feed, its company has no live feed. On a stale one, feed_updated_at is when its feed last updated (UTC), null if the feed gives no time
latest_event_start, latest_event_end
Its latest spill, as the company's feed gives it (UTC)
lta_spills
Its long-term average number of spills a year, from the Environment Agency's annual returns

Licence. The water companies' data, CC BY 4.0, via the National Storm Overflow Hub, with the Environment Agency's annual returns (OGL v3.0), combined by SwimSignal.

verification.json

Everything on the Accuracy page, which explains each table: live, the scores of the forecasts this site has issued, against what the overflows then did and, for the E. coli estimate, against the Environment Agency's samples (live.ecoli_live); holdout, leads and reliability, the test on a held-out year, 2025; lead_calibration; ecoli, ecoli_combined and ecoli_model, the E. coli estimate's tests; sampling_plan; and credits.

Licence. SwimSignal's scores, made against the water companies' discharge records (CC BY 4.0) and the Environment Agency's samples (OGL v3.0).

verification_live.csv

One row for each scored overflow, day and forecast lead, so the live spill scores can be checked independently. It is published only when its row count matches live.n_scored in verification.json. Read it as CSV after skipping lines beginning with #, which hold its credits.

overflow_id, company, day
The overflow, its water company and the day being forecast
lead, issued_at
Days ahead (0 is the same day), and when the scored forecast was issued
forecast_raw, forecast_calibrated, climatology
Spill probabilities, from 0 to 1: before and after lead calibration, and the historical baseline used for comparison
observed
1 if a spill was observed that day, 0 if none was observed; days without sufficient observations are excluded

Licence. SwimSignal's forecast and scoring rows, with the water companies' discharge records (CC BY 4.0).

upstream/

One file a spot, upstream/<id>.json, with every monitored storm overflow within 60 km upstream along the river network, most reach first. spots.json keeps only the ten that matter most in the current forecast; the organisers' page loads a spot's file when it has more. A spot with nothing upstream has no file. Each file has the spot's id, generated_at, credits and overflows, one entry an overflow:

site_id, site_name, company, receiving_watercourse
The water company's id and name for the overflow, the company, and the water it discharges into, as the company names it
distance_km, lake_distance_km, travel_h
How far upstream along the river network, the part of that across a lake, and how long sewage takes to arrive at the model's speeds
weight
Reach, 0 to 1: the chance a spill there affects the spot, from die-off over the travel time and dilution
lta_spills, spill_hours
Its long-term average spills a year, and the hours it spilled in its latest annual return (Environment Agency)
has_live, lat, lon
Whether its water company reports its status live, and where it is

Licence. SwimSignal's upstream calculations, with the overflow records and river network under the licences in the credits.

anypoint/

The files the map uses to calculate a forecast at a point you click, on your device. The table links tiles.json, the index of available 0.25-degree squares, versions, distance units and probability scales; its size is the whole folder's. These files are intended to be read together by the site's point-forecast script.

overflow_ids.json
The ordered list of overflow ids, names and positions. Other files refer to an overflow by its position in this list; their ids version must match
overflow_days.json
Spill probabilities by day and overflow, in thousandths, and the hours since each overflow's latest spill ended (ended_h), from which the page works out right now's weights with the travel time to the point. Null probabilities mean missing rain data. Also the live statuses, companies, which company feeds are stale (feed_stale_since) and which companies have none (no_feed), rain forecast ages and modelling assumptions
link_index/<tile>.bin
Packed river lines used to find the nearest watercourse. The binary format is defined by pack_link_index in the build script
links/<tile>.json
River-link lengths and upstream overflows, with the distances and dilution factors needed to trace a click along each link
lakes.json
Mapped lake outlines, inlets and the upstream overflows that can reach them
outside_england.json
Wales, Scotland and the island of Ireland, widened over their tidal rivers, estuaries and sea loughs, as one GeoJSON geometry in latitude and longitude. A click inside it gets no forecast, because the overflow data covers England only. parts says, for each polygon, whether it is Wales and Scotland (wales_scotland) or the island of Ireland (ireland); on the island, northern_ireland marks Northern Ireland, and the rest is the Republic, so the page can name the official source there. Made from the Office for National Statistics' country boundaries and Tailte Éireann's provinces of the Republic by make_outside_england.py; made says how, and credit carries the notices

Rain forecasts are refreshed in batches. rain_h gives the age of the forecast used for each overflow; cached forecasts are used for up to 24 hours. A clicked-point forecast has no local river gauge or bathing-water sample attached to it: it is a sewage-spill forecast, and cannot confirm that swimming is safe.

Licence. SwimSignal's forecasts and upstream calculations, with the source river network, lake outlines, overflow records and rainfall under the licences in the credits. outside_england.json: Source: Office for National Statistics licensed under the Open Government Licence v.3.0. Contains OS data © Crown copyright and database right 2026. © Tailte Éireann, Provinces - National Statutory Boundaries - 2019 - Generalised 20m, CC BY 4.0; joined, widened and simplified by SwimSignal.

places.json

The places Plan a swim can start from: every city, town, suburban area, village and other settlement in England and Wales in Ordnance Survey's OS Open Names, about 26,000 names. places lists each as [name, latitude, longitude], to two decimal places (about a kilometre), cities first and villages last. A name that several places share keeps it bare for the biggest, and the others carry their district or county after a comma. The page fetches the file the first time a place is typed, and looks the name up on the device. The file changes only when make_places.py is run again.

Licence. Contains OS data © Crown copyright and database right 2026, Open Government Licence v3.0. The file's credit field carries the notice.

The credits field

The JSON files carry credits: attribution, the notices each provider asks for; modified, which says that SwimSignal combined and modelled the data and that no provider endorses it; licences, each licence's name and address; and full, the address of the full credits on the terms page. Individual river-link tiles carry the full credits' address; their binary index uses tiles.json's credits. The CSV's comment header gives the same notices. The MIT licence of the source code covers the code, not the data.

Using the files