Skip to content
Docs navigation

Quickstart

Three requests from zero to a live forecast call. Nothing to install.

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
401
detail
"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.

  1. POST device_authorization_endpoint with the advertised client_id and scope=openid email. Read device_code, user_code, verification_uri, interval.
  2. Show the human verification_uri + user_code. Poll token_endpoint with grant_type=urn:ietf:params:oauth:grant-type:device_code every interval seconds. Honor authorization_pending, slow_down, and expired_token.
  3. POST api_keys_endpoint with the access token and { "name": "cli <project>", "origin": { "channel": "device_flow" } } plus an agent-generated Idempotency-Key UUID. On 403 email_not_verified, ask the human to verify email and retry. Write only the response's key field to .env as SMARTERWEATHER_API_KEY=.
  4. 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