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.