Dashboard

API

JSON over HTTPS. All endpoints are GET, CORS-enabled, and served from the most recent scrape of the FAA NAS Status feeds (every minute).

Base URL: https://www.groundstop.app

curl https://www.groundstop.app/api/v1/ground-stops
Authentication

Every endpoint except /api/v1/health needs an API key. Create an account, then make a key on the Alerts & API page. Send it as X-API-Key: nas_…, Authorization: Bearer nas_…, or ?api_key=nas_…. Requests from a signed-in browser also work without a key. Missing or invalid keys get 401.

curl -H "X-API-Key: $KEY" "https://www.groundstop.app/api/v1/ground-stops?airport=EWR"
GET/api/v1/ground-stops
Active ground stops and projected (planned/possible) ground stops. Active stops carry a scope when the FAA publishes one: the airport's home ARTCC (center), the departure facilities whose flights are held (ARTCC ids like ZNY, sometimes Canadian facilities like CYYZ), official start/end times (UTC), the probability of extension and the advisory link. Ground delay programs carry the same scope object.
airport
Filter to one airport, e.g. EWR (KEWR also accepted)
type
active or projected to return only one list
{
  "fetchedAt": "2026-09-30T22:35:00.000Z",
  "faaUpdateTime": "Tue Sep 30 22:34:12 2026 GMT",
  "stale": false,
  "active": [
    { "airport": "DFW", "reason": "thunderstorms", "endTime": "8:15 am CDT",
      "scope": {
        "center": "ZFW",
        "includedFacilities": ["ZLA", "ZAU", "ZTL", "ZHU", "ZJX", "ZFW", "ZDV",
                               "ZMA", "ZKC", "ZME", "ZID", "ZAB", "ZMP"],
        "startTime": "2026-10-01T12:00:00Z", "endTime": "2026-10-01T13:15:00Z",
        "probabilityOfExtension": "MEDIUM",
        "advisoryUrl": "https://www.fly.faa.gov/adv/adv_otherdis.jsp?advn=38&..." } }
  ],
  "projected": [
    { "airports": ["EWR", "LGA"], "event": "EWR/LGA GS POSSIBLE",
      "time": "after 2000Z", "section": "terminal" }
  ]
}
GET/api/v1/ground-stops/history
One record per ground stop, active and ended: start, end, duration, reasons, and every published end time (more than one means it was extended). Start and end are when the scraper first and last saw the stop, so they're accurate to about a minute. Ended records are kept long-term (the most recent 10,000), not just 7 days.
airport
Filter to one airport
from / to
Date (2026-09-01) or ISO timestamp, UTC. Returns stops that overlap the window. Default: last 30 days
status
active, ended or all (default all)
limit
Records returned, 1–1000 (default 100). The summary always covers every match
{
  "from": "2026-09-01T00:00:00.000Z", "to": "2026-09-30T23:59:59.999Z",
  "airport": null, "status": "all", "count": 42, "returned": 42,
  "summary": { "groundStops": 42, "active": 1, "totalMinutes": 3810,
    "averageMinutes": 91, "longestMinutes": 305, "extended": 9,
    "byAirport": [ { "airport": "EWR", "name": "Newark Liberty", "count": 7,
      "totalMinutes": 812, "averageMinutes": 116 } ] },
  "records": [
    { "id": "EWR-2026-09-30T20:14:07.000Z", "airport": "EWR", "name": "Newark Liberty",
      "reason": "thunderstorms", "reasons": ["thunderstorms"],
      "startedAt": "2026-09-30T20:14:07.000Z", "endedAt": "2026-09-30T21:52:05.000Z",
      "active": false, "durationMinutes": 98, "startApproximate": false,
      "scheduledEndTimes": ["5:00 pm EDT", "5:45 pm EDT"], "extensions": 1 } ]
}
GET/api/v1/status
The full latest snapshot: ground stops, projected ground stops, ground delay programs, arrival/departure delays, closures and the whole operations plan.
{ "fetchedAt": "...", "groundStops": [...], "projectedGroundStops": [...],
  "groundDelayPrograms": [...], "arrivalDepartureDelays": [...],
  "closures": [...], "plannedEvents": [...], "errors": [], "stale": false }
GET/api/v1/airports/{code}
Everything currently reported for one airport.
{ "airport": "EWR", "name": "Newark Liberty",
  "hasActiveGroundStop": true, "hasProjectedGroundStop": false,
  "groundStop": {...}, "projectedGroundStops": [], "groundDelayProgram": null,
  "arrivalDepartureDelays": [], "closures": [] }
GET/api/v1/history
Time series (newest first), kept for 7 days: one point every 5 minutes, plus a point whenever active or projected ground stops change.
hours
Window in hours, 1–168 (default 24)
airport
Keep only that airport's ground stops in each point
{ "hours": 24, "count": 291, "points": [
  { "t": "...", "groundStops": [...], "projectedGroundStops": [...],
    "groundDelayPrograms": 2, "closures": 1, "arrivalDepartureDelays": 0 } ] }
GET/api/v1/events
Ground stop change log: started, updated (new end time or reason) and ended.
limit
1–2000 (default 100)
airport
Filter to one airport
{ "count": 1, "events": [
  { "t": "...", "airport": "SFO", "type": "started",
    "reason": "low ceilings", "endTime": "10:30 pm PDT" } ] }
GET/api/v1/health
Storage backend and age of the last successful scrape. Returns 503 when data is older than ~4 minutes.
{ "ok": true, "storage": "redis", "lastScrape": "...", "ageSeconds": 94, "lastErrors": [] }

Errors return { "error": "..." } with 400 (bad parameter), 401 (API key), or 503 (no data yet). stale: true means the last refresh from the FAA failed and older data is being served.