Get a free API key

This page as Markdown, byte for byte: /hazard.md

Point hazard lookup: what the pinned data says about a coordinate#

GET /hazard, with peril, lat and lon in the query, answers one question — what has happened at this place, in the record this build pins — and answers it synchronously. There is no object to create first and no job to poll: a coordinate goes in, a hazard metric comes out, and the response names the exact snapshot it was computed from.

It is the first slice of Analyse (hazard lookups, event footprints, accumulation). This build serves all of it: the point lookup here, the event catalogue and event footprints, the analyse job over an object — which is this same lookup, aimed at an object's own locations — and accumulation across a set of objects.

Read the quickstart first for the two values you export (CEDE_BASE_URL, CEDE_API_KEY) and how to get a key from POST /signup.

What it is for, and what it is not#

A trigger is a threshold, and a threshold is a decision about a place. Before you write attachment.value into a risk object, this route tells you what the public record for that place actually holds: how often the ground there has broken above magnitude 6, or what daily rainfall total the last forty years put a one-in-ten-year label on. That number is the input to the decision, and you can check it against the same bytes the price path reads.

What it is not:

The three parameters#

Parameter What it is
peril One of the perils this build pins data for. GET /health will not tell you which; the refusal for a wrong one does, and so does the table below.
lat Latitude in decimal degrees, negative for south. Degrees, minutes and seconds are not read.
lon Longitude in decimal degrees, negative for west.

Nothing else is read. A fourth parameter is refused rather than ignored — a call answered as though lng were lon is a lookup of somewhere else.

What can be asked, and where#

A peril is answerable when a snapshot of it is committed, because the engine reads snapshots and never the network. So the answerable set is exactly the pinned data, listed in full on the pinned data feeds:

peril Answered from Where it answers
earthquake usgs-eq-kanto — the USGS catalogue extract 34.5–36.5N, 138.5–141.0E: the extract's box, less the half-degree cell the lookup counts in
weather_station era5-rain-bangkok, or any of the four pinned wind series within 0.25° of one of their grid points (listed below)
tropical_cyclone the four pinned wind series — a daily wind maximum is a named-storm index within 0.25° of one of their grid points (listed below)

The grid points, which are the publisher's cell centres and not the coordinates the snapshots were requested at:

Feed Grid point
era5-rain-bangkok 13.743409N, 100.495865E
era5-wind-galveston 29.279436N, -94.87326E
era5-wind-miami 25.764498N, -80.196075E
era5-wind-new-orleans 29.982424N, -90.10489E
era5-wind-tampa 27.94376N, -82.49155E

Two shapes of snapshot, two shapes of answer:

Coverage is checked rather than approximated. A cell that reached over the edge of an extract would count fewer events than really occurred and report a rate too low for a reason nothing records, so a point near the edge is refused with the edge it crossed. The area is an axis-aligned cell in degrees rather than a radius in kilometres, deliberately: a great-circle distance needs arithmetic that does not replay byte for byte, and replay is worth more here than a round search area.


Walk it#

The block below looks up earthquake hazard at a point in Tokyo and wind at a point in Miami, prints what each answer rests on, and then fetches the manifest of the snapshot the first answer named — so the feed id, the vintage and the digest in the response are three strings you can follow, not three strings you have to take on trust.

set -euo pipefail

: "${CEDE_BASE_URL:?export CEDE_BASE_URL first — see the quickstart}"
: "${CEDE_API_KEY:?export CEDE_API_KEY first — see the quickstart}"

# 1 — Earthquake hazard at 35.6284N, 139.7387E (Tokyo).
curl -sS "$CEDE_BASE_URL/hazard?peril=earthquake&lat=35.6284&lon=139.7387" \
  -H "Authorization: Bearer $CEDE_API_KEY" > quake.json

python3 -c 'import json
answer = json.load(open("quake.json"))
feed = answer["feed"]
print("feed     :", feed["id"], feed["vintage"], feed["sha256"][:12] + "…")
record = answer["assumptions"]["period_of_record"]
print("record   :", record["start"], "to", record["end"],
      "(" + str(record["years"]), "years)")
metrics = answer["metrics"]
print("cell     :", metrics["cell"]["min_latitude"], "to",
      metrics["cell"]["max_latitude"], "N /",
      metrics["cell"]["min_longitude"], "to",
      metrics["cell"]["max_longitude"], "E")
print("events   :", metrics["events_in_cell"])
for row in metrics["exceedance"]:
    print("   M%-5s %4d event(s), %s a year; %d occurrence(s), %s a year"
          % (row["magnitude"], row["events"], row["annual_rate"],
             row["occurrences"], row["annual_occurrence_rate"]))
largest = metrics.get("largest_event")
if largest:
    print("largest  : M%s on %s" % (largest["magnitude"], largest["time"]))
print("disclosure:", answer["disclosure"])'

# 2 — Named-storm wind at 25.7617N, -80.1918E (Miami).
curl -sS "$CEDE_BASE_URL/hazard?peril=tropical_cyclone&lat=25.7617&lon=-80.1918" \
  -H "Authorization: Bearer $CEDE_API_KEY" > wind.json

python3 -c 'import json
answer = json.load(open("wind.json"))
metrics = answer["metrics"]
measure = metrics["measurement"]
print("measures :", measure["variable"], "in", measure["unit"],
      "(" + measure["resolution"] + ")")
grid = metrics["grid_point"]
print("grid cell:", grid["latitude"], grid["longitude"],
      "offset", grid["offset_degrees"]["latitude"],
      grid["offset_degrees"]["longitude"], "degrees")
print("days     :", metrics["days_observed"], "observed,",
      metrics["days_missing"], "missing")
print("wettest/windiest day:", metrics["maximum_daily"]["value"],
      "on", metrics["maximum_daily"]["date"])
for level in metrics["return_levels"]:
    print("   1-in-%-3d %s %s" % (level["return_period_years"],
                                  level["level"], measure["unit"]))
print("known limits:")
for line in answer["assumptions"]["limitations"]:
    print("  -", line)'

# 3 — The snapshot the first answer named, fetched by the id and version in it.
FEED_ID="$(python3 -c 'import json; print(json.load(open("quake.json"))["feed"]["id"])')"
VERSION="$(python3 -c 'import json; print(json.load(open("quake.json"))["feed"]["version"])')"

curl -sS "$CEDE_BASE_URL/feeds/$FEED_ID/versions/$VERSION" \
  -H "Authorization: Bearer $CEDE_API_KEY" > manifest.json

python3 -c 'import json, sys
answer = json.load(open("quake.json"))
manifest = json.load(open("manifest.json"))
same = manifest["sha256"] == answer["feed"]["sha256"]
print("the lookup read", manifest["id"], manifest["version"],
      "published by", manifest["publisher"])
print("digests agree:", same)
sys.exit(0 if same else 1)'

The last line exits non-zero if the digest on the answer and the digest on the snapshot ever disagreed. They cannot, and that is the point: the lookup names the bytes, and the bytes are fetchable at GET /feeds/{feed_id}/versions/{version}/data, where you can recompute the digest yourself — the pinned data feeds walks that check.


Reading the answer#

Every response carries five things, and none of them is optional:

The full response shape is published as [hazard.v0.schema.json](/hazard.v0.schema.json); the map from every route to the schema governing its responses is [api-contract.v0.json](/api-contract.v0.json).

When it refuses#

Both refusals are 422 in the standard error envelope (error reference), and both name what would have worked.

A peril nothing is pinned for:

{
  "schema_version": "0.1.0",
  "error": {
    "status": 422,
    "code": "unsupported_peril",
    "message": "this build answers point hazard lookups for earthquake, tropical_cyclone, weather_station; it has no pinned feed that speaks to wildfire. …"
  }
}

A point no pinned snapshot of that peril covers:

{
  "schema_version": "0.1.0",
  "error": {
    "status": 422,
    "code": "outside_feed_coverage",
    "message": "no pinned earthquake snapshot covers 0,0. usgs-eq-kanto@2026-08-10: its extract covers 34.0..37.0N and 138.0..141.5E, which does not hold the whole 0.5° cell around this point. …"
  }
}

There is no all-of-the-world feed to fall back on, by design. A snapshot that covered less than the area asked about would answer with less hazard than the record holds, and nothing in the response would say so.

What comes next#

GET /hazard is a lookup, not an artifact: it writes nothing onto an object and leaves no provenance behind. The verb that does — this same reading at every location of an object, recorded on that object's provenance with its assumptions and data vintages — is [POST /objects/{id}/analyse](analyse.md). Two more slices answer elsewhere and write nothing either: the event catalogue and footprints, and accumulation across a set of objects, which totals the exposure you have already stored. GET /health is the list of what the environment in front of you reaches at all.