This page as Markdown, byte for byte: /events.md
Events: the hazard record behind a result#
A backtest tells you six years paid and names the events it paid on. This page is where those names come apart into the record itself: the catalogue Craton read, the exact solutions that qualified, and the rule that collapsed several of them into one occurrence.
Two routes, both reads, neither metered:
| Route | What it answers |
|---|---|
GET /events/{peril} |
The pinned event catalogue for a peril, filtered the way a structure filters it |
GET /events/{peril}/{event_id}/footprint |
One event's footprint as the record has it: the occurrence it belongs to, and the box that occurrence spans |
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. Feeds is the neighbouring page: it serves the publisher's bytes verbatim so you can check the digest, and this page serves the same bytes parsed the way the engine parses them.
What is served, and what is not#
Event catalogues are served for perils this build pins a catalogue of. Today that is **earthquake**, from the USGS Kanto extract. A peril whose pinned record is a gridded daily series — rainfall, wind — has no event catalogue here, and asking for one is answered with a 404 no_event_catalogue rather than with events manufactured out of exceedance days:
{ "error": {
"status": 404,
"code": "no_event_catalogue",
"message": "this build pins no event catalogue for peril 'tropical_cyclone'. Event catalogues are served for earthquake. …" } }
The refusal names the perils that do answer, so finding the right one costs a round trip rather than a search.
A footprint here is a cluster of catalogue solutions, not a shaking map. The pinned snapshot holds hypocentres, depths and magnitudes — it holds no modelled intensity surface, so none is published. Synthesising one would be a model, with assumptions and a version and a card on it, and a model belongs in the registry rather than behind a data route. Every footprint response says this in its own limitations.
Reading the catalogue#
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 — The first page of the pinned earthquake catalogue.
curl -sS "$CEDE_BASE_URL/events/earthquake?limit=5" \
-H "Authorization: Bearer $CEDE_API_KEY" > events.json
python3 -c 'import json
page = json.load(open("events.json"))
feed = page["feed"]
print("catalogue:", feed["id"], "vintage", feed["vintage"])
print("digest: ", feed["sha256"])
print("in snapshot:", page["counts"]["events_in_snapshot"],
"selected:", page["counts"]["selected"],
"on this page:", page["counts"]["returned"])
for event in page["events"]:
print(" ", event["time"], "M" + str(event["magnitude"]),
event["latitude"], event["longitude"],
"depth", event["depth_km"], "km", "—", event["event_id"])'
# 2 — The same catalogue, filtered the way a structure filters it: a magnitude
# floor, a box and a period. The response states the filter back.
curl -sS "$CEDE_BASE_URL/events/earthquake?min_magnitude=6.5&min_lat=34&max_lat=37&min_lon=138&max_lon=141.5&from=1986-01-01&to=2026-01-01&limit=50" \
-H "Authorization: Bearer $CEDE_API_KEY" > qualifying.json
python3 -c 'import json
page = json.load(open("qualifying.json"))
selection = page["selection"]
print("M>=", selection["minimum_magnitude"],
"in", selection["box"]["min_latitude"], "-", selection["box"]["max_latitude"], "N",
selection["box"]["min_longitude"], "-", selection["box"]["max_longitude"], "E")
print("from", selection["from"], "to", selection["to"], "(", selection["period_bounds"], ")")
print("defaulted:", selection["defaulted"] or "nothing — every filter was yours")
print(page["counts"]["selected"], "events qualify")
print("next page:", page["page"]["next"])'
# 3 — The digest on the response is the digest of the pinned file, so the
# events above and the bytes GET /feeds serves are one snapshot.
FEED_ID="$(python3 -c 'import json; print(json.load(open("events.json"))["feed"]["id"])')"
VERSION="$(python3 -c 'import json; print(json.load(open("events.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
served = json.load(open("events.json"))["feed"]["sha256"]
pinned = json.load(open("manifest.json"))["sha256"]
print("same snapshot:", served == pinned)'
The parameters#
| Parameter | Default | What it does |
|---|---|---|
feed, version |
the latest pinned vintage | Pin the exact snapshot. A result names both in its assumption set; pass them to read the record that result read. |
min_magnitude |
the snapshot's own minimum | Magnitude floor. Never below the snapshot's minimum: the events under it are missing from the extract, not from history. |
max_depth_km |
none | Focal depth limit. An event with no published depth is excluded when a limit is set, never assumed shallow. |
from, to |
the snapshot's period of record | from inclusive, to exclusive, so consecutive periods tile without counting a midnight event twice. A bare date is midnight UTC. |
min_lat, max_lat, min_lon, max_lon |
the snapshot's own box | An axis-aligned box, inclusive on every edge. All four or none — three edges describe no region. |
limit, offset |
100, 0 | Paging. The ceiling is 1000; follow page.next, which pins the feed and version so a walk cannot mix two vintages. |
Anything else is refused with 422 invalid_request rather than ignored: a parameter answered as though it were another would hand you a different set of events with no way to tell.
Reading one event's footprint#
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 — Find a large event. Every event carries the address of its own footprint.
curl -sS "$CEDE_BASE_URL/events/earthquake?min_magnitude=7.0&limit=1" \
-H "Authorization: Bearer $CEDE_API_KEY" > big.json
FOOTPRINT="$(python3 -c 'import json
page = json.load(open("big.json"))
assert page["events"], "no M7.0+ event in this catalogue"
print(page["events"][0]["footprint"])')"
echo "reading $FOOTPRINT"
# 2 — The footprint: the occurrence this event belongs to.
curl -sS "$CEDE_BASE_URL$FOOTPRINT" \
-H "Authorization: Bearer $CEDE_API_KEY" > footprint.json
python3 -c 'import json
document = json.load(open("footprint.json"))
occurrence = document["footprint"]["occurrence"]
extent = document["footprint"]["extent"]
print("event: ", document["event_id"], "M" + str(document["event"]["magnitude"]))
print("catalogue: ", document["feed"]["id"], "vintage", document["feed"]["vintage"])
print("window: ", occurrence["window"], "from", occurrence["from"], "to", occurrence["to"])
print("solutions: ", occurrence["event_count"], "— peak", occurrence["peak_event_id"])
print("extent: ", extent["min_latitude"], "-", extent["max_latitude"], "N",
extent["min_longitude"], "-", extent["max_longitude"], "E")
for event in occurrence["events"][:10]:
print(" ", event["time"], "M" + str(event["magnitude"]), event["event_id"])
print()
for limitation in document["limitations"]:
print("*", limitation)'
# 3 — Your structure's own window, not the default: a 24-hour event window
# collapses fewer solutions into one occurrence than a 72-hour one.
curl -sS "$CEDE_BASE_URL$FOOTPRINT?window=PT24H&min_magnitude=5.0" \
-H "Authorization: Bearer $CEDE_API_KEY" > narrow.json
python3 -c 'import json
wide = json.load(open("footprint.json"))["footprint"]["occurrence"]
narrow = json.load(open("narrow.json"))["footprint"]["occurrence"]
print(wide["window"], "->", wide["event_count"], "solutions")
print(narrow["window"], "->", narrow["event_count"], "solutions at M>=",
narrow["minimum_magnitude"])'
What the occurrence means#
A cat-in-a-box trigger pays per occurrence, and a mainshock's aftershocks are not separate occurrences. The rule is the aggregation window, measured from the first event of the cluster and never from the previous one — a chained rule would merge an unbounded aftershock sequence into a single occurrence, which is not what a 72-hour event window means.
That rule is the step between "the catalogue holds 2409 events" and "the backtest paid six times", and this route is where you can watch it happen:
windowtakes your structure'saggregation_window.duration;min_magnitudetakes its attachment level, so only the solutions that qualify take part in the clustering;occurrence.eventsis then exactly the set of catalogue rows the backtest collapsed into that one payout, andpeak_event_idis the magnitude the payout function read.
The rule is time only, applied across the whole extract. Two solutions far apart inside the box can land in one occurrence, which is why every member is listed with its own position rather than summarised into a point — and why extent is a bounding box of solutions rather than a contour of anything.
What these documents are, and are not#
- Hazard data. A public catalogue of natural events — time, hypocentre, magnitude — published by a government agency. Feeds on Craton are aggregate only: no schedule, no insured value, no individual-level record appears on this surface or any other feed surface.
- A record, not a view of risk. No price, no loading, no statement about anyone's willingness to take the hazard on. Prices live behind Price, with their assumptions exposed.
- Not a modelled footprint. See the limitations on every footprint response. A ground-motion or damage surface would be a model in the registry, carrying a card that states its assumptions and its author.
- Free. Craton bills per object and per model run; reading the record behind
a result is neither, so these routes are not metered.
GET /usagewill not show them.
The published schemas are event-catalogue.v0.schema.json and event-footprint.v0.schema.json, both listed in the API contract document with every other response shape — validate against them while holding no Craton code.
Where to go next#
- Feeds — the same snapshots as bytes, with the digest to check.
- Backtest — the results these events explain.
- Checking a backtest yourself — the whole loop, from a payout back to the catalogue rows behind it.