Developer API

CityAlert.live normalizes live public-safety incidents — police open-data feeds, earthquake agencies, wildfire services, weather warnings, disaster alerts, geocoded media reports and community submissions — into one GeoJSON stream. The same endpoints that power the map are public and free to use with attribution.

Machine-readable spec: /api/openapi.json (OpenAPI 3.1). For AI agents, see llms.txt, agents.txt and the MCP server below. What changed recently: /changelog (Atom).

Live endpoints need no key. For history, higher limits or webhooks: get an API key ↓

Public endpoints

  • GET/api/incidents

    Merged live incident stream as a GeoJSON FeatureCollection

    Filter with ?region=, ?category=, ?source=, ?bbox=minLon,minLat,maxLon,maxLat, ?days=, ?limit= (max 10000), ?id=. Use ?format=json for a plain array. ?refresh=1 forces an upstream re-fetch. History beyond ?days=7 requires a paid API key.

  • GET/api/archive

    Historical incident archive — every incident every feed has ever returned (paid)

    Requires an x-api-key header on a Developer or Pro plan. Filter with ?source=, ?region=, ?category=, ?bbox=minLon,minLat,maxLon,maxLat, ?from= & ?to= (ISO 8601, on the incident's occurred time), ?limit= (default 500, max 5000). Returns {incidents, count, from, to} newest-first.

  • GET/api/regions

    Focus-region list — key, label, center, zoom, bbox

  • GET/api/trends

    Historical aggregates — crime YoY, earthquakes by region, GDACS by year

  • GET/api/crime-year?year=YYYY&region=<key>

    Full-year incident GeoJSON + neighbourhood ranking

    Vancouver VPD history goes back to 2003; other regions via police portals or the media log.

  • GET/api/news

    Regional safety news items

    ?place=<name> for ad-hoc places outside the focus regions.

  • GET/cities/<slug>/feed.xml

    Atom feed of a city's 20 most recent incidents

    Advertised via <link rel="alternate"> on every city page.

  • GET/api/reports

    Community-submitted incident reports

  • POST/api/reports

    Submit a community report

    Body: {title, lat, lon, category?, severity?, description?}. Rate-limited to 5/hour per IP; reports are community-verified before going active.

  • POST/api/reports/<id>/vote

    Vote on a report — confirm, resolve or flag

    Body: {kind: "confirm"|"resolve"|"flag"}. 30 votes/hour per IP.

  • GET/api/dashboards?id=<id>

    Fetch a shared dashboard config

  • POST/api/dashboards

    Create a shareable dashboard — returns {id, token}

    The token is the write credential; PUT /api/dashboards updates with {id, token, config}.

  • POST/api/mcp

    MCP (Model Context Protocol) server for AI assistants

    JSON-RPC 2.0 — initialize, tools/list, tools/call over the same incident data. Historical tools (get_trends, get_crime_year) require a Pro-plan x-api-key.

Incident object

Every incident — in GeoJSON properties or JSON array items — carries:

id          stable source-prefixed identifier
source      feed key (e.g. "usgs", "vpd", "zwaailicht")
category    crime | fire | disaster | weather | unrest | health | infra | report
type        source-specific type (e.g. "earthquake", "Break and Enter")
title       headline
description optional detail (plain text, max ~160 chars)
lat, lon    WGS84 coordinates
time        reported time, ISO 8601
severity    low | medium | high | critical
url         upstream source link
region      focus-region key when inside one
nhood       neighbourhood label when the feed provides one
status      community-report state: pending | active | resolved | hidden

API keys & plans

Paid endpoints authenticate with an x-api-key: ca_… header. Keys are self-serve: sign in and create one under Account → API keys — free keys are instant, paid tiers upgrade through Stripe on the same page. Usage is metered per UTC day across all endpoints; over-limit requests return 429 with a Retry-After hint.

  • Free

    60 requests/day — shared ~15-minute snapshot (responses carry meta.delayed); real-time refresh, archive, deep history and MCP history tools require a paid plan.

  • Developer — $99/mo

    5,000 requests/day — GET /api/archive; days>7 on /api/incidents; real-time refresh.

  • Pro — $499/mo

    50,000 requests/day — everything in Developer; get_trends + get_crime_year MCP history tools; outbound webhook delivery.

To upgrade, sign in and pick a paid tier under Account → API keys — Stripe handles checkout. The full comparison, including consumer and municipal plans, is on the pricing page. Missing or invalid keys get 401; a valid key on too-low a plan gets 403.

Outbound webhooks

Pro-plan keys can register HTTPS endpoints under Account → API keys instead of polling. Each dispatch tick (~15 min) POSTs a batch of newly-seen incidents matching the endpoint's category/region/severity filters:

POST <your-endpoint>
content-type: application/json
x-cityalert-signature: t=<unix>,v1=<hmac-sha256>

{"type":"incidents.batch","created":"<iso>","incidents":[
  {"id","source","category","type","title","description",
   "lat","lon","time","severity","url","region"} ]}

Add a bbox ([minLat, minLon, maxLat, maxLon]) to geo-fence an endpoint, and cluster_threshold (integer 2–1000) to switch it from per-batch delivery to a single incidents.cluster post when that many matching incidents sit inside the bbox within the trailing hour — it refires only when new incidents grow the cluster:

{"type":"incidents.cluster","created":"<iso>",
 "cluster":{"threshold":5,"window_minutes":60,
            "count":7,"bbox":[49.6,-119.5,50.1,-118.8]},
 "incidents":[ …same item shape as incidents.batch… ]}

Verify x-cityalert-signature exactly like a Stripe webhook: compute HMAC-SHA256(secret, t + "." + raw_body) with the signing secret shown once at registration and compare to v1. Endpoints are at-most-once per tick — a failed POST is logged but not retried, so re-pull /api/incidents to fill any gap. Endpoints that fail 25 consecutive ticks are auto-disabled (re-enable from your account).

MCP server

POST /api/mcp speaks the Model Context Protocol — point any MCP client (Claude, ChatGPT, open-source agents) at it to query incidents, regions and trends as tools instead of raw HTTP.

In Claude or ChatGPT, add https://cityalert.live/api/mcp as a custom connector / MCP server (streamable HTTP, no auth needed for the free tools). Clients that only send Accept: application/json get plain JSON-RPC responses — SSE is optional. Free and anonymous calls get the shared ~15-minute delayed snapshot; a paid x-api-key header unlocks real-time refresh, andPro-plan keys add get_trends and get_crime_year. Anonymous calls to those two return a tool-level error naming the key requirement — the MCP session itself stays healthy (no HTTP 401 mid-handshake).

Per-city Atom feeds

Each of the 157 city pages publishes an Atom feed of its 20 most recent incidents at /cities/<slug>/feed.xml. City pages themselves are ISR-rendered every ~5 minutes with live incident lists — safe to crawl and cite. Community reports drop out of rendered pages immediately when they're resolved or hidden (the status flip purges the page — no waiting on the revalidate window).

Embed widget

/embed renders a compact, map-free incident summary that can be iframed into any page — scoped by ?region=<key> (see /api/regions) or ?city=<slug> (any /cities/ slug):

<iframe
  src="https://cityalert.live/embed?city=vancouver-bc"
  width="100%" height="360" style="border:0" loading="lazy"
  title="CityAlert incident summary"></iframe>

The widget refreshes every ~10 minutes and links back to the live map. Keep the CityAlert.live attribution link intact. Self-serve: Embed a widget generates a ready-to-paste snippet for any region.

Usage, rate limits & attribution

  • Live read endpoints are public; deep history (/api/archive, days>7, MCP history tools) requires a paid API key. Community write endpoints are IP rate-limited.
  • Attribution: cite CityAlert.live and the underlying source listed in each incident's source field.
  • Media-sourced incidents are approximate block-level locations — preserve that caveat when republishing.
  • Commercial use, redistribution and team delivery are covered on the data licensing page.
  • This is an aggregation/awareness tool. For emergencies defer to official authorities.
  • Data is provided under CC BY 4.0 for our normalization layer; underlying feeds carry their own licenses — see Sources & licenses.