voda.earth
Open the map

Open data API

Hourly counts of what people report about their tap water supply, by area. Version 1.0.0, CC BY 4.0.

Start

Voda is a map where people report whether they have tap water right now. This API publishes those reports as counts per H3 area and UTC hour. Never a single report, a position, a device or an address.

An area-hour is published only when it holds at least 3 reports. The rest are counted in their region's n_suppressed. Counts are what people said, not measurements: see the methodology.

No key or account. A day is settled 48 hours after its end: its counts then rarely change, and it may be cached for a day. Cite as: Voda (voda.earth), CC BY 4.0, accessed YYYY-MM-DD.

Base URL https://app.voda.earth/api/data/v1. The contract: openapi.json (OpenAPI 3.1.0). How the counts come about: the data page.

Endpoints

GET /

The dataset: coverage, license, how to cite, and links. Includes a schema.org Dataset (JSON-LD) for data portals.

GET /openapi.json

This document.

GET /days/{day}.{format}

Every published area-hour of one UTC day. Ordered by hour, then area. Today holds the full hours so far.

ParameterInType
day requiredpathstring (date)
format requiredpathjson | geojson | csv
limitqueryinteger 1–5000 Default 5000.
cursorquerystringFrom the previous page's next. Opaque.

GET /observations

Area-hours of a time range in one place. Exactly one of cell, parent, bbox or place. At most 168 hours.

ParameterInType
from requiredquerystring (date-time)First hour, included. ISO 8601 UTC, a whole hour.
to requiredquerystring (date-time)Last hour, excluded. At most 168 hours after from.
cellquerystringOne area (res 9 or 10). A res-9 area also returns the small areas inside it.
parentquerystringEvery area of one res-5 region.
bboxquerystringwest,south,east,north in degrees. At most 8 res-5 regions.
placequerystringA place's id, as in /api/places: its box.
formatqueryjson | geojson | csv Default json.
limitqueryinteger 1–5000 Default 5000.
cursorquerystringFrom the previous page's next. Opaque.

GET /regions

Every region-hour of a time range, with what was suppressed.

ParameterInType
from requiredquerystring (date-time)First hour, included. ISO 8601 UTC, a whole hour.
to requiredquerystring (date-time)Last hour, excluded. At most 168 hours after from.
formatqueryjson | geojson | csv Default json.
limitqueryinteger 1–5000 Default 5000.
cursorquerystringFrom the previous page's next. Opaque.

Rows

Observation: one area, one hour

One area in one hour. Published only with at least 3 reports.

ColumnType
hour_utcstring (date-time)Start of the hour, ISO 8601 UTC. The hour runs to the next one.
h3_cellstringH3 index (https://h3geo.org).
h3_res9 | 1010: a small area (~135 m across). 9: a big area (~360 m), reports before 2026-09-27.
center_latnumberLatitude of the hexagon's centre, WGS 84, 5 decimals. Never a report's position.
center_lngnumberLongitude of the hexagon's centre, WGS 84, 5 decimals. Never a report's position.
n_reportsintegerReports in this hour: n_good + n_ok + n_low + n_none.
n_goodintegerWater, strong pressure.
n_okintegerWater, medium pressure.
n_lowintegerWater, weak pressure.
n_noneintegerNo water.
n_out_lt1hintegerOf n_none: without water for less than an hour.
n_out_h1_6integerOf n_none: for 1 to 6 hours.
n_out_h6_24integerOf n_none: for 6 to 24 hours.
n_out_gt24hintegerOf n_none: for more than 24 hours.

Region: one res-5 region, one hour

One region (H3 res 5, ~20 km across) in one hour: every report in it.

ColumnType
hour_utcstring (date-time)Start of the hour, ISO 8601 UTC. The hour runs to the next one.
h3_cellstringH3 index (https://h3geo.org).
h3_res5
center_latnumberLatitude of the hexagon's centre, WGS 84, 5 decimals. Never a report's position.
center_lngnumberLongitude of the hexagon's centre, WGS 84, 5 decimals. Never a report's position.
n_reportsintegerReports in this hour: n_good + n_ok + n_low + n_none.
n_goodintegerWater, strong pressure.
n_okintegerWater, medium pressure.
n_lowintegerWater, weak pressure.
n_noneintegerNo water.
n_suppressedintegerOf n_reports: those in area-hours with fewer than 3 reports, not published by area.

Formats and pages

JSON {"data": [...], "next": url | null}; GeoJSON, a FeatureCollection with one hexagon Polygon per row; CSV with a header line, CRLF. At most 5,000 rows a page: follow next, also sent as Link: <…>; rel="next".

Errors

RFC 9457 problem details, application/problem+json, with type, title, status, detail and a stable error code:

  • invalid: The request is not valid (400)
  • range_too_long: The time range is longer than 168 hours (400)
  • area_too_large: The area covers more than 8 regions (400)
  • bad_cursor: The cursor is not valid for this query (400)
  • not_found: Not found (404)
  • internal: Internal error (500)

Caching and versions

A day or range that ended more than 48 hours ago is settled: Cache-Control: public, max-age=86400. Its counts rarely change after that: only when a person withdraws an answer of a report still open, or a report that was not real is removed. Anything newer: 5 minutes. Every data answer has an ETag (marked weak, W/, when it comes compressed); send it back as If-None-Match for a 304. Any web page may read the API (Access-Control-Allow-Origin: *).

The version is in the path. Version 1 is frozen: a new column, a changed meaning or a changed threshold makes /v2, and /v1 keeps working. A version that will stop is announced at least six months ahead with the Deprecation and Sunset headers.

Cite as: Voda (voda.earth), CC BY 4.0, accessed YYYY-MM-DD.