Methodology
How Bay Area Swim decides whether a beach is safe, caution, unsafe, or inconclusive — and what that label does and does not mean.
The four labels
- Safe — Most recent sample is within EPA recreational thresholds AND no meaningful recent rainfall AND no advisory in effect.
- Caution — Some risk signal: meaningful recent rainfall (more than 0.5 in / 72 h), an active HAB Caution Advisory, or a borderline reading.
- Unsafe — Bacteria reading from the most recent sample exceeded the EPA single-sample or 30-day geomean limit, OR an active HAB Danger Advisory, OR more than 1.0 in of rain in 48 h at an enclosed / storm-drain-adjacent beach.
- Inconclusive — No recent data, or the most recent sample is more than 30 days old. Tells you "we don't know" rather than asserting a stale reading is still accurate.
How we compute the score
The risk macro evaluates these rules in priority order. Whichever rule fires first determines the label.
- HAB Danger Advisory in effect → unsafe (overrides everything; this is a real-time agency advisory, not a sample-based signal).
- No data at all (no bacteria, no rain, no advisory) → inconclusive.
- Last bacterial sample > 30 days old → inconclusive. The water has had a month of mixing, runoff, sun, and microbial die-off; the reading is no longer load-bearing.
- Off-season + sample > 14 days old → inconclusive. Some agencies stop sampling Nov–Mar; we don't pretend their last summer reading still applies.
- Bacterial exceedance on the most recent sample → unsafe.
- Heavy 48 h rain (more than 1.0 in / 25.4 mm) at an enclosed or storm-drain-adjacent beach → unsafe.
- HAB Caution Advisory in effect → at least caution.
- Meaningful 72 h rain (more than 0.5 in / 12.7 mm) → caution.
- Otherwise → safe.
EPA bacterial thresholds we use
- Enterococcus — single sample limit 104 MPN/100 mL; 30-day geomean limit 35.
- E. coli — single sample limit 235; geomean limit 100.
- Fecal coliform — single sample limit 400.
- Total coliform — no single recreational threshold per EPA; we plot it on the chart when nothing else is available, but it does not feed the safe/caution/unsafe label.
Source: U.S. EPA Recreational Water Quality Criteria, 2012.
The recent rainfall caveat
A "safe" label is based on the most recent sample. If that sample was taken before a recent storm, the water you see today may not match what was tested. When more than ~0.2 in of rain has fallen in the past 72 hours, every beach page surfaces a small "recent rainfall may have changed water quality" banner under the status. The label itself doesn't change — sample-based scoring stays sample-based — but you can factor the storm in yourself.
What we deliberately don't model
- Water temperature, wave height, rip currents — we ingest NOAA buoy data but it doesn't feed the safe/caution/unsafe label. Cold water and surf are real swim hazards but they're outside the bacterial-quality scope of this site.
- Sharks, jellyfish, marine mammals.
- Drowning hazards, tides, undertow. Always check current conditions and your own ability before entering the water.
- Personal vulnerability. Children, immunocompromised people, and those with open wounds tolerate exposure differently from healthy adults; the EPA thresholds we use assume a typical recreational exposure.
Limitations
- Sample lag. Bacterial samples take 18–24 hours to incubate before results post. By the time you read a "safe" label, the underlying sample is at least a day old, often a week.
- Sampling cadence. Most agencies sample weekly during the bathing season (April–October). EBRPD posts PDFs once a week. Berkeley's Aquatic Park PDF is updated irregularly. The /status page shows actual cadence per source.
- Indicator switching. Some stations report Enterococcus in summer and switch to fecal/total coliform off-season — the chart auto-picks whichever indicator has data and labels its y-axis accordingly.
- Geographic gaps. Some swim spots (particularly inland creeks and some North Bay coves) aren't in any monitoring program. They will show as inconclusive.
- Right-censored readings. The IDEXX assay caps at 24,196 MPN/100 mL. Anything above that is recorded as exactly 24,196 in our data — the true value could be higher.
FAQ
How fresh is this data?
Most sources update weekly. The pipeline checks every source daily and re-publishes the snapshot whenever any source has changed. The /status page shows the exact last-update time for each source.
Why does this beach show "Inconclusive"?
Either no recent monitoring data exists for it, or the most recent sample is more than 30 days old. A 60-day-old "exceeded" reading is no longer a useful indicator of swimmer safety, so we degrade to inconclusive rather than confidently flag unsafe.
What does "Caution" actually mean?
A real but secondary risk signal — typically meaningful recent rainfall (above the 0.5 in / 72 h threshold) without a confirmed bacterial exceedance, or a HAB (harmful algal bloom) Caution Advisory from the local agency. Use your own judgment; pay attention to the explanation below the status badge.
Why does it say "Safe" even though it rained yesterday?
When recent rain (more than ~0.2 in in the last 72 hours) has occurred but the official sample predates the storm, we keep the safe label but show a rainfall caveat directly under the status. The sample-based status is unchanged; the caveat lets you weigh the staleness yourself.
Can I trust this for medical decisions?
No. This site aggregates public monitoring data with our own risk thresholds; we are not a public health agency, do not collect samples, and do not make medical claims. When in doubt, check the agency's official advisory page (linked from every beach detail page) or call the local park / beach office.
How do I report a discrepancy or a wrong location?
Open an issue on GitHub at github.com/patrickbeekman/bay-area-swim. Pull requests welcome — every data source and the risk macro are in the open repo.
Open source
Every source fetcher, dbt model, and risk threshold is in
github.com/patrickbeekman/bay-area-swim.
The risk-scoring rules above live in dbt_project/macros/compute_risk_status.sql; if you
think the logic is wrong, open an issue or a PR.
Information provided for general guidance only. Conditions change rapidly. Swim at your own risk. Not medical advice.