Skip to content
CRATON Docs Get a free API key

Data

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 catalog 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 catalog 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 neighboring 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.

On this page

What is served, and what is not

Event catalogs are served for perils Craton pins a catalog of. Today that is:

  • earthquake, from seven regional USGS extracts: Kanto, California, Cascadia, Alaska, Chile, Turkey and Mexico (feeds has each one's box);
  • severe_convective_storm, from NOAA's storm reports: hail, thunderstorm wind and tornado for six US metros;
  • wildfire, from the fires MTBS has mapped in five western regions.

The storm and wildfire records are report extracts rather than quake catalogs, and their pages are shaped for them: reading a report extract below. Because every peril has more than one feed, a read has to say which it means, in one of three ways:

  • feed=<id> names the catalog outright, for example feed=usgs-eq-kanto.
  • A box (min_lat, max_lat, min_lon, max_lon) is answered by the one catalog whose extract holds the whole box. A box no single extract holds is refused with every extract's edges, because a catalog that stops inside the box would report fewer events than occurred.
  • A footprint is found by its event id, which is the publisher's own and names one event in one catalog. Earthquakes only: a storm report or a mapped wildfire is a point, and asking for its footprint is answered 404 no_event_footprint.

A read that does none of these is refused with 404 no_event_catalogue and the list of feeds, rather than answered from whichever catalog happened to sort last.

A peril whose pinned record is a gridded daily series (rainfall, wind) has no event catalog here, and asking for one is answered with a 404 no_event_catalogue rather than with events manufactured out of exceedance days:

JSON
{ "error": {
    "status": 404,
    "code": "no_event_catalogue",
    "message": "Craton pins no event catalogue for peril 'tropical_cyclone'. Event catalogues are served for earthquake, severe_convective_storm, wildfire. …" } }

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 catalog solutions, not a shaking map. The pinned snapshot holds hypocenters, depths and magnitudes. It holds no modeled intensity surface, so none is published. Synthesizing 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 catalog

Shell Runs in CI
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 Kanto earthquake catalog.
curl -sS "$CEDE_BASE_URL/events/earthquake?feed=usgs-eq-kanto&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 catalog, filtered the way a structure filters it: a magnitude
#     floor, a box and a period. No feed= this time: the box is the Kanto
#     extract's own, so the Kanto catalog is the one that answers. 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. Earthquake catalogs only.
max_depth_km none Focal depth limit. An event with no published depth is excluded when a limit is set, never assumed shallow. Earthquake catalogs only.
min_value none A level in a report extract's own unit (inches of hail, knots, the tornado rating, acres). A record with no value never passes it. Report extracts only; on an earthquake catalog it is refused with 422 invalid_request, and so are min_magnitude and max_depth_km on a report extract.
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 a report extract

A storm report or a mapped wildfire is a record of one value at one point: a hail size, a gust, a tornado rating, the acres inside a fire's burn boundary. The page lists those records exactly as the extract holds them, filtered by a box, a period and a level in the extract's own unit, and its selection says which unit that is. The feed.kind on the page is storm_report_set, the same data-source kind a report-box price names, so you can tell the two shapes of page apart before reading an event.

Shell Runs in CI
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. Every Colorado wildfire MTBS mapped at 100,000 acres or more.
curl -sS "$CEDE_BASE_URL/events/wildfire?feed=mtbs-wildfire-colorado&min_value=100000" \
  -H "Authorization: Bearer $CEDE_API_KEY" > fires.json

python3 -c 'import json
page = json.load(open("fires.json"))
selection = page["selection"]
print("extract:", page["feed"]["id"], "-", page["feed"]["kind"])
print("at least", selection["minimum_value"], selection["unit"], "-",
      page["counts"]["selected"], "of", page["counts"]["events_in_snapshot"], "fires")
for fire in page["events"]:
    print("  ", fire["time"][:10], fire["event_id"], fire["value"], fire["unit"])'

# 2. Dallas-Fort Worth hail of 4 inches or more, three to a page. The storm
#     feeds for one metro share one box, so feed= is how a read chooses.
curl -sS "$CEDE_BASE_URL/events/severe_convective_storm?feed=ncei-hail-dallas&min_value=4&limit=3" \
  -H "Authorization: Bearer $CEDE_API_KEY" > hail.json

python3 -c 'import json
page = json.load(open("hail.json"))
for report in page["events"]:
    print("  ", report["time"], report["value"], report["unit"], report["event_id"])
print("next page:", page["page"]["next"])'

A record the publisher gave no value (an unrated tornado, a gust nobody measured) is on the page when you set no min_value, with a null value, and never passes one: a gap is not a zero. The default period runs through the end of the extract's last day. The records a report-box backtest pays on are these, and its rows name them by the same ids.


Reading one event's footprint

Shell Runs in CI
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?feed=usgs-eq-kanto&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 catalog holds 2409 events" and "the backtest paid six times", and this route is where you can watch it happen:

  • window takes your structure's aggregation_window.duration;
  • min_magnitude takes its attachment level, so only the solutions that qualify take part in the clustering;
  • occurrence.events is then exactly the set of catalog rows the backtest collapsed into that one payout, and peak_event_id is 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 summarized 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 catalog of natural events (time, hypocenter, 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 modeled 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 /usage will 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

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