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.