Developer API

CityAlert 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 and the MCP server below.

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 pro+ API key.

  • GET/api/archive

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

    Requires an x-api-key header on a pro or business 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 business-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 minted per plan and metered per UTC day across all endpoints; over-limit requests return 429 with a Retry-After hint.

  • free

    60 requests/day. Live endpoints only — the archive, deep history and MCP history tools are not included.

  • pro

    5,000 requests/day. Unlocks GET /api/archive and days>7 on /api/incidents.

  • business

    50,000 requests/day. Everything in pro plus the MCP historical tools (get_trends, get_crime_year).

Missing or invalid keys get 401; a valid key on too-low a plan gets 403.

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.

Per-city Atom feeds

Each of the 74 city pages publishes an Atom feed of its 20 most recent incidents at /cities/<slug>/feed.xml. City pages themselves are ISR-rendered every ~10 minutes with live incident lists — safe to crawl and cite.

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 attribution link intact.

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 and the underlying source listed in each incident's source field.
  • Media-sourced incidents are approximate block-level locations — preserve that caveat when republishing.
  • 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.