Weatherkind API
One base URL, JSON in, weather out. Everything on weatherkind.com is served by the same endpoints documented here, including forecasts, historical observations, nowcasts, alerts, accuracy, and climate.
https://api.weatherkind.comAuthorization: Bearer {api_key}application/json · ISO-8601v1 · additive changes onlyOverview
The API is a small family of endpoints around one idea: field-selectable weather. You state a coordinate, the forecast sections you want (from current conditions to daily summaries), and the exact variables in each section, and the response contains precisely that, nothing else.
Every JSON endpoint below has a live console: build the call
with real parameters, copy it as curl, and inspect the returned JSON
or visualization. Console calls run through this site's demo proxy.
Your own integration uses your API key against
api.weatherkind.com. Map tiles include a copyable
integration example.
Data endpoints return their result directly; there is no
data envelope. Every response carries
updated_at, a stale flag (true
only when a cached payload is served during upstream degradation),
and a warnings array for non-fatal notices.
POST /v1/forecast: named sections: current · quarter_hourly · hourly · dailyPOST /v1/nowcast: fixed two-hour, minute-by-minute precipitation nowcastPOST /v1/outlook: daily climate outlook, days 15–365POST /v1/historical: field-selectable daily observations for completed local datesGET /v1/historical/variables: variables, units, coverage limits, and methodology (public)POST /v1/alerts: CAP-aligned active public alertsPOST /v1/elevation: ground elevation & terrain profilePOST /v1/accuracy: our verified error record, by locality, region, or networkGET /v1/accuracy/catalog: regions, leads, variables, metrics, methodology (public)GET /v1/map-tiles/{layer}/{z}/{x}/{y}: radar & environment rasters
Authentication
Every request carries a bearer token. Keys are scoped to a plan (free tier available) and work across all endpoints your plan includes.
Authorization: Bearer wk_live_…
Keys carry scopes: forecast:read covers forecast,
outlook, and elevation; nowcast:read covers the Pro+
minute nowcast; alerts:read covers alerts; and
accuracy:read covers /v1/accuracy.
historical:read covers
/v1/historical.
Calling an endpoint your key doesn't cover returns 403.
Three catalog endpoints need no key at all:
GET /v1/forecast/variables and
GET /v1/accuracy/catalog, and
GET /v1/historical/variables.
Keys are secrets: call the API from your backend or an edge proxy, not from browser JavaScript, and rotate any key that ships in a client binary.
Quick start
Current conditions for Aspen, Colorado, in one call:
Response
{
"location": {
"latitude": 39.1911,
"longitude": -106.8175,
"timezone": "America/Denver",
"forecast_elevation_m": 2422
},
"units": { "system": "us", "elevation": "m", "pressure": "hPa" },
"current": {
"time": "2026-07-30T17:04:00Z",
"temperature": 78.3,
"condition": "partly",
"wind_speed": 6.8
},
"updated_at": "2026-07-30T17:04:11Z",
"stale": false,
"warnings": []
} POST /v1/forecast
Returns the operational forecast from right now through the next two weeks. One request can combine current conditions, 15-minute or hourly detail, and daily summaries, with only the variables you select. Use this endpoint when you need specific near-term conditions or weather values for a particular time.
| Field | Presence | Type | Notes |
|---|---|---|---|
location.latitude | required | number | −90…90 |
location.longitude | required | number | −180…180 |
timezone | optional | string | "auto" by default, or an IANA zone |
units | optional | string | Defaults to "metric" (°C, km/h, mm, hPa, m); also accepts "us" or "si" |
unit_overrides | optional | object | Override dimensions such as pressure or elevation |
current | optional | object | variables to return in one flat object at request time |
quarter_hourly | optional | object | hours (1–48) and variables |
hourly | optional | object | hours (1–384), optional past_hours (0–48), and variables |
daily | optional | object | days (1–16), or exact inclusive start/end dates, plus variables |
| Field | Presence | Type | Notes |
|---|---|---|---|
location | always | object | Resolved forecast location. |
location.latitude | always | number | Latitude used by the forecast. |
location.longitude | always | number | Longitude used by the forecast. |
location.timezone | always | string | Resolved IANA time zone. |
location.forecast_elevation_m | nullable | number | null | Representative elevation used by the forecast, in meters. |
units | always | object | Units applied to every returned variable. |
current | conditional | object | Flat current values when requested. |
quarter_hourly | conditional | object | Resolved range plus flat 15-minute entries when requested. |
hourly | conditional | object | Resolved range plus flat hourly entries when requested. |
daily | conditional | object | Resolved range plus flat daily entries when requested. |
*.range | conditional | object | The window the server resolved. Timestamp ranges are start-inclusive and end-exclusive; daily dates include both start and end. |
*.entries[] | conditional | array | Flat records for the section. |
entries[].time | conditional | ISO datetime | UTC instant on subdaily records. |
daily.entries[].date | always | local date | Local calendar date. |
updated_at | nullable | ISO datetime | null | Generation time of the returned data; null when unknown. |
stale | always | boolean | True only when resilient cached data was served. |
warnings[] | always | array | Non-fatal availability or substitution notices. |
Interactive example
curl 'https://api.weatherkind.com/v1/forecast' \
-H "Authorization: Bearer $WEATHERKIND_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"location": {
"latitude": 39.1911,
"longitude": -106.8175
},
"timezone": "auto",
"units": "us",
"current": {
"variables": [
"temperature",
"condition",
"wind_speed"
]
},
"hourly": {
"hours": 48,
"variables": [
"temperature",
"condition",
"wind_speed",
"precipitation",
"precipitation_probability"
]
},
"daily": {
"days": 14,
"variables": [
"temperature_max",
"temperature_min",
"condition",
"precipitation",
"precipitation_probability"
]
}
}'{
"location": {
"latitude": 39.1911,
"longitude": -106.8175,
"timezone": "America/Denver",
"forecast_elevation_m": 2422
},
"units": {
"system": "us",
"elevation": "m",
"pressure": "hPa"
},
"current": {
"time": "2026-07-30T17:04:00Z",
"temperature": 78.3,
"condition": "partly",
"wind_speed": 6.8,
"uv_index": 7
},
"hourly": {
"range": {
"start": "2026-07-30T17:00:00Z",
"end": "2026-07-31T17:00:00Z"
},
"entries": [
{
"time": "2026-07-30T17:00:00Z",
"temperature": 78.3,
"precipitation_probability": 12,
"wind_gust": 14.2
},
{
"time": "2026-07-30T18:00:00Z",
"temperature": 76.9,
"precipitation_probability": 15,
"wind_gust": 16.1
}
]
},
"daily": {
"range": {
"start": "2026-07-30",
"end": "2026-08-12"
},
"entries": [
{
"date": "2026-07-30",
"temperature_max": 81,
"temperature_min": 48,
"condition": "partly",
"sunrise": "2026-07-30T05:58:00-06:00",
"sunset": "2026-07-30T20:27:00-06:00"
},
{
"date": "2026-07-31",
"temperature_max": 79,
"temperature_min": 47,
"condition": "thunderstorm",
"sunrise": "2026-07-31T05:59:00-06:00",
"sunset": "2026-07-31T20:26:00-06:00"
}
]
},
"updated_at": "2026-07-30T17:04:11Z",
"stale": false,
"warnings": []
}- temperature78.3
- conditionpartly
- wind_speed6.8
- uv_index7
condition partly · thunderstormsunrise 2026-07-30T05:58:00-06:00 · 2026-07-31T05:59:00-06:00sunset 2026-07-30T20:27:00-06:00 · 2026-07-31T20:26:00-06:00| Status | Meaning |
|---|---|
422 | Validation failed: a field is missing, malformed, or out of range. The problem response says which. |
401 | Missing or invalid API key. |
403 | The key is valid but the plan doesn't include this endpoint (e.g. map tiles on Free). |
429 | Rate limit exceeded. Honor the Retry-After header before retrying. |
5xx | Something failed on our side. Retry with backoff; responses may serve from cache with stale: true while we recover. |
A mismatched variable or invalid section returns 422 with the exact field path in the problem response.
POST /v1/nowcast
Returns the operational precipitation nowcast in one fixed shape: one-minute entries for the next two hours. It is refreshed on the radar cadence and cached independently from the broader forecast, so clients can poll it frequently without recomputing or transferring current, hourly, and daily sections.
Poll according to Retry-After when present. A
pending status means the first radar-backed result is
still warming; dry is a valid result with no active precipitation.
| Field | Presence | Type | Notes |
|---|---|---|---|
location.latitude | required | number | −90…90 |
location.longitude | required | number | −180…180 |
timezone | optional | string | "auto" by default, or an IANA time zone |
units | optional | string | Defaults to "metric" (°C, km/h, mm, hPa, m); also accepts "us" or "si" |
variables | optional | string[] | Minute variables to return; defaults to every nowcast variable |
| Field | Presence | Type | Notes |
|---|---|---|---|
location | always | object | Resolved location, time zone, and forecast elevation. |
location.forecast_elevation_m | nullable | number | null | Representative elevation used by the nowcast, in meters. |
units | always | object | Units applied to precipitation amounts, rates, and hail size. |
status | always | enum | active, dry, or pending. |
start | always | ISO datetime | Start of the fixed two-hour window. |
end | always | ISO datetime | Exclusive end of the fixed two-hour window. |
minutes[] | always | array | Up to 120 flat one-minute records containing the selected variables. |
minutes[].time | always | ISO datetime | UTC instant for this minute. |
updated_at | nullable | ISO datetime | null | Generation time of the nowcast; null when unknown. |
stale | always | boolean | True only when resilient cached data was served. |
radar_pending | always | boolean | True while the first radar-backed result is warming. |
onset_uncertainty_minutes | nullable | number | null | Estimated uncertainty around precipitation onset timing. |
warnings[] | always | array | Non-fatal availability notices. |
Interactive example
curl 'https://api.weatherkind.com/v1/nowcast' \
-H "Authorization: Bearer $WEATHERKIND_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"location": {
"latitude": 39.1911,
"longitude": -106.8175
},
"timezone": "auto",
"units": "us"
}'{
"location": {
"latitude": 39.1911,
"longitude": -106.8175,
"timezone": "America/Denver"
},
"units": {
"system": "us",
"precipitation": "in",
"precipitation_rate": "in/h"
},
"status": "dry",
"start": "2026-07-30T17:04:00Z",
"end": "2026-07-30T19:04:00Z",
"minutes": [],
"updated_at": "2026-07-30T17:04:11Z",
"stale": false,
"radar_pending": false,
"onset_uncertainty_minutes": null,
"warnings": []
}Dry for the next two hours at this location.
| Status | Meaning |
|---|---|
422 | Validation failed: a field is missing, malformed, or out of range. The problem response says which. |
401 | Missing or invalid API key. |
403 | The key is valid but the plan doesn't include this endpoint (e.g. map tiles on Free). |
429 | Rate limit exceeded. Honor the Retry-After header before retrying. |
5xx | Something failed on our side. Retry with backoff; responses may serve from cache with stale: true while we recover. |
POST /v1/outlook
Returns a long-range daily outlook for local dates 15 to 365 days
ahead. It is designed for seasonal planning and broad temperature
or precipitation tendencies, not precise event timing. Uncertainty
is higher than /v1/forecast, so use the forecast for
the next two weeks and the outlook for the period beyond it.
| Field | Presence | Type | Notes |
|---|---|---|---|
location.latitude | required | number | −90…90 |
location.longitude | required | number | −180…180 |
units | optional | string | Defaults to "metric" (°C and mm); also accepts "us" or "si" |
start | required | local date | First date returned, 15–365 days ahead |
end | required | local date | Last date returned, inclusive |
variables | required | string[] | Daily temperature and precipitation variables |
| Field | Presence | Type | Notes |
|---|---|---|---|
location | always | object | Requested coordinates and resolved time zone. |
location.forecast_elevation_m | nullable | number | null | Representative elevation used by the outlook, in meters. |
units | always | object | Units applied to outlook variables. |
daily | always | object | Resolved inclusive date range plus flat daily outlook entries. |
daily.range | always | object | Resolved local dates; start and end are both inclusive. |
daily.entries[] | always | array | Flat daily outlook records. |
entries[].date | always | local date | Calendar date in the location's time zone. |
updated_at | nullable | ISO datetime | null | Time the outlook was generated; null when unknown. |
stale | always | boolean | Whether resilient cached data was served. |
warnings[] | always | array | Non-fatal notices. |
Interactive example
curl 'https://api.weatherkind.com/v1/outlook' \
-H "Authorization: Bearer $WEATHERKIND_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"location": {
"latitude": 39.1911,
"longitude": -106.8175
},
"units": "us",
"start": "2026-08-15",
"end": "2026-11-13",
"variables": [
"temperature_max",
"temperature_min",
"precipitation"
]
}'{
"location": {
"latitude": 39.1911,
"longitude": -106.8175,
"timezone": "America/Denver",
"forecast_elevation_m": 2422
},
"units": {
"system": "us",
"temperature": "degF",
"precipitation": "in"
},
"daily": {
"range": {
"start": "2026-08-14",
"end": "2026-10-01"
},
"entries": [
{
"date": "2026-08-14",
"temperature_max": 78,
"temperature_min": 46,
"precipitation": 0.04
},
{
"date": "2026-08-15",
"temperature_max": 76,
"temperature_min": 45,
"precipitation": 0.12
}
]
},
"updated_at": "2026-07-30T17:04:11Z",
"stale": false,
"warnings": []
}| Status | Meaning |
|---|---|
422 | Validation failed: a field is missing, malformed, or out of range. The problem response says which. |
401 | Missing or invalid API key. |
403 | The key is valid but the plan doesn't include this endpoint (e.g. map tiles on Free). |
429 | Rate limit exceeded. Honor the Retry-After header before retrying. |
5xx | Something failed on our side. Retry with backoff; responses may serve from cache with stale: true while we recover. |
Dates before day 15 or after day 365 return 422.
POST /v1/historical
Returns field-selectable daily weather observations for completed
local dates. Use it for backtesting, event reconstruction, and
observed weather history—not forecast-performance metrics, which
come from /v1/accuracy.
Coverage depends on the location, date, and variable; missing
observations remain in the response as null with
structured warnings.
| Field | Presence | Type | Notes |
|---|---|---|---|
location.latitude | required | number | −90…90 |
location.longitude | required | number | −180…180 |
timezone | optional | string | "auto" by default, or an IANA zone; defines local calendar days |
units | optional | string | Defaults to "metric" (°C and mm); also accepts "us" or "si". Use unit_overrides for individual dimensions |
start | required | local date | First completed local date, inclusive |
end | required | local date | Last completed local date, inclusive; the range may span up to 50 calendar years |
variables | required | string[] | One or more historical variables; at most 25,000 scalar values |
include | optional | string[] | "stations" and/or "sources" adds observation provenance |
Requests containing air_quality_index may span at
most 366 days. Other variables may span up to 50 calendar years,
subject to the 25,000-value request limit.
| Field | Presence | Type | Notes |
|---|---|---|---|
location | always | object | Requested coordinates and resolved time zone. |
units | always | object | Temperature and precipitation units applied to returned observations. |
start | always | local date | First requested date, inclusive. |
end | always | local date | Last requested date, inclusive. |
daily[] | always | array | Flat daily observations; unavailable values are null. |
sources[] | conditional | array | Observation and analysis provenance when sources is included. |
stations[] | conditional | array | Contributing station metadata when stations is included. |
updated_at | nullable | ISO datetime | null | Generation time of the returned observations; null when unknown. |
stale | always | boolean | Whether resilient cached data was served. |
warnings[] | always | array | Structured coverage, modeled-AQI, and stale-data notices. |
Interactive example
curl 'https://api.weatherkind.com/v1/historical' \
-H "Authorization: Bearer $WEATHERKIND_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"location": {
"latitude": 39.1911,
"longitude": -106.8175
},
"timezone": "auto",
"units": "us",
"start": "2025-06-01",
"end": "2025-07-01",
"variables": [
"temperature_max",
"temperature_min",
"precipitation",
"rain",
"snow"
],
"include": [
"stations",
"sources"
]
}'{
"location": {
"latitude": 39.1911,
"longitude": -106.8175,
"timezone": "America/Denver"
},
"units": {
"system": "us",
"temperature": "degF",
"precipitation": "in"
},
"start": "2025-06-01",
"end": "2025-07-01",
"daily": [],
"sources": [],
"stations": [],
"updated_at": "2026-07-30T17:04:11Z",
"stale": false,
"warnings": []
}| Status | Meaning |
|---|---|
401 | Missing or invalid API key. |
403 | The key does not include historical:read. |
422 | Invalid coordinates, dates, variables, range size, or unknown request fields. The RFC 9457 response identifies each field with errors[].path and errors[].code. |
429 | Rate limit exceeded. Honor Retry-After. |
503 | Historical providers are temporarily unavailable. Retry after the supplied Retry-After interval. |
POST /v1/alerts
Returns active public weather alerts that apply to the requested
coordinate. Each alert includes CAP-aligned severity, urgency,
certainty, onset and expiration times, instructions, and the
issuing authority; request geometry when you also need the affected
polygon. A successful response with an empty alerts
array means no public alert is active at that point.
| Field | Presence | Type | Notes |
|---|---|---|---|
location.latitude | required | number | −90…90 |
location.longitude | required | number | −180…180 |
include | optional | string[] | Add "geometry" to return the GeoJSON polygon |
| Field | Presence | Type | Notes |
|---|---|---|---|
location | always | object | Coordinates evaluated for active alerts. |
alerts[] | always | array | Active public alerts; empty when none are active. |
alerts[].id | always | string | Stable issuer alert identifier. |
alerts[].event | always | string | Alert event name. |
alerts[].headline | always | string | Short human-readable alert summary. |
alerts[].description | always | string | Detailed hazard description. |
alerts[].instruction | nullable | string | null | Recommended protective action when supplied. |
alerts[].severity | always | enum | extreme, severe, moderate, minor, or unknown. |
alerts[].urgency | always | enum | CAP-aligned urgency classification. |
alerts[].certainty | always | enum | CAP-aligned certainty classification. |
alerts[].onset_at | always | ISO datetime | Expected hazard onset. |
alerts[].expires_at | always | ISO datetime | Alert expiration time. |
alerts[].issuer | always | object | Issuing authority. |
alerts[].affected_areas[] | always | array | Issuer-provided area descriptions. |
alerts[].geometry | conditional | GeoJSON | null | Alert polygon when geometry was requested and available. |
updated_at | always | ISO datetime | Time the response was produced. |
stale | always | boolean | Whether cached data was served. |
warnings[] | always | array | Non-fatal notices. |
Interactive example
curl 'https://api.weatherkind.com/v1/alerts' \
-H "Authorization: Bearer $WEATHERKIND_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"location": {
"latitude": 39.1911,
"longitude": -106.8175
}
}'{
"meta": {
"stale": false,
"generated_at": "2026-07-31T23:15:24Z"
},
"location": {
"latitude": 39.1911,
"longitude": -106.8175
},
"warnings": [],
"alerts": []
}No active alerts at this point right now. Quiet skies. Try coordinates under a storm.
| Status | Meaning |
|---|---|
422 | Validation failed: a field is missing, malformed, or out of range. The problem response says which. |
401 | Missing or invalid API key. |
403 | The key is valid but the plan doesn't include this endpoint (e.g. map tiles on Free). |
429 | Rate limit exceeded. Honor the Retry-After header before retrying. |
5xx | Something failed on our side. Retry with backoff; responses may serve from cache with stale: true while we recover. |
POST /v1/elevation
Returns the canonical elevation of the immutable forecast region containing the coordinate, along with that region's minimum and maximum terrain elevations. Optionally include its build-time valley, mid-slope, and peak forecast-region anchors. To request a summit or slope forecast, use the returned representative coordinate; no runtime DEM search or elevation parameter is needed.
| Field | Presence | Type | Notes |
|---|---|---|---|
location.latitude | required | number | −90…90 |
location.longitude | required | number | −180…180 |
units | optional | string | Defaults to "metric" (m); also accepts "us" or "si" |
include | optional | string[] | Add "terrain_profile" for valley / mid / peak levels |
| Field | Presence | Type | Notes |
|---|---|---|---|
location | always | object | Requested coordinates resolved to an immutable forecast region. |
forecast_region | always | object | Containing forecast-region identity, name, and terrain class. |
units | always | object | Elevation and distance units. |
elevation.ground | always | number | Canonical forecast elevation shared by the region. |
elevation.forecast | always | number | Canonical elevation used to generate every forecast in the region. |
elevation.minimum | always | number | Minimum DEM elevation observed while building the region. |
elevation.maximum | always | number | Maximum DEM elevation observed while building the region. |
terrain_profile | conditional | object | null | Terrain context when terrain_profile was requested. |
terrain_profile.classification | conditional | enum | Terrain-relief classification. |
terrain_profile.relief | conditional | number | Local peak-to-valley relief. |
terrain_profile.matched_level | conditional | string | Static tier closest to the containing region's canonical elevation. |
terrain_profile.levels[] | conditional | array | Build-time valley, mid, and peak forecast-region representatives. |
levels[].id | conditional | enum | valley, mid, or peak. |
levels[].elevation | conditional | number | Representative level elevation. |
levels[].representative_location | conditional | object | Coordinates representing this terrain level. |
levels[].distance | nullable | number | null | Distance from the requested point. |
levels[].synthetic | conditional | boolean | False for catalog-defined forecast-region representatives. |
updated_at | always | ISO datetime | Time the response was produced. |
stale | always | boolean | Whether cached data was served. |
warnings[] | always | array | Non-fatal notices. |
Interactive example
curl 'https://api.weatherkind.com/v1/elevation' \
-H "Authorization: Bearer $WEATHERKIND_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"location": {
"latitude": 39.1911,
"longitude": -106.8175
},
"units": "metric",
"include": [
"terrain_profile"
]
}'{
"location": {
"latitude": 39.1911,
"longitude": -106.8175
},
"units": {
"system": "metric",
"elevation": "m",
"distance": "km"
},
"elevation": {
"ground": 2422
},
"terrain_profile": {
"classification": "mountainous",
"relief": 1533,
"matched_level": "valley",
"levels": [
{
"id": "valley",
"elevation": 2422,
"representative_location": {
"latitude": 39.1911,
"longitude": -106.8175
},
"distance": 0,
"synthetic": false
},
{
"id": "mid",
"elevation": 3190,
"representative_location": {
"latitude": 39.1842,
"longitude": -106.8111
},
"distance": 2.9,
"synthetic": false
},
{
"id": "peak",
"elevation": 3955,
"representative_location": {
"latitude": 39.1604,
"longitude": -106.7938
},
"distance": 5.8,
"synthetic": false
}
]
},
"updated_at": "2026-07-30T17:04:11Z",
"stale": false,
"warnings": []
}- ground 2422 m
- peak 3955 m
- mid 3190 m
- valley 2422 m
| Status | Meaning |
|---|---|
422 | Validation failed: a field is missing, malformed, or out of range. The problem response says which. |
401 | Missing or invalid API key. |
403 | The key is valid but the plan doesn't include this endpoint (e.g. map tiles on Free). |
429 | Rate limit exceeded. Honor the Retry-After header before retrying. |
5xx | Something failed on our side. Retry with backoff; responses may serve from cache with stale: true while we recover. |
POST /v1/accuracy
Returns historical forecast-performance statistics, not current
weather. Query a coordinate, weather region, or the full verified
network to see error and skill by interval and lead time, scored
against quality-controlled station observations. Use it to
understand expected uncertainty, compare Weatherkind with a
persistence baseline, and qualify decisions made from
/v1/forecast.
| Field | Presence | Type | Notes |
|---|---|---|---|
scope | required | object | local with location, region with id, or network |
days | optional | integer | Trailing window, 7–365; default 30 |
units | optional | string | Defaults to "metric" (°C and mm); also accepts "us" or "si". Dimension overrides are supported |
minutely | optional | object | lead_minutes and variables |
hourly | optional | object | lead_hours and variables |
daily | optional | object | lead_days and variables |
compare_to | optional | string[] | ["persistence"] adds baseline comparisons and skill scores |
include | optional | string[] | ["stations"] adds verifying stations for local scope |
Sections, leads & variables
| Section | Default leads | Variables |
|---|---|---|
1m | 5, 15, 30, 60, 90, 115 min | precipitation, precipitation_probability |
1h | 0, 1, 2, 3, 6, 12, 24 h | temperature, precipitation fields |
1d | 1–14 days | daily temperature and precipitation fields |
| Field | Presence | Type | Notes |
|---|---|---|---|
scope | always | object | Resolved local, region, or network evaluation scope. |
evaluation_period | always | object | Evaluated dates, data cutoff, and window length. |
coverage.available | always | boolean | Whether enough verified observations exist. |
coverage.basis | always | string | Observation basis used for verification. |
coverage.radius_km | conditional | number | Local verification radius. |
coverage.verified_areas | always | integer | Number of verified areas contributing samples. |
units | always | object | Units applied to error metrics. |
minutely[] | conditional | array | Minute forecast accuracy by lead_minutes. |
hourly[] | conditional | array | Hourly forecast accuracy by lead_hours. |
daily[] | conditional | array | Daily forecast accuracy by lead_days. |
*.mean_absolute_error | nullable | number | null | Mean absolute error when the metric applies. |
*.bias | nullable | number | null | Signed average error. |
*.root_mean_square_error | nullable | number | null | Root mean square error. |
*.brier_score | nullable | number | null | Probability calibration score. |
*.samples | always | integer | Verified sample count behind the metric. |
*.comparisons | conditional | object | Requested baselines and skill scores. |
stations[] | conditional | array | Contributing stations when requested for local scope. |
updated_at | always | ISO datetime | Time the response was produced. |
stale | always | boolean | Whether cached data was served. |
warnings[] | always | array | Non-fatal notices. |
Interactive example
curl 'https://api.weatherkind.com/v1/accuracy' \
-H "Authorization: Bearer $WEATHERKIND_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"scope": {
"type": "local",
"location": {
"latitude": 39.1911,
"longitude": -106.8175
}
},
"days": 30,
"units": "us",
"hourly": {
"lead_hours": [
1,
6,
12,
24
],
"variables": [
"temperature",
"precipitation_probability"
]
},
"daily": {
"variables": [
"temperature_max",
"temperature_min"
]
},
"compare_to": [
"persistence"
],
"include": [
"stations"
]
}'{
"scope": {
"type": "local",
"location": {
"latitude": 39.1911,
"longitude": -106.8175
}
},
"evaluation_period": {
"start": "2026-06-30",
"end": "2026-07-30",
"data_through": "2026-07-29",
"days": 30
},
"coverage": {
"available": true,
"basis": "nearby_verified_areas",
"radius_km": 16.1,
"verified_areas": 18
},
"units": {
"system": "us",
"temperature": "degF",
"precipitation": "in"
},
"hourly": [
{
"lead_hours": 1,
"temperature": {
"mean_absolute_error": 1.8,
"bias": -0.2,
"root_mean_square_error": 2.4,
"samples": 2160,
"comparisons": {
"persistence": {
"mean_absolute_error": 3.1,
"samples": 2160,
"skill_score": 0.42
}
}
},
"precipitation_probability": {
"brier_score": 0.08,
"bias": 0.01,
"samples": 2160
}
}
],
"daily": [
{
"lead_days": 1,
"temperature_max": {
"mean_absolute_error": 2,
"bias": -0.2,
"root_mean_square_error": 3.1,
"samples": 114
},
"temperature_min": {
"mean_absolute_error": 2.5,
"bias": -0.8,
"root_mean_square_error": 3.9,
"samples": 114
}
}
],
"stations": [
{
"id": "KASE",
"name": "Aspen-Pitkin Airport",
"source": "nws",
"latitude": 39.2232,
"longitude": -106.8687,
"distance_miles": 3.5,
"elevation_ft": 7815
}
],
"updated_at": "2026-07-30T17:04:11Z",
"stale": false,
"warnings": []
}hourly temperature · mean absolute error by lead · 30-day window through 2026-07-29
- +1 h ±1.8° 42% better
Weatherkind persistence baseline
| Status | Meaning |
|---|---|
422 | Validation failed: a field is missing, malformed, or out of range. The problem response says which. |
401 | Missing or invalid API key. |
403 | The key is valid but the plan doesn't include this endpoint (e.g. map tiles on Free). |
429 | Rate limit exceeded. Honor the Retry-After header before retrying. |
5xx | Something failed on our side. Retry with backoff; responses may serve from cache with stale: true while we recover. |
Insufficient verified coverage is returned as a successful response with coverage.available: false, not as an error.
GET /v1/map-tiles/{layer}/{z}/{x}/{y}
Returns a 256 by 256 PNG raster tile for an XYZ map coordinate,
rather than a JSON document. Use these tiles as visual overlays in
MapLibre, Leaflet, or MapKit for radar, temperature, air quality,
smoke, and severe-weather layers. Tiles are cacheable and intended
for map rendering; query /v1/forecast when you need
numeric weather values instead.
| Field | Presence | Type | Notes |
|---|---|---|---|
layer | required | string | Radar or environmental layer id |
z | required | integer | XYZ zoom level |
x | required | integer | XYZ horizontal tile coordinate |
y | required | integer | XYZ vertical tile coordinate |
request body | none | none | This endpoint is a GET |
| Field | Presence | Type | Notes |
|---|---|---|---|
HTTP status | always | 200 | Successful tile response. |
Content-Type | always | image/png | Raster PNG payload. |
Cache-Control | always | header | Public five-minute cache policy. |
body | always | binary | 256 by 256 pixel tile bytes, not JSON. |
Use this endpoint
Request
curl 'https://api.weatherkind.com/v1/map-tiles/radar/7/26/49' \
-H "Authorization: Bearer $WEATHERKIND_API_KEY" \
--output 'radar.png'const response = await fetch("https://api.weatherkind.com/v1/map-tiles/radar/7/26/49", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.WEATHERKIND_API_KEY}`,
},
});
if (!response.ok) {
throw new Error(`Weatherkind returned ${response.status}`);
}
const bytes = await response.arrayBuffer();
console.log(`Received ${bytes.byteLength} bytes`);import os
import requests
response = requests.get(
"https://api.weatherkind.com/v1/map-tiles/radar/7/26/49",
headers={
"Authorization": f"Bearer {os.environ['WEATHERKIND_API_KEY']}"
},
timeout=30,
)
response.raise_for_status()
with open("radar.png", "wb") as file:
file.write(response.content)package main
import (
"io"
"log"
"net/http"
"os"
)
func main() {
var body io.Reader
req, err := http.NewRequest("GET", "https://api.weatherkind.com/v1/map-tiles/radar/7/26/49", body)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("WEATHERKIND_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
log.Fatalf("Weatherkind returned %s", resp.Status)
}
bytes, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
if err := os.WriteFile("radar.png", bytes, 0o644); err != nil {
log.Fatal(err)
}
}response =
Req.get!(
"https://api.weatherkind.com/v1/map-tiles/radar/7/26/49",
auth: {:bearer, System.fetch_env!("WEATHERKIND_API_KEY")}
)
File.write!("radar.png", response.body)require "json"
require "net/http"
uri = URI("https://api.weatherkind.com/v1/map-tiles/radar/7/26/49")
request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch("WEATHERKIND_API_KEY")}"
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
raise "Weatherkind returned #{response.code}" unless response.is_a?(Net::HTTPSuccess)
File.binwrite("radar.png", response.body) | Status | Meaning |
|---|---|
401 | Missing or invalid API key. |
403 | The key is valid but the plan doesn't include this endpoint (e.g. map tiles on Free). |
404 | Unknown path or tile coordinates. |
429 | Rate limit exceeded. Honor the Retry-After header before retrying. |
5xx | Something failed on our side. Retry with backoff; responses may serve from cache with stale: true while we recover. |
Variables
Which variables each forecast section accepts. Requesting a variable outside
its supported section is a 422; the authoritative live list is
GET /v1/forecast/variables.
| Variable | current | 15m | 1h | 1d |
|---|---|---|---|---|
temperature | ● | ● | ● | – |
temperature_max | – | – | – | ● |
temperature_min | – | – | – | ● |
apparent_temperature | ● | ● | ● | – |
condition | ● | ● | ● | ● |
precipitation | – | ● | ● | ● |
precipitation_probability | – | ● | ● | ● |
rain | – | ● | ● | ● |
snow | – | ● | ● | ● |
wind_speed | ● | ● | ● | – |
wind_gust | – | – | ● | – |
wind_direction | – | ● | ● | – |
humidity | ● | – | ● | – |
dew_point | ● | – | ● | – |
pressure | ● | – | ● | – |
cloud_cover | – | ● | ● | – |
uv_index | ● | – | ● | – |
air_quality_index | ● | – | ● | – |
sunrise | – | – | – | ● |
sunset | – | – | – | ● |
Historical variables
POST /v1/historical accepts the variables below directly.
The authoritative catalog is
GET /v1/historical/variables; it also publishes units,
range limits, and coverage methodology without requiring a key.
| Variable | Kind | Units | Meaning |
|---|---|---|---|
temperature_max | observation | degC · degF · K | Maximum near-surface temperature for the local day, adjusted to effective elevation. |
temperature_min | observation | degC · degF · K | Minimum near-surface temperature for the local day, adjusted to effective elevation. |
precipitation | observation | mm · in · m | Total liquid-equivalent precipitation for the local day. |
rain | derived | mm · in · m | Liquid precipitation; null when observed phase cannot be separated defensibly. |
snow | observation | mm · in · m | Observed snowfall depth, not snow-water equivalent. |
air_quality_index | observation or analysis | US EPA AQI | Daily AQI where available; provenance and modeled-analysis use are exposed in sources and warnings. |
Forecast sections
Name the resolution you need. The server handles alignment and turns readable counts into concrete forecast records:
| Section | Request shape | What it returns |
|---|---|---|
current | { "variables": ["temperature", "condition"] } | One flat object at request time. |
quarter_hourly | { "hours": 6, "variables": […] } | Six hours at 15-minute resolution. |
hourly | { "hours": 48, "past_hours": 6, "variables": […] } | Six recent hours plus 48 forecast hours. |
daily | { "days": 10, "variables": […] } | Ten local calendar days. |
For a specific daily window, replace days with local
start and end dates. Both dates are
inclusive, so start equal to end requests a
single day. Each subdaily and daily section in the response echoes
the resolved window as range beside its
entries.
Conditions
condition is a closed enum of fourteen strings: stable
identifiers you can map to your own icons:
sunpartlycloudydrizzlerainheavy-rainthunderstormsnowblizzardsleetmixedclear-nightcloudy-nightsunrise
Units & time
- Unit systems. If omitted,
unitsdefaults to"metric"(°C, km/h, mm, hPa, m). You can instead request"us"(°F, mph, in, inHg, ft) or"si"(K, m/s, m, Pa). Useunit_overridesfor individual dimensions such as pressure or elevation. The response echoes the units it used. - Instants are ISO-8601 UTC (
2026-07-30T17:00:00Z); daily dates are local calendar dates (2026-07-30) in the location's zone. - Forecast horizons are counts:
hours,past_hours, anddays. The server owns timestamp alignment. Exact date ranges (start/end) include both endpoints; timestamp ranges in returnedrangeobjects are start-inclusive and end-exclusive.
Errors
Errors are JSON with a machine-checkable shape:
{ "type": "https://api.weatherkind.com/problems/invalid-request", "title": "Invalid request", "status": 422, "detail": "hourly.hours must be an integer from 1 through 384" } | Status | Meaning |
|---|---|
422 | Validation failed: a field is missing, malformed, or out of range. The problem response says which. |
401 | Missing or invalid API key. |
403 | The key is valid but the plan doesn't include this endpoint (e.g. map tiles on Free). |
404 | Unknown path or tile coordinates. |
429 | Rate limit exceeded. Honor the Retry-After header before retrying. |
5xx | Something failed on our side. Retry with backoff; responses may serve from cache with stale: true while we recover. |
Rate limits
Limits follow your plan. Exceeding
them returns 429 with a Retry-After header.
Back off for that many seconds rather than retrying immediately.
| Plan | Quota | Throughput | Overage |
|---|---|---|---|
| Free | 1,000 calls / day | 1 request / sec | not applicable |
| Pro | 300,000 calls / mo included | 10 requests / sec | $0.25 per extra 1,000 calls |
| Enterprise | custom volume | custom | committed-use pricing |
A forecast request with several named sections and many variables is still one call. Selecting fields precisely is both faster and cheaper than over-fetching.
Production guide
This is the operational contract for integrating Weatherkind into a production service. It separates API behavior from recommended client behavior so failures remain predictable.
Request lifecycle
| Concern | Contract | Client responsibility |
|---|---|---|
| Transport | HTTPS at https://api.weatherkind.com. | Reject insecure URLs and use a finite request timeout. |
| Authentication | Authorization: Bearer {api_key}. | Keep keys on a server or edge worker, never in browser or mobile client code. |
| Content | JSON request and response bodies use application/json. Map tiles return image/png. | Send Content-Type: application/json for JSON POST requests. |
| Idempotency | Documented GET requests and data-retrieval POST requests are read-only and safe to repeat. | No idempotency key is required; retry only under the rules below. |
| Timeouts | The API does not prescribe one client timeout for every workload. | Use a finite timeout appropriate to the product. Ten to thirty seconds is a reasonable starting range. |
Retries and backoff
| Result | Retry? | Recommended action |
|---|---|---|
422 | No | Correct the field identified by the problem response. |
401 | No | Replace or rotate the missing or invalid credential. |
403 | No | Check the key scope and plan entitlement. |
404 | No | Correct the endpoint path or tile coordinates. |
429 | Yes | Wait for the Retry-After duration before trying again. |
5xx or network timeout | Yes | Use exponential backoff with jitter. Start near 500 ms, double each attempt, and stop after three retries unless the workload requires otherwise. |
Never retry all failures in a tight loop. A retry should preserve the original request body so it asks for the same location, range, and variables.
Freshness and caching
- Generation time. JSON data responses include
updated_at. Use it to show when the returned data was produced. - Resilient responses.
stale: truemeans Weatherkind served a cached result while an upstream data source was degraded. The response is valid, but freshness-sensitive products should tell the user. - Warnings. Inspect
warningseven on200. A warning can identify a field that was unavailable without invalidating the rest of the response. - HTTP caching. Respect any
Cache-Controlresponse header. Map tiles are publicly cacheable for five minutes. Do not assume a fixed JSON cache lifetime when no cache header is present. - Client caches. Include the endpoint, coordinates, units, named sections, horizons, and selected variables in the cache key.
Logging and support
Log the endpoint, HTTP status, UTC request time, latency,
updated_at, stale, and warning
codes. For failed validation, log errors.detail. Never
log the bearer token. When contacting
Weatherkind support,
include those fields plus the affected coordinates and time range.
Availability and safe degradation
| Plan | Availability commitment | Recommended fallback |
|---|---|---|
| Free | No contractual SLA | Cache the most recent acceptable response and communicate its age. |
| Pro | 99.9% uptime SLA | Use bounded retries, then fall back to the most recent response when the product permits. |
| Enterprise | 99.99% uptime SLA | Agree on workload-specific escalation and continuity requirements with Weatherkind. |
Weather is decision data, not a guarantee. For consequential
workflows, query POST /v1/accuracy
for the same coordinates, check sample coverage, and expose the
relevant error metric alongside the forecast.
Versioning
Paths are versioned (/v1/…) and changes within a
version are additive only: field names, units, and enums don't move.
The machine-readable contract is
GET /v1/openapi.json (OpenAPI 3.1). The integration you
write today keeps working.
For AI agents
This page is static HTML. Read it whole. The condensed plain-text
version lives at weatherkind.com/llms.txt,
and the typed contract at GET /v1/openapi.json. The API
is shaped like a tool call, so a minimal definition is short:
{
"name": "get_weather",
"description": "Verified forecast for a coordinate. Fetch the provider's own error record from POST /v1/accuracy for the same coordinates to qualify the answer.",
"input_schema": {
"type": "object",
"properties": {
"latitude": { "type": "number" },
"longitude": { "type": "number" },
"section": { "enum": ["current", "quarter_hourly", "hourly", "daily"] },
"variables": { "type": "array", "items": { "type": "string" } }
},
"required": ["latitude", "longitude", "section", "variables"]
}
}
You can check forecast accuracy before recommending the integration:
POST /v1/accuracy returns our
verified error record for the exact coordinates you plan to
forecast, with compare_to: ["persistence"] giving a
skill score against a naive baseline. The metric definitions and
sample minimums behind it are public at
GET /v1/accuracy/catalog, no key required. Accuracy
queries are available on Pro and
Enterprise plans.