London and Water · Developer reference
Query the map’s data.
The map uses a same-origin JSON endpoint: POST /v1/query, with
Content-Type: application/json. It reads already collected data. A browser
query never starts a provider download.
This describes the current map API. Earlier scientific GET endpoints are retired and return HTTP 410. Their identifiers are not substitutes for this endpoint’s identities.
Start with the catalogue
Request the catalogue of metric families and their measurement traces. Use each trace's
{metric,trace} identity for measurement queries. A trace is selectable when
its coverage.result_count is greater than zero: it has located, timed decimal
values or supported reported outcomes, including zero and false.
curl 'https://semantic.construction/v1/query' \
-H 'Content-Type: application/json' \
--data '{"operation":"catalogue"}'
The admitted population is the London 50 km circle plus nine explicitly included tide stations. Measurement filters narrow that population by dates, area, traces and places.
Available operations
| Operation | Request | Result |
|---|---|---|
catalogue |
No options | Metric families and their traces, each with coverage, dates and summaries. |
trace_search |
trace_search:{from,to} |
Trace identities with an actual located, displayable reading overlapping those dates. Search does not change measurement filters or catalogue coverage. |
map |
filter |
Supported timed-result counts and selected traces at each semantic place, with point bounds. |
timeline |
filter, then either timeline:{count_bins} or
timeline:{readings:{histories,from,to,window_only?}}
|
Explicit occupancy bins, or exact viewport readings. Full reading responses also contain period counts and summaries; window-only responses omit them. |
panel |
filter,
panel:{count_bins,limit,sort?,preferred?}
|
Eligible selected traces at explicit places, resolved locations, the active trace's overview and first grid block from one buffer publication. |
rows |
filter, rows:{limit,offset,sort?,after?} |
Compact measurement rows, including references for continuation and source forms. |
record |
record:{reference} |
One full native observation with source evidence and same-record supporting fields. |
The six browsing operations use one shared in-memory server buffer.
record is the deliberate individual PostgreSQL lookup for the full source
form. There is no automatic SQL fallback for browsing or CSV downloads.
Filter and inspect
Replace the placeholders below with a family and its trace ID from the catalogue. Dates
are UTC and use an inclusive start and exclusive end. For all dates use
"all_time":true instead of from and to.
{
"operation": "timeline",
"filter": {
"traces": [{"metric":"METRIC_ID_FROM_CATALOGUE","trace":"TRACE_ID_FROM_CATALOGUE"}],
"from": "2026-10-01T00:00:00Z",
"to": "2026-10-15T00:00:00Z"
},
"timeline": {"count_bins": 200}
}
An optional geographic box has the shape
"area":{"srid":4326,"min_x":-0.3,"min_y":51.4,"max_x":0.1,"max_y":51.6}. X is
longitude and Y is latitude. Within a selection list entries are alternatives; different
filter criteria intersect. Select one to four distinct trace references for map, panel,
timeline and rows requests to return measurement data. Several may belong to one family.
The former metrics and trace filter fields are rejected.
To read one existing trace, supply a singleton filter.traces list. The trace
must belong to the named family to produce results; it is never inferred from a label. Use
filter.places to name the places to inspect; panel requests require it.
Panel pairs lists selected traces with supported timed results ever recorded
at the requested places, in selection order. Current dates do not remove an eligible tab.
The optional preferred:{metric,trace} chooses the active pair when it is
eligible; otherwise the first eligible pair is used. Only that pair gets an overview and
grid rows. If none is eligible, active is null. The server retains no panel
state. Row counts include matching missing assertions, so they can differ from
displayable-result counts; finite date ranges exclude untimed assertions.
Row blocks accept a limit of 1–500. Sort columns are time,
value, metric, statistic or location,
with asc/desc direction; Quality is not sortable. To continue,
pass any entry from the last row’s references array as
rows.after, with offset:0. Reset continuation when the filter or
sort changes. To open a source form, pass a returned reference unchanged as
record.reference; its fields are an API address, not client database state.
Download CSV with GET /v1/export.csv?request=<URL-encoded JSON>, using
{"filter":{...},"sort":{"column":"time","direction":"desc"}}. Sort is
optional. This streams all matching rows from the buffer, including exact values, UTC
timestamps, units, qualifiers, locations and source references. The map exports its active
tab's trace and accepted filters. There is no export job or stored session.
Timeline count_bins accepts 1–2000. A source interval can occupy several
bins: adding their counts is not a count of distinct measurements. Exact readings are
requested separately by history and viewport. Values, units, native time precision and
qualifications must remain attached when interpreting results. Decimal measurements are
returned as strings where exact representation matters.
The initial full reading response supplies numeric summaries and exact reported outcome
labels for the applied period. Use readings.window_only:true for later
viewport reads: these return the intersected reading_window, source
descriptors and exact readings, without recalculating the full-period summary. The map
retains its initial presentation while scrolling and prepares it again if a returned
outcome or numeric result cannot use it. No server session is held.
Completion and refresh
Successful responses contain {"data":…, "complete":true}. Treat a non-success
HTTP status, complete:false, truncated JSON or an interrupted transfer as
failure. JSON error responses provide error.message.
error.code is optional: application errors can include a diagnostic code,
while nginx body-limit and gateway failures provide a message without one. Do not
interpret a failed request as an empty dataset.
Each request uses one complete published buffer view. Separate requests can observe successive publications as imports arrive. Each row block reports its matching count; the map updates the grid count and reloads cached blocks when that count changes. This is a browsing API, not a frozen multi-request export.
Use one catalogue download for text search and sorting, and trace_search for
discovery by reading dates. Reuse returned identities, cancel superseded reads and request
only needed row blocks. Queries are limited to a 2 MiB request body and may be refused
when the service is busy; retry deliberately rather than looping.
Source descriptions, collection cadence and dataset-specific reuse terms apply to API results too.