US-focused backend API for finding the best times to get outside.
It combines forecast, alerts, air quality, UV, astronomy, and aviation weather data into activity-aware recommendation windows for:
bikehikefishingastronomydrone
Outdoor decision-making is scattered across multiple sources: forecast, alerts, air quality, UV, moonlight, and in some cases aviation visibility. This API pulls those signals into one place and turns them into recommendation windows that are easier for a mobile app to explain and trust.
The API is designed to turn multiple upstream data sources into one recommendation payload with:
- normalized hourly weather, AQI, UV, astronomy, and aviation context
- activity-aware hourly scores
- top recommendation windows with plain-language reasons
- safety overlays such as severe alerts and aviation visibility context
flowchart LR
Client["Mobile / Web Client"] --> API["Outdoor Activity API"]
API --> NWS["Weather.gov\nforecast, alerts, grid data"]
API --> AirNow["AirNow\nAQI"]
API --> EPA["EPA UV\nhourly UV"]
API --> USNO["USNO\nmoon phase + illumination"]
API --> AWC["Aviation Weather\nMETAR + TAF"]
API --> Geo["Zippopotam.us / Census\nlocation search + reverse geocode"]
API --> Score["Scoring Engine\nwindows, safety, comfort"]
Score --> Client
- Hour-by-hour scoring for the next 24 hours
- Top recommendation windows instead of single "best hours"
- Weather.gov alert awareness with high-risk filtering
- Structured wind gust and feels-like temperature enrichment from Weather.gov grid data
- AirNow AQI integration
- EPA UV integration
- USNO moon illumination and phase enrichment for astronomy
- Aviation Weather current METAR visibility plus nearest-airport TAF forecast context for drone and astronomy
- ZIP, city/state, and coordinate-based location workflows
- Render-friendly deployment and production hardening
- ZIP lookup
- City/state lookup
- GPS-style coordinate requests
- Weather.gov forecast and alerts
- AirNow AQI
- EPA UV
- Weather.gov wind gusts
- Weather.gov feels-like temperature
- USNO moon illumination and phase
- Aviation Weather METAR visibility
- Aviation Weather TAF forecast summary
- Activity-aware scoring for
bike,hike,fishing,astronomy, anddrone
- Weather.gov
- AirNow API
- EPA UV data
- USNO Astronomical Applications API
- Aviation Weather Center Data API
- Zippopotam.us
- U.S. Census Geocoder
Basic health check.
Example:
curl http://localhost:3000/healthResolves a US city/state pair to coordinates and, when available, ZIP.
Example response:
{
"city": "Gaithersburg",
"state": "MD",
"displayName": "Gaithersburg, MD",
"lat": 39.1419,
"lon": -77.189,
"zip": "20877"
}Returns scored hourly forecast data plus top recommendation windows.
Example:
curl "http://localhost:3000/recommendations?lat=39.1419&lon=-77.189&zip=20877&city=Gaithersburg&state=MD&activity=bike"Response includes:
locationwarningsairQualityuvastronomy(for astronomy activity)aviation(current METAR plus forecast TAF data for drone and astronomy)recommendationshourly
Example response excerpt:
{
"location": {
"lat": 39.1419,
"lon": -77.189,
"zip": "20877",
"displayName": "Gaithersburg, MD"
},
"activity": "bike",
"warnings": {
"hasAnyAlert": false,
"hasSevereAlert": false,
"hasHighRiskAlert": false
},
"airQuality": {
"source": "airnow",
"currentAqi": 36
},
"uv": {
"source": "epa-uv"
},
"aviation": {
"current": {
"source": "aviationweather",
"visibilityMiles": 10,
"flightCategory": "VFR"
},
"forecast": {
"source": "aviationweather",
"issuedAt": "2026-03-24T15:02:00.000Z",
"periods": [
{
"startTime": "2026-03-24T15:00:00.000Z",
"endTime": "2026-03-24T20:00:00.000Z",
"visibilityMiles": 6,
"ceilingFeet": null,
"flightCategory": "VFR"
}
]
}
},
"recommendations": [
{
"start": "2026-03-18T14:00:00-04:00",
"end": "2026-03-18T16:00:00-04:00",
"hours": 2,
"averageScore": 84,
"why": [
"Cooler than preferred",
"Wind conditions look good",
"Low rain chance"
]
}
]
}- Prefers comfortable temperatures, manageable wind, good AQI, and daylight
- Uses structured gusts and feels-like temperature to avoid “looks nice on paper” false positives
- Uses stricter wind and gust thresholds
- Surfaces aviation visibility and TAF forecast context
- Treats severe alerts and unsafe wind as hard stops
- Prefers nighttime and clearer skies
- Uses moon phase and illumination from USNO
- Adds aviation visibility as a helpful haze/clarity signal
npm installCopy .env.example to .env and fill in values as needed.
PORT=3000
NODE_ENV=production
WEATHER_GOV_UA=OutdoorTimeFinder/1.0 (your-email@example.com)
AIRNOW_API_KEY=your_airnow_api_key_here
CORS_ORIGINS=https://your-frontend.example.com
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX_REQUESTS=60
UPSTREAM_TIMEOUT_MS=8000
RECOMMENDATION_CACHE_TTL_MS=300000Notes:
AIRNOW_API_KEYis optional for local development, but AQI will benullwithout it.WEATHER_GOV_UAshould include a real contact email in production.CORS_ORIGINSaccepts a comma-separated allowlist of browser origins.
npm startcurl http://localhost:3000/health
curl "http://localhost:3000/location-search?city=Gaithersburg&state=MD"
curl "http://localhost:3000/recommendations?lat=39.1419&lon=-77.189&zip=20877&city=Gaithersburg&state=MD&activity=bike"Run a quick code sanity check:
npm testCurrent test coverage is lightweight and focused on syntax/entrypoint validation. Functional API smoke tests are still manual.
Regression tests currently cover:
- UV hourly matching without timezone drift
- multi-word city/state parsing
- cache key separation for explicit place requests
- structured Weather.gov gust parsing
- structured Weather.gov apparent temperature parsing
- USNO moon illumination parsing
- Aviation Weather visibility parsing
- Aviation Weather TAF category and ceiling derivation
- cache behavior for UV edge cases
- UV may be
nullat night after the upstream hourly UV series ends; this is normal and should be treated as no UV risk, not a daytime failure. - Aviation data is advisory and best-effort. Nearby stations sometimes have incomplete observations, so the API falls back to other nearby METAR stations when needed.
- TAF data is airport-based forecast context, not a hyperlocal surface forecast for every coordinate.
- Astronomy and aviation enrichments are scoped to the activities that actually benefit from them, rather than affecting every recommendation equally.
This repo includes render.yaml, but the easiest path for most users is a standard Render Web Service.
Use:
- Build command:
npm install - Start command:
npm start - Health check path:
/health
Recommended environment variables:
NODE_ENV=production
WEATHER_GOV_UA=OutdoorTimeFinder/1.0 (your-email@example.com)
AIRNOW_API_KEY=your_airnow_api_key_here
CORS_ORIGINS=https://your-frontend.example.com
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX_REQUESTS=60
UPSTREAM_TIMEOUT_MS=8000
RECOMMENDATION_CACHE_TTL_MS=300000If your Render plan supports Blueprints, this repo is already set up for that via render.yaml.
- proxy-aware request handling
- configurable CORS allowlist
- basic security headers
- per-IP rate limiting
- upstream timeouts
- short-lived response caching
- safer external API error handling
- backend-only integration for providers that do not permit browser CORS
- Visibility-aware scoring for drone and astronomy
- Richer astronomy response examples in the docs
- Broader API regression coverage beyond parser-focused tests
- Optional persistent caching layer for production deployments
src/
config.js
scoring.js
server.js
services/
airnow.js
aviationWeather.js
geocode.js
usno.js
uv.js
weatherGov.js
ISC. See LICENSE.