{"openapi":"3.1.0","info":{"title":"weather.parkert.dev","version":"1.0.0","description":"A free, keyless public weather API. No account, payment, or API key required. Default fair-use limit: 60 requests/minute per client IP (IPv6 /64). Weather values use Celsius, metres/second, millimetres and pascals. Availability and freshness depend on upstream providers. Attribution is included in every data response. No emergency alert delivery guarantee.","license":{"name":"Source data licenses vary; see /api/v1/sources"},"contact":{"url":"https://gitlab.parkert.dev/parker/weather-api"}},"servers":[{"url":"https://weather.parkert.dev"},{"url":"http://localhost:3000","description":"Local development"}],"security":[],"paths":{"/api/v1/weather":{"get":{"summary":"Aggregate public forecasts and available NWS station observations","description":"Returns independent source records with attribution and provenance. NWS primarily covers the US; MET Norway provides global modeled forecasts. current is an actual station observation for NWS and always null for MET Norway. Forecasts are not blended. Partial source failures return HTTP 200 with meta.partial=true; all-source failures return 502 or 503. Null means unavailable. NWS wind ranges normalize to the upper end; precipitationMm represents the interval ending at endTime. Data is cached according to upstream cache headers; no stale-on-error fallback is returned.","parameters":[{"name":"lat","in":"query","required":true,"description":"Latitude in decimal degrees, truncated to four decimal places.","schema":{"type":"number","minimum":-90,"maximum":90},"example":44.9778},{"name":"lon","in":"query","required":true,"description":"Longitude in decimal degrees, truncated to four decimal places.","schema":{"type":"number","minimum":-180,"maximum":180},"example":-93.265},{"name":"sources","in":"query","required":false,"description":"Comma-separated unique source IDs. Defaults to both.","schema":{"type":"string","enum":["nws","metno","nws,metno","metno,nws"],"default":"nws,metno"}}],"responses":{"200":{"description":"At least one source returned weather data; inspect meta.errors and per-source warnings.","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WeatherResponse"}}}},"400":{"description":"Invalid, missing, repeated, or unsupported query parameter.","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Client rate limit exceeded (default 60 requests per minute).","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}},"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Seconds before retrying."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Every requested provider failed. The response retains location, data:[], meta.partial:true, and per-source errors in meta.errors.","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WeatherResponse"}}}},"503":{"description":"Every requested provider failed and upstream capacity was exhausted. The response retains location, data:[], and per-source errors in meta.errors.","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WeatherResponse"}}}}}}},"/api/v1/forecast":{"get":{"summary":"Alias for /api/v1/weather","description":"Returns independent source records with attribution and provenance. NWS primarily covers the US; MET Norway provides global modeled forecasts. current is an actual station observation for NWS and always null for MET Norway. Forecasts are not blended. Partial source failures return HTTP 200 with meta.partial=true; all-source failures return 502 or 503. Null means unavailable. NWS wind ranges normalize to the upper end; precipitationMm represents the interval ending at endTime. Data is cached according to upstream cache headers; no stale-on-error fallback is returned.","parameters":[{"name":"lat","in":"query","required":true,"description":"Latitude in decimal degrees, truncated to four decimal places.","schema":{"type":"number","minimum":-90,"maximum":90},"example":44.9778},{"name":"lon","in":"query","required":true,"description":"Longitude in decimal degrees, truncated to four decimal places.","schema":{"type":"number","minimum":-180,"maximum":180},"example":-93.265},{"name":"sources","in":"query","required":false,"description":"Comma-separated unique source IDs. Defaults to both.","schema":{"type":"string","enum":["nws","metno","nws,metno","metno,nws"],"default":"nws,metno"}}],"responses":{"200":{"description":"At least one source returned weather data; inspect meta.errors and per-source warnings.","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WeatherResponse"}}}},"400":{"description":"Invalid, missing, repeated, or unsupported query parameter.","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Client rate limit exceeded (default 60 requests per minute).","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}},"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Seconds before retrying."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Every requested provider failed. The response retains location, data:[], meta.partial:true, and per-source errors in meta.errors.","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WeatherResponse"}}}},"503":{"description":"Every requested provider failed and upstream capacity was exhausted. The response retains location, data:[], and per-source errors in meta.errors.","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WeatherResponse"}}}}}}},"/api/weather":{"get":{"summary":"Legacy path alias for /api/v1/weather","description":"Returns independent source records with attribution and provenance. NWS primarily covers the US; MET Norway provides global modeled forecasts. current is an actual station observation for NWS and always null for MET Norway. Forecasts are not blended. Partial source failures return HTTP 200 with meta.partial=true; all-source failures return 502 or 503. Null means unavailable. NWS wind ranges normalize to the upper end; precipitationMm represents the interval ending at endTime. Data is cached according to upstream cache headers; no stale-on-error fallback is returned.","parameters":[{"name":"lat","in":"query","required":true,"description":"Latitude in decimal degrees, truncated to four decimal places.","schema":{"type":"number","minimum":-90,"maximum":90},"example":44.9778},{"name":"lon","in":"query","required":true,"description":"Longitude in decimal degrees, truncated to four decimal places.","schema":{"type":"number","minimum":-180,"maximum":180},"example":-93.265},{"name":"sources","in":"query","required":false,"description":"Comma-separated unique source IDs. Defaults to both.","schema":{"type":"string","enum":["nws","metno","nws,metno","metno,nws"],"default":"nws,metno"}}],"responses":{"200":{"description":"At least one source returned weather data; inspect meta.errors and per-source warnings.","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WeatherResponse"}}}},"400":{"description":"Invalid, missing, repeated, or unsupported query parameter.","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Client rate limit exceeded (default 60 requests per minute).","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}},"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Seconds before retrying."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Every requested provider failed. The response retains location, data:[], meta.partial:true, and per-source errors in meta.errors.","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WeatherResponse"}}}},"503":{"description":"Every requested provider failed and upstream capacity was exhausted. The response retains location, data:[], and per-source errors in meta.errors.","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WeatherResponse"}}}}}}},"/api/v1/alerts":{"get":{"summary":"Active NWS alerts for a coordinate","description":"US NWS coverage. The service first verifies NWS point coverage; an unsupported location returns an error instead of an empty alerts list. Empty alerts means no active alerts were returned, not a guarantee of safety. Text is preserved from NWS.","parameters":[{"name":"lat","in":"query","required":true,"description":"Latitude in decimal degrees, truncated to four decimal places.","schema":{"type":"number","minimum":-90,"maximum":90},"example":44.9778},{"name":"lon","in":"query","required":true,"description":"Longitude in decimal degrees, truncated to four decimal places.","schema":{"type":"number","minimum":-180,"maximum":180},"example":-93.265}],"responses":{"200":{"description":"Active alerts with source attribution and timestamps.","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlertsResponse"}}}},"400":{"description":"Invalid, missing, repeated, or unsupported query parameter.","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Client rate limit exceeded (default 60 requests per minute).","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}},"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Seconds before retrying."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The upstream provider returned an error, timed out, or has no coverage for these coordinates. Inspect error.code (for example OUTSIDE_COVERAGE, UPSTREAM_TIMEOUT, or UPSTREAM_RATE_LIMITED).","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Global upstream capacity or the shared location search quota is exhausted.","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/search":{"get":{"summary":"Find up to five places by name","description":"Keyless Open-Meteo geocoding backed by GeoNames. Credit Open-Meteo and GeoNames, CC BY 4.0. The hosted geocoder is used within its noncommercial allowance and global request quotas; coordinate endpoints work independently of it.","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":2,"maxLength":100},"example":"Minneapolis"}],"responses":{"200":{"description":"Matching locations; an empty results array is a successful search with no matches.","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"400":{"description":"Invalid, missing, repeated, or unsupported query parameter.","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Client rate limit exceeded (default 60 requests per minute).","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}},"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Seconds before retrying."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The upstream provider returned an error, timed out, or has no coverage for these coordinates. Inspect error.code (for example OUTSIDE_COVERAGE, UPSTREAM_TIMEOUT, or UPSTREAM_RATE_LIMITED).","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Global upstream capacity or the shared location search quota is exhausted.","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/sources":{"get":{"summary":"Data providers, coverage, attribution, and licenses","responses":{"200":{"description":"Weather provider registry and location search provider.","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SourcesResponse"}}}},"429":{"description":"Client rate limit exceeded (default 60 requests per minute).","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}},"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Seconds before retrying."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/health":{"get":{"summary":"Service liveness (does not probe upstream providers)","responses":{"200":{"description":"Service is running.","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}},"429":{"description":"Client rate limit exceeded (default 60 requests per minute).","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}},"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Seconds before retrying."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/health":{"get":{"summary":"Alias for /api/v1/health","responses":{"200":{"description":"Service is running.","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}},"429":{"description":"Client rate limit exceeded (default 60 requests per minute).","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}},"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Seconds before retrying."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/openapi.json":{"get":{"summary":"This OpenAPI 3.1 document","responses":{"200":{"description":"OpenAPI specification.","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenApiDocument"}}}},"429":{"description":"Client rate limit exceeded (default 60 requests per minute).","headers":{"RateLimit-Limit":{"description":"Requests allowed per client IP per window (IPv6 /64 grouped).","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until this window resets.","schema":{"type":"integer","minimum":1}},"Retry-After":{"schema":{"type":"integer","minimum":1},"description":"Seconds before retrying."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}},"SourceError":{"type":"object","required":["source","code","message"],"properties":{"source":{"type":"string"},"code":{"type":"string"},"message":{"type":"string"}}},"Coordinates":{"type":"object","required":["lat","lon"],"properties":{"lat":{"type":"number","minimum":-90,"maximum":90},"lon":{"type":"number","minimum":-180,"maximum":180}}},"Meta":{"type":"object","required":["generatedAt","partial","errors","units"],"properties":{"generatedAt":{"type":"string","format":"date-time"},"partial":{"type":"boolean"},"errors":{"type":"array","items":{"$ref":"#/components/schemas/SourceError"}},"units":{"type":"object","required":["temperature","windSpeed","precipitation","pressure"],"properties":{"temperature":{"const":"celsius"},"windSpeed":{"const":"m/s"},"precipitation":{"const":"mm"},"pressure":{"const":"Pa"}}}}},"Observation":{"type":["object","null"],"required":["observedAt","station","temperatureC","relativeHumidityPercent","windSpeedMs","windDirectionDegrees","pressurePa","description"],"properties":{"observedAt":{"type":"string","format":"date-time"},"station":{"type":"string","format":"uri"},"temperatureC":{"type":["number","null"]},"relativeHumidityPercent":{"type":["number","null"]},"windSpeedMs":{"type":["number","null"]},"windDirectionDegrees":{"type":["number","null"]},"pressurePa":{"type":["number","null"]},"description":{"type":["string","null"]}}},"ForecastPeriod":{"type":"object","required":["time","endTime","temperatureC","windSpeedMs","windDirectionDegrees","precipitationMm","precipitationProbabilityPercent","summary","details"],"properties":{"time":{"type":"string","format":"date-time"},"endTime":{"type":["string","null"],"format":"date-time"},"temperatureC":{"type":["number","null"]},"windSpeedMs":{"type":["number","null"]},"windDirectionDegrees":{"type":["number","null"]},"precipitationMm":{"type":["number","null"]},"precipitationProbabilityPercent":{"type":["number","null"]},"summary":{"type":["string","null"]},"details":{"type":["string","null"]}}},"WeatherData":{"type":"object","required":["source","attribution","license","sourceUrl","licenseUrl","updatedAt","retrievedAt","provenance","current","forecast","warnings"],"properties":{"source":{"type":"string","enum":["nws","metno"]},"attribution":{"type":"string"},"license":{"type":"string"},"sourceUrl":{"type":"string","format":"uri"},"licenseUrl":{"type":"string","format":"uri"},"updatedAt":{"type":["string","null"],"format":"date-time"},"retrievedAt":{"type":"string","format":"date-time"},"provenance":{"type":"array","items":{"type":"string","format":"uri"}},"current":{"$ref":"#/components/schemas/Observation"},"forecast":{"type":"array","items":{"$ref":"#/components/schemas/ForecastPeriod"}},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/SourceError"}}}},"WeatherResponse":{"type":"object","required":["location","data","meta"],"properties":{"location":{"type":"object","required":["lat","lon"],"properties":{"lat":{"type":"number","minimum":-90,"maximum":90},"lon":{"type":"number","minimum":-180,"maximum":180}}},"meta":{"$ref":"#/components/schemas/Meta"},"data":{"type":"array","items":{"$ref":"#/components/schemas/WeatherData"}}}},"Alert":{"type":"object","required":["id","event","severity","certainty","urgency","headline","description","instruction","area","effective","onset","expires","ends"],"properties":{"id":{"type":["string","null"]},"event":{"type":["string","null"]},"severity":{"type":["string","null"]},"certainty":{"type":["string","null"]},"urgency":{"type":["string","null"]},"headline":{"type":["string","null"]},"description":{"type":["string","null"]},"instruction":{"type":["string","null"]},"area":{"type":["string","null"]},"effective":{"type":["string","null"],"format":"date-time"},"onset":{"type":["string","null"],"format":"date-time"},"expires":{"type":["string","null"],"format":"date-time"},"ends":{"type":["string","null"],"format":"date-time"}}},"AlertsResponse":{"type":"object","required":["location","data","meta"],"properties":{"location":{"type":"object","required":["lat","lon"],"properties":{"lat":{"type":"number","minimum":-90,"maximum":90},"lon":{"type":"number","minimum":-180,"maximum":180}}},"meta":{"$ref":"#/components/schemas/Meta"},"data":{"type":"array","items":{"type":"object","required":["source","attribution","license","sourceUrl","licenseUrl","retrievedAt","provenance","alerts"],"properties":{"source":{"const":"nws"},"attribution":{"type":"string"},"license":{"type":"string"},"sourceUrl":{"type":"string","format":"uri"},"licenseUrl":{"type":"string","format":"uri"},"retrievedAt":{"type":"string","format":"date-time"},"provenance":{"type":"array","items":{"type":"string","format":"uri"}},"alerts":{"type":"array","items":{"$ref":"#/components/schemas/Alert"}}}}}}},"SearchResponse":{"type":"object","required":["query","results","meta"],"properties":{"query":{"type":"string"},"meta":{"type":"object","required":["generatedAt","attribution","licenseUrl","sourceUrls"],"properties":{"generatedAt":{"type":"string","format":"date-time"},"attribution":{"type":"string"},"licenseUrl":{"type":"string","format":"uri"},"sourceUrls":{"type":"array","items":{"type":"string","format":"uri"}}}},"results":{"type":"array","maxItems":5,"items":{"type":"object","required":["id","name","label","lat","lon","country","region","source"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"label":{"type":"string"},"lat":{"type":"number","minimum":-90,"maximum":90},"lon":{"type":"number","minimum":-180,"maximum":180},"country":{"type":["string","null"]},"region":{"type":["string","null"]},"source":{"const":"open-meteo"}}}}}},"Source":{"type":"object","required":["id","name","coverage","capabilities","license","attribution","url","licenseUrl"],"properties":{"id":{"type":"string","enum":["nws","metno"]},"name":{"type":"string"},"coverage":{"type":"string"},"capabilities":{"type":"array","items":{"type":"string","enum":["forecast","observations","alerts"]}},"license":{"type":"string"},"attribution":{"type":"string"},"url":{"type":"string","format":"uri"},"licenseUrl":{"type":"string","format":"uri"}}},"SourcesResponse":{"type":"object","required":["sources","search"],"properties":{"sources":{"type":"array","items":{"$ref":"#/components/schemas/Source"}},"search":{"type":"object","required":["id","name","attribution","license","url","licenseUrl","usage"],"properties":{"id":{"const":"open-meteo"},"name":{"type":"string"},"attribution":{"type":"string"},"license":{"type":"string"},"url":{"type":"string","format":"uri"},"licenseUrl":{"type":"string","format":"uri"},"usage":{"type":"string"}}}}},"Health":{"type":"object","required":["status","service","version","time"],"properties":{"status":{"const":"ok"},"service":{"const":"weather.parkert.dev"},"version":{"type":"string"},"time":{"type":"string","format":"date-time"}}},"OpenApiDocument":{"type":"object","required":["openapi","info","paths"],"properties":{"openapi":{"const":"3.1.0"},"info":{"type":"object"},"paths":{"type":"object"}}}}}}