Docs navigation
REST API
Weather MCP
Agents as developers
Quickstart
Works the same for a person with a terminal and for a coding agent acting on their behalf: the API answers every unauthenticated or over-limit call with an RFC 9457 problem whose next_steps member says what to do next. Agents: the machine-readable version of this page is llms.txt.
Option A: three requests, no install
1. Call the API with no key
The 401 carries a WWW-Authenticate challenge and a next_steps map. recommended names the link to take first.
curl -sS -D - 'https://api.smarterweather.com/v1/weather?lat=41.66&lon=-91.53'type
"https://smarterweather.com/errors/unauthorized"title
"Unauthorized"status
401detail
"Missing Authorization header. Pass your API key as 'Authorization: Bearer sw_live_*'."instance
"/v1/weather"next_steps
{
"recommended": "device_flow",
"device_flow": {
"href": "https://developers.smarterweather.com/quickstart?utm_campaign=401&utm_medium=problem-json&utm_source=api#device-flow",
"description": "No install, no loopback port: POST device_authorization_endpoint, show the human the code, poll, then GET /developer/keys.",
"client_id": "k2h05BUoTP393zcD",
"device_authorization_endpoint": "https://clerk.smarterweather.com/oauth/device_authorization",
"token_endpoint": "https://clerk.smarterweather.com/oauth/token",
"api_keys_endpoint": "https://api.smarterweather.com/developer/keys"
},
"get_key": {
"href": "https://developers.smarterweather.com/dashboard/api-keys?utm_campaign=401&utm_medium=problem-json&utm_source=api",
"description": "Sign in (free, no card) and mint an API key. Pass it as 'Authorization: Bearer sw_live_*'."
},
"quickstart": {
"href": "…",
"description": "…"
},
"agents": {
"href": "…",
"description": "…"
},
"onboarding_mcp": {
"href": "…",
"description": "…"
},
"keyless_x402": {
"href": "…",
"description": "…"
},
"key_handling": {
"href": "…",
"description": "…"
},
"errors": {
"href": "…",
"description": "…"
}
}2. Get a key
Human present: npx -y @smarterweather/mcp-onboarding@latest login. No human: npx -y @smarterweather/mcp-onboarding@latest trial. Both write .env (mode 0600, gitignored) and print only a key prefix — never the bearer (key handling). A human can also mint from the dashboard (create a free account, no card).
3. Call it again with the key
Success is HTTP 200 with meta and attribution. Anything else is a problem document with next_steps.
# .env -> SMARTERWEATHER_API_KEY=sw_live_... (and add .env to .gitignore)
set -a; source .env; set +a
curl -sS 'https://api.smarterweather.com/v1/weather?lat=41.66&lon=-91.53' \
-H "Authorization: Bearer $SMARTERWEATHER_API_KEY"location
{
"lat": 42.3601,
"lon": -71.0589,
"name": "Boston",
"region": "MA",
"country": "US",
"timezone": "America/New_York",
"county": "Suffolk",
"zip": "02108"
}current
{
"time": "2026-04-20T15:00:00Z",
"temperature": 58.4,
"feels_like": 56.1,
"humidity": 62,
"dew_point": 45.2,
"pressure": 30.05,
"wind_speed": 11.3,
"wind_gust": 17.8,
"wind_direction": 240,
"visibility": 10,
"cloud_cover": 35,
"uv_index": 5,
"precip_rate": 0,
"precip_type": "none",
"conditions": "Partly Cloudy",
"icon": "partly-cloudy-day",
"severe": false
}hourly
[
{
"time": "2026-04-20T16:00:00Z",
"temperature": 59.1,
"feels_like": 56.8,
"humidity": 60,
"wind_speed": 12.1,
"wind_direction": 245,
"precip_probability": 5,
"precip_amount": 0,
"conditions": "Partly Cloudy",
"icon": "partly-cloudy-day"
},
{
"time": "2026-04-20T17:00:00Z",
"temperature": 59.6,
"feels_like": 57.2,
"humidity": 58,
"wind_speed": 12.4,
"wind_direction": 245,
"precip_probability": 5,
"precip_amount": 0,
"conditions": "Partly Cloudy",
"icon": "partly-cloudy-day"
}
]daily
[
{
"date": "2026-04-20",
"high": 61,
"low": 44.5,
"humidity_avg": 55,
"wind_speed_max": 14.2,
"wind_direction": 240,
"precip_probability": 10,
"precip_total": 0,
"snow_total": 0,
"conditions": "Partly Cloudy",
"icon": "partly-cloudy-day",
"day": {
"temperature": 61,
"wind_speed": 14.2,
"wind_direction": 240,
"precip_probability": 10,
"precip_total": 0,
"conditions": "Partly Cloudy",
"icon": "partly-cloudy-day",
"narrative": "Partly cloudy with a high near 61. West winds 10 to 15 mph."
},
"night": {
"temperature": 44.5,
"wind_speed": 6.8,
"wind_direction": 260,
"precip_probability": 5,
"precip_total": 0,
"conditions": "Mostly Clear",
"icon": "mostly-clear-night",
"narrative": "Mostly clear with a low around 45. Winds becoming light."
}
}
]alerts
[]outlooks
[]astro
{
"civil_dawn": "2026-04-20T09:37:00Z",
"sunrise": "2026-04-20T10:05:00Z",
"solar_noon": "2026-04-20T16:47:00Z",
"sunset": "2026-04-20T23:29:00Z",
"civil_dusk": "2026-04-20T23:57:00Z",
"day_length": 48240,
"daylight_length": 51600,
"moonrise": "2026-04-20T11:42:00Z",
"moonset": "2026-04-21T01:18:00Z",
"moon_phase": 0.15,
"moon_phase_name": "Waxing Crescent",
"moon_illumination": 0.22
}units
{
"temperature": "F",
"wind_speed": "mph",
"pressure": "inHg",
"visibility": "mi",
"precip_amount": "in"
}attribution
{
"sources": [
"NOAA NBM",
"HRRR",
"RTMA",
"MRMS",
"NWS",
"JPL Ephemeris"
],
"notice": "Weather data from NOAA/NWS. Astronomical calculations from JPL ephemerides."
}meta
{
"schema_version": "2026-04-20",
"generated_at": "2026-04-20T15:00:04Z",
"freshness": [
{
"source": "rtma",
"product_id": "rtma_conus_2p5km",
"reference_time": "2026-04-20T14:45:00Z",
"latest_valid": "2026-04-20T14:45:00Z",
"age_seconds": 904
},
{
"source": "hrrr",
"product_id": "hrrr_conus_3km",
"reference_time": "2026-04-20T14:00:00Z",
"latest_valid": "2026-04-20T15:00:00Z",
"age_seconds": 3604
}
],
"warnings": []
}Device flow (agents)
Preferred: run npx -y @smarterweather/mcp-onboarding@latest login. It shows the human a verification URL and code, writes SMARTERWEATHER_API_KEY to ./.env (mode 0600, gitignored), and prints only a key prefix. login blocks until the human approves — run it in the background or with a long shell timeout and relay the printed URL. The 401 next_steps.device_flow.command field names that one-liner. Without Node, follow the raw endpoints below and write only the key field of the POST api_keys_endpoint response to .env as SMARTERWEATHER_API_KEY= — never print the key or access token.
POSTdevice_authorization_endpointwith the advertisedclient_idandscope=openid email. Readdevice_code,user_code,verification_uri,interval.- Show the human
verification_uri+user_code. Polltoken_endpointwithgrant_type=urn:ietf:params:oauth:grant-type:device_codeeveryintervalseconds. Honorauthorization_pending,slow_down, andexpired_token. POST api_keys_endpointwith the access token and{ "name": "cli <project>", "origin": { "channel": "device_flow" } }plus an agent-generatedIdempotency-KeyUUID. On403 email_not_verified, ask the human to verify email and retry. Write only the response'skeyfield to.envasSMARTERWEATHER_API_KEY=.- Discard the access token. Never print the key.
# Preferred: one command. Shows the human a URL, writes ./.env (0600, gitignored),
# prints only a key prefix.
npx -y @smarterweather/mcp-onboarding@latest login
# Raw recipe (no Node). Stop if .env already has SMARTERWEATHER_API_KEY.
# 1. Ask Clerk for a device code (client_id is public; it is in the 401 body).
curl -sS -X POST 'https://clerk.smarterweather.com/oauth/device_authorization' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=k2h05BUoTP393zcD' \
--data-urlencode 'scope=openid email'
# 2. Show the human verification_uri + user_code. Poll until they approve.
# grant_type=urn:ietf:params:oauth:grant-type:device_code
# handle authorization_pending / slow_down / expired_token.
# 3. Create a key and write ONLY its key field to .env. Nothing reaches the terminal.
# (umask 077; curl -sS -X POST 'https://api.smarterweather.com/developer/keys' \
# -H "Authorization: Bearer $ACCESS_TOKEN" \
# -H "Idempotency-Key: $(uuidgen)" \
# -H 'Content-Type: application/json' \
# -d '{"name":"cli my-project","origin":{"channel":"device_flow"}}' \
# | jq -er '"SMARTERWEATHER_API_KEY=" + .key' >> .env)
# grep -qx '.env' .gitignore 2>/dev/null || echo .env >> .gitignore
# 4. Discard the access token (unset ACCESS_TOKEN).Option B: give your agent weather tools
If your client speaks MCP, paste this prompt. Your agent connects to the onboarding MCP anonymously, signs you up, mints a key after OAuth consent, and configures the weather MCP in your client. sign_up is anonymous; create_api_key and configure_mcp run after consent. Agents with a wallet can instead pay per call via x402.
Install the Smarter Weather onboarding skill. Run `npx skills add https://developers.smarterweather.com --skill smarterweather-onboarding` and select your agent. You can read the skill directly at https://developers.smarterweather.com/.well-known/skills/smarterweather-onboarding/SKILL.md (raw: https://raw.githubusercontent.com/smarterweather/developer/main/plugins/smarterweather-onboarding/skills/smarterweather-onboarding/SKILL.md). Then use the onboarding skill to sign up, mint an API key, and configure the weather MCP.
Option C: from the dashboard, in your language
Same key, same endpoint, with a place name instead of coordinates.
curl "https://api.smarterweather.com/v1/weather?location=Boston,%20MA&units=imperial" \
-H "Authorization: Bearer YOUR_API_KEY"const res = await fetch(
"https://api.smarterweather.com/v1/weather?location=Boston,%20MA&units=imperial",
{ headers: { Authorization: `Bearer ${process.env.SMARTERWEATHER_API_KEY}` } },
);
const data = await res.json();import os, requests
data = requests.get(
"https://api.smarterweather.com/v1/weather",,
params={"location":"Boston, MA","units":"imperial"},
headers={"Authorization": f"Bearer {os.environ['SMARTERWEATHER_API_KEY']}"},
).json()req, _ := http.NewRequest("GET",
"https://api.smarterweather.com/v1/weather?location=Boston,%20MA&units=imperial",
nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("SMARTERWEATHER_API_KEY"))
res, _ := http.DefaultClient.Do(req)location
{
"lat": 42.3601,
"lon": -71.0589,
"name": "Boston",
"region": "MA",
"country": "US",
"timezone": "America/New_York",
"county": "Suffolk",
"zip": "02108"
}current
{
"time": "2026-04-20T15:00:00Z",
"temperature": 58.4,
"feels_like": 56.1,
"humidity": 62,
"dew_point": 45.2,
"pressure": 30.05,
"wind_speed": 11.3,
"wind_gust": 17.8,
"wind_direction": 240,
"visibility": 10,
"cloud_cover": 35,
"uv_index": 5,
"precip_rate": 0,
"precip_type": "none",
"conditions": "Partly Cloudy",
"icon": "partly-cloudy-day",
"severe": false
}hourly
[
{
"time": "2026-04-20T16:00:00Z",
"temperature": 59.1,
"feels_like": 56.8,
"humidity": 60,
"wind_speed": 12.1,
"wind_direction": 245,
"precip_probability": 5,
"precip_amount": 0,
"conditions": "Partly Cloudy",
"icon": "partly-cloudy-day"
},
{
"time": "2026-04-20T17:00:00Z",
"temperature": 59.6,
"feels_like": 57.2,
"humidity": 58,
"wind_speed": 12.4,
"wind_direction": 245,
"precip_probability": 5,
"precip_amount": 0,
"conditions": "Partly Cloudy",
"icon": "partly-cloudy-day"
}
]daily
[
{
"date": "2026-04-20",
"high": 61,
"low": 44.5,
"humidity_avg": 55,
"wind_speed_max": 14.2,
"wind_direction": 240,
"precip_probability": 10,
"precip_total": 0,
"snow_total": 0,
"conditions": "Partly Cloudy",
"icon": "partly-cloudy-day",
"day": {
"temperature": 61,
"wind_speed": 14.2,
"wind_direction": 240,
"precip_probability": 10,
"precip_total": 0,
"conditions": "Partly Cloudy",
"icon": "partly-cloudy-day",
"narrative": "Partly cloudy with a high near 61. West winds 10 to 15 mph."
},
"night": {
"temperature": 44.5,
"wind_speed": 6.8,
"wind_direction": 260,
"precip_probability": 5,
"precip_total": 0,
"conditions": "Mostly Clear",
"icon": "mostly-clear-night",
"narrative": "Mostly clear with a low around 45. Winds becoming light."
}
}
]alerts
[]outlooks
[]astro
{
"civil_dawn": "2026-04-20T09:37:00Z",
"sunrise": "2026-04-20T10:05:00Z",
"solar_noon": "2026-04-20T16:47:00Z",
"sunset": "2026-04-20T23:29:00Z",
"civil_dusk": "2026-04-20T23:57:00Z",
"day_length": 48240,
"daylight_length": 51600,
"moonrise": "2026-04-20T11:42:00Z",
"moonset": "2026-04-21T01:18:00Z",
"moon_phase": 0.15,
"moon_phase_name": "Waxing Crescent",
"moon_illumination": 0.22
}units
{
"temperature": "F",
"wind_speed": "mph",
"pressure": "inHg",
"visibility": "mi",
"precip_amount": "in"
}attribution
{
"sources": [
"NOAA NBM",
"HRRR",
"RTMA",
"MRMS",
"NWS",
"JPL Ephemeris"
],
"notice": "Weather data from NOAA/NWS. Astronomical calculations from JPL ephemerides."
}meta
{
"schema_version": "2026-04-20",
"generated_at": "2026-04-20T15:00:04Z",
"freshness": [
{
"source": "rtma",
"product_id": "rtma_conus_2p5km",
"reference_time": "2026-04-20T14:45:00Z",
"latest_valid": "2026-04-20T14:45:00Z",
"age_seconds": 904
},
{
"source": "hrrr",
"product_id": "hrrr_conus_3km",
"reference_time": "2026-04-20T14:00:00Z",
"latest_valid": "2026-04-20T15:00:00Z",
"age_seconds": 3604
}
],
"warnings": []
}Three recipes
Schedule check
Move outdoor work when daily precipitation probability crosses a threshold.
const weather = await fetch(
"https://api.smarterweather.com/v1/weather?location=Boston,%20MA&include=daily",
{ headers: { Authorization: `Bearer ${process.env.SMARTERWEATHER_API_KEY}` } },
).then((r) => r.json());
if (weather.daily[0].precip_probability >= 40) {
console.log("Move outdoor work inside");
}Alert routing
Forward the alerts array to a webhook.
const weather = await fetch(
"https://api.smarterweather.com/v1/weather?location=Boston,%20MA&include=alerts",
{ headers: { Authorization: `Bearer ${process.env.SMARTERWEATHER_API_KEY}` } },
).then((r) => r.json());
for (const alert of weather.alerts) {
await fetch(process.env.WEBHOOK_URL, { method: "POST", body: JSON.stringify(alert) });
}Map overlay
Load GET /v1/storm-tracks GeoJSON into MapLibre.
const geo = await fetch("https://api.smarterweather.com/v1/storm-tracks", {
headers: { Authorization: `Bearer ${process.env.SMARTERWEATHER_API_KEY}` },
}).then((r) => r.json());
map.addSource("storms", { type: "geojson", data: geo });
map.addLayer({ id: "storms", type: "line", source: "storms" });Next steps
- REST API — per-resource references
- Weather endpoint — the unified response
- MCP setup — Claude, ChatGPT, or Cursor
- Error model — every problem type and its
next_steps