API Documentation

outdoor4cast provides activity-specific weather risk scores for outdoor operations. All endpoints return JSON.

Quick Start

Get a risk score for construction work in Denver in under a minute.

# 1. Sign up and create an API key at https://outdoor4cast.com/auth/sign-up

# 2. Make your first request
curl "https://outdoor4cast.com/api/v1/risk?location=Denver,CO&activity=construction" \
  -H "x-api-key: od4c_YOUR_KEY_HERE"

# Response
{
  "location": { "name": "Denver, CO", "lat": 39.74, "lon": -104.99, "resolution_note": null },
  "generated_at": "2026-03-29T12:00:00.000Z",
  "forecast_hours": 24,
  "activity": "construction",
  "risk": {
    "overall": { "score": 2, "label": "Low", "color": "green" },
    "factors": { ... },
    "active_alerts": []
  },
  "premium": { "briefing": null, "alert_copy": null, "email_subject": null }
}

Authentication

All requests must include your API key in the x-api-key header.

curl "https://outdoor4cast.com/api/v1/risk?location=..." \
  -H "x-api-key: od4c_YOUR_KEY_HERE"

API keys are prefixed with od4c_. Generate and manage keys at your dashboard. Each key is shown only once at creation — store it securely.

Base URL & Versioning

https://outdoor4cast.com/api/v1/

The current API version is v1. Breaking changes will be released under a new version prefix with a migration period.

Rate Limits

Monthly request limits by plan:

PlanMonthly requestsActivitiesPremium output
Free5003
Builder10,0004
Professional100,000All 47
Business1,000,000All 47

When you exceed your limit, requests return 429 RATE_LIMIT_EXCEEDED. Limits reset on a rolling monthly basis.

GET /api/v1/risk

Returns a weather risk score for a location and activity over a forecast window. Available on all plans.

Parameters

locationrequired
string
Location to query. Accepts many formats: city name (Denver), city + state (Denver, CO), ZIP code (84101), county (Salt Lake County, UT), lat/lon (39.74,-104.99), address (1600 Pennsylvania Ave NW, Washington DC), airport code (SLC or KSLC), or landmark / POI name (Zion National Park).
activityrequired
string
Activity profile ID. See Activities for available values and tier requirements.
start
string
Window start. Accepts a named shorthand (sat, satnight, sun, sunnight, monfrinight, tonight, today), an ISO date (2026-04-12 → 06:00 local), an ISO datetime (2026-04-12T14:00-06:00), or an integer hours from now (72).
end
string
Window end. Named shorthand or ISO date/datetime only — no integer. Mutually exclusive with window. ISO date defaults to 23:59 local.
window
integer
Duration in hours (1–168). Mutually exclusive with end. Defaults to 24 when start is set, 1 for current conditions.
output
string
data — raw weather periods + risk score (Builder+). ai — risk score + AI briefing, alert copy, email subject (Professional+). full — raw weather + risk + AI briefing (Professional+). Omit for risk score only. See Output Modes.

Example request

# Current conditions (next 1 hour)
curl "https://outdoor4cast.com/api/v1/risk?location=Salt+Lake+City,UT&activity=construction" \
  -H "x-api-key: od4c_YOUR_KEY_HERE"

# Named period
curl "https://outdoor4cast.com/api/v1/risk?location=Denver,CO&activity=hiking&start=sat" \
  -H "x-api-key: od4c_YOUR_KEY_HERE"

# 48-hour window starting 72 hours from now
curl "https://outdoor4cast.com/api/v1/risk?location=Miami,FL&activity=beach_volleyball&start=72&window=48" \
  -H "x-api-key: od4c_YOUR_KEY_HERE"

# Raw weather data + risk score (Builder+)
curl "https://outdoor4cast.com/api/v1/risk?location=Denver,CO&activity=hiking&start=sat&output=data" \
  -H "x-api-key: od4c_YOUR_KEY_HERE"

# Full output with AI briefing (Professional+)
curl "https://outdoor4cast.com/api/v1/risk?location=Denver,CO&activity=hiking&start=sat&output=full" \
  -H "x-api-key: od4c_YOUR_KEY_HERE"

Example response

{
  "location": {
    "name": "Salt Lake City, UT",
    "lat": 40.76,
    "lon": -111.89,
    "resolution_note": null
  },
  "generated_at": "2026-04-03T12:00:00.000Z",
  "forecast_hours": 24,
  "activity": "skiing",
  "window_start": "2026-04-05T06:00:00-06:00",
  "window_end":   "2026-04-06T06:00:00-06:00",
  "window_label": "Saturday",
  "short_forecast": "Partly Cloudy",
  "risk": {
    "summary": {
      "peak":    { "score": 68, "level": "HIGH", "at": "2026-04-05T14:00:00-06:00" },
      "average": { "score": 42, "level": "MODERATE" },
      "recommendation": "DELAY",
      "recommendation_note": "Peak risk HIGH at Sat Apr 5, 2pm. 34 mph gusts exceed Skiing / Snowboarding thresholds."
    },
    "factors": {
      // Peak-period factors (as_of = peak period startTime)
      "wind":        { "score": 75, "value": "34 mph gusts",   "exceeds_threshold": true  },
      "cold_stress": { "score": 10, "value": "18°F wind chill","exceeds_threshold": false },
      "visibility":  { "score": 60, "value": "0.4 mi",         "exceeds_threshold": true  },
      "as_of": "2026-04-05T14:00:00-06:00"
    },
    "windows": [
      {
        "start": "2026-04-05T06:00:00-06:00", "end": "2026-04-05T07:00:00-06:00",
        "level": "LOW", "score": 18,
        "weather": {
          "temperature_f": 62, "wind_speed_mph": 8, "wind_gust_mph": 12,
          "humidity_pct": 42, "dewpoint_c": 8.1, "precip_pct": 5,
          "wind_chill_f": 60, "wbgt_celsius": 18.2, "visibility_mi": 10.0,
          "ceiling_ft": null, "sky_cover_pct": 20, "uv_index": 4,
          "thunder_prob_pct": 0, "wave_height_ft": null, "haines_index": null,
          "lightning_level": 0
        }
        // weather present only when output=data or output=full (Builder+)
      },
      { "start": "2026-04-05T07:00:00-06:00", "end": "2026-04-05T08:00:00-06:00", "level": "MODERATE", "score": 35 },
      // ... one entry per hour in the window
      { "start": "2026-04-05T14:00:00-06:00", "end": "2026-04-05T15:00:00-06:00", "level": "HIGH",     "score": 68 }
    ],
    "safe_windows":   ["Sat Apr 5, 6am–8am", "Sat Apr 5, 10pm–6am"],
    "danger_windows": ["Sat Apr 5, 12pm–4pm"],
    "acclimatization_flag": false
  },
  "premium": {
    "briefing": null,       // populated when output=ai or output=full (Professional+)
    "alert_copy": null,
    "email_subject": null
  }
}

GET /api/v1/alerts

Returns active NWS weather alerts for a location. Available on all plans.

Parameters

locationrequired
string
Location to query. Same format as /risk.

Example request

curl "https://outdoor4cast.com/api/v1/alerts?location=Miami,FL" \
  -H "x-api-key: od4c_YOUR_KEY_HERE"

Example response

{
  "location": { "name": "Miami, FL", "lat": 25.77, "lon": -80.19 },
  "retrieved_at": "2026-03-29T12:00:00.000Z",
  "alerts": [
    {
      "event": "Rip Current Statement",
      "severity": "Moderate",
      "headline": "Dangerous rip currents expected at area beaches.",
      "description": "...",
      "instruction": "...",
      "expires": "2026-03-30T00:00:00Z"
    }
  ]
}

Returns an empty alerts array when no alerts are active. Alert data is sourced from the National Weather Service and cached hourly.

GET /api/v1/settings/thresholds

Returns the custom risk thresholds currently set on your API key, plus all available activity IDs. Requires Professional tier or above.

Example request

curl "https://outdoor4cast.com/api/v1/settings/thresholds" \
  -H "x-api-key: od4c_YOUR_KEY_HERE"

Example response

{
  "custom_thresholds": {
    "construction": {
      "wind_gust_mph": 35,
      "precip_pct": 50
    }
  },
  "available_activities": [
    "construction", "roofing", "landscaping", "utilities", "survey",
    "events", "sports", "golf", "photography", "hiking", "skiing",
    "cycling", "climbing", "surfing", "agriculture", "forestry",
    "fire_crew", "logistics", "trucking", "aviation_small",
    "boating", "fishing", "marine_logistics",
    "stargazing", "running", "trail_running", "camping", "hunting",
    "beach", "outdoor_fitness", "concert_festival", "outdoor_dining",
    "hot_air_balloon", "paragliding", "skydiving", "helicopter",
    "kayaking", "whitewater", "open_water_swim",
    "cross_country_ski", "snowmobile", "ice_fishing",
    "crane_operation", "window_washing", "pipeline",
    "solar_farm", "concrete_work"
  ]
}

PUT /api/v1/settings/thresholds

Saves custom risk thresholds for a specific activity on your API key. These override the default thresholds when scoring risk for that activity. Requires Professional tier or above.

Request body

activityrequired
string
Activity profile ID to customize. Must be a valid activity ID.
thresholdsrequired
object
Key-value pairs of threshold names and numeric values. Only include fields you want to override — unspecified fields use the activity default.

Threshold fields

wind_gust_mph
number
Wind gust speed in mph that triggers elevated risk
precip_pct
number
Precipitation probability % threshold
temp_min_f
number
Minimum temperature in °F below which risk increases
visibility_mi
number
Visibility in miles below which risk increases
lightning_threshold
number
Lightning activity level (1–8) that triggers elevated risk
wave_height_ft
number
Wave height in feet (marine/water activities only)
sky_cover_pct_threshold
number
Max sky cover % before risk increases (stargazing, photography)
uv_index_threshold
number
Max UV index before risk increases (beach, outdoor fitness)
ceiling_ft_threshold
number
Min cloud ceiling in feet (aerial activities only)
thunder_prob_pct_threshold
number
Max thunder probability % before risk increases

Example request

curl -X PUT "https://outdoor4cast.com/api/v1/settings/thresholds" \
  -H "x-api-key: od4c_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "activity": "construction",
    "thresholds": {
      "wind_gust_mph": 35,
      "precip_pct": 50
    }
  }'

Example response

{
  "activity": "construction",
  "thresholds": { "wind_gust_mph": 35, "precip_pct": 50 },
  "message": "Custom thresholds saved"
}

Activities

Pass the ID string in the activity parameter. Access depends on your plan tier.

IDNameMinimum tier
constructionConstruction / Generalfree
hikingHiking / Trailfree
eventsOutdoor Eventsfree
landscapingLandscapingbuilder
agricultureAgriculture / Farmingbuilder
roofingRoofingprofessional
utilitiesUtilities / Lineworkprofessional
surveyLand Surveyprofessional
forestryForestry / Loggingprofessional
fire_crewWildfire / Fire Crewprofessional
logisticsLogistics / Deliveryprofessional
truckingTrucking / CDLprofessional
crane_operationCrane Operationprofessional
window_washingHigh-Rise Window Washingprofessional
pipelinePipeline / Field Inspectionprofessional
solar_farmSolar Farm / Panel Installationprofessional
concrete_workConcrete Pouring / Finishingprofessional
sportsOutdoor Sports / Practiceprofessional
golfGolfprofessional
photographyOutdoor Photography / Filmprofessional
skiingSkiing / Snowboardingprofessional
cyclingCycling / Mountain Bikingprofessional
climbingRock Climbingprofessional
runningRunning / Road Raceprofessional
trail_runningTrail Runningprofessional
campingCamping / Backpackingprofessional
huntingHunting / Field Sportsprofessional
beachBeach / Swimmingprofessional
outdoor_fitnessOutdoor Fitness / Trainingprofessional
concert_festivalConcert / Festivalprofessional
outdoor_diningOutdoor Dining / Patioprofessional
stargazingStargazing / Astronomyprofessional
aviation_smallSmall Aircraft / Droneprofessional
hot_air_balloonHot Air Balloonprofessional
paraglidingParagliding / Hang Glidingprofessional
skydivingSkydiving / BASE Jumpingprofessional
helicopterHelicopter Operationsprofessional
surfingSurfing / Paddle Sportsprofessional
boatingBoating / Sailingprofessional
fishingFishing (Recreational)professional
marine_logisticsMarine / Commercial Vesselprofessional
kayakingKayaking / Canoeingprofessional
whitewaterWhitewater Raftingprofessional
open_water_swimOpen Water Swimmingprofessional
cross_country_skiCross-Country Skiingprofessional
snowmobileSnowmobile / Snow Machineprofessional
ice_fishingIce Fishingprofessional

Risk Response Schema

The top-level response and risk object:

// Top-level response fields
{
  "location":      { "name": string, "lat": number, "lon": number, "resolution_note": string | null },
  "generated_at":  string,          // ISO 8601
  "forecast_hours": number,
  "activity":      string,
  "window_start":  string,          // ISO 8601 — start of scored window
  "window_end":    string,          // ISO 8601 — end of scored window
  "window_label":  string,          // e.g. "Saturday" — only when start= is a named period or ISO date
  "short_forecast": string,         // e.g. "Partly Cloudy" — only when start= is a named period or ISO date
  "risk": { ... },
  "premium": { ... }
}

// risk object
"risk": {
  "summary": {
    "peak":    { "score": number, "level": "LOW"|"MODERATE"|"HIGH"|"VERY_HIGH"|"EXTREME", "at": string },
    "average": { "score": number, "level": "LOW"|"MODERATE"|"HIGH"|"VERY_HIGH"|"EXTREME" },
    "recommendation": "PROCEED" | "CAUTION" | "DELAY" | "CANCEL",
    "recommendation_note": string   // human-readable explanation of peak risk drivers
  },
  "factors": {
    // Peak-period factor scores — only active factors for the activity are included
    "wind":         { "score": number, "value": string, "exceeds_threshold": boolean },
    "heat_stress":  { "score": number, "value": string, "exceeds_threshold": boolean },
    "cold_stress":  { "score": number, "value": string, "exceeds_threshold": boolean },
    "precipitation":{ "score": number, "value": string, "exceeds_threshold": boolean },
    "snow_ice":     { "score": number, "value": string, "exceeds_threshold": boolean },
    "lightning":    { "score": number, "value": string, "exceeds_threshold": boolean },
    "visibility":   { "score": number, "value": string, "exceeds_threshold": boolean },
    "fire_weather": { "score": number, "value": string, "exceeds_threshold": boolean },
    "waves":        { "score": number, "value": string, "exceeds_threshold": boolean },
    "sky_cover":    { "score": number, "value": string, "exceeds_threshold": boolean },
    "uv_index":     { "score": number, "value": string, "exceeds_threshold": boolean },
    "thunder_prob": { "score": number, "value": string, "exceeds_threshold": boolean },
    "ceiling":      { "score": number, "value": string, "exceeds_threshold": boolean },
    "as_of": string  // ISO 8601 — startTime of the peak-risk period
  },
  "windows": [
    // One entry per hourly period in the window
    { "start": string, "end": string, "level": string, "score": number, "weather?": WeatherConditions }
    // weather present when output=data or output=full (Builder+)
  ],
  "safe_windows":   string[],  // e.g. ["Sat Apr 5, 6am–9am", "Sat Apr 5, 8pm–6am"]
  "danger_windows": string[],  // e.g. ["Sat Apr 5, 12pm–4pm"]
  "acclimatization_flag": boolean
}

The resolution_note on the location object is populated for precision-sensitive activities (aviation, marine) to note that weather data uses a ~2.5km grid.

Output Modes

The output parameter controls how much detail is returned. Omit it for a risk score only (available on all plans).

output=What you getMin tier
(omitted)Risk score, summary, factors, safe/danger windowsFree
dataRisk score + raw weather object on each window entryBuilder
aiRisk score + AI briefing, alert copy, email subjectProfessional
fullRisk score + raw weather + AI briefingProfessional

AI briefing (output=ai or output=full)

Add output=ai or output=full to receive an AI-generated briefing alongside the risk score. Requires Professional tier or above.

curl "https://outdoor4cast.com/api/v1/risk?location=Denver,CO&activity=construction&start=sat&output=ai" \
  -H "x-api-key: od4c_YOUR_KEY_HERE"

The premium object in the response is populated:

"premium": {
  "briefing": "Moderate risk for outdoor construction today. Wind gusts to 28 mph expected
               between 2–6 PM. Morning work is clear; consider suspending crane operations
               and elevated work after midday.",
  "alert_copy": "⚠️ Wind Advisory in effect 2 PM–8 PM. Secure loose materials.",
  "email_subject": "Construction Risk Update: Moderate — Denver CO, March 29"
}

Briefings are cached for 6 hours per location/activity combination to minimize latency.

Custom Thresholds

Professional and Business subscribers can override the default risk thresholds for any activity to match their specific operational requirements. Thresholds are stored on your API key and applied automatically on every /risk request.

For example, a construction company that operates safely up to 35 mph gusts (vs. the default 25 mph) can set a custom threshold so wind only elevates risk at their actual operational limit:

# Set custom wind threshold for construction
curl -X PUT "https://outdoor4cast.com/api/v1/settings/thresholds" \
  -H "x-api-key: od4c_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{"activity": "construction", "thresholds": {"wind_gust_mph": 35}}'

# Subsequent /risk calls now use wind_gust_mph: 35 for this key
curl "https://outdoor4cast.com/api/v1/risk?location=Denver,CO&activity=construction" \
  -H "x-api-key: od4c_YOUR_KEY_HERE"

Error Codes

All errors return JSON with an error object:

{
  "error": {
    "code": "UPGRADE_REQUIRED",
    "message": "Builder tier supports: construction, landscaping, agriculture, events. Upgrade to Professional for all activities.",
    "docs": "https://outdoor4cast.com/docs#UPGRADE_REQUIRED"
  }
}
INVALID_API_KEY
401
Missing, invalid, or deactivated API key.
RATE_LIMIT_EXCEEDED
429
Monthly request limit reached for your plan. Resets on your billing cycle date.
UPGRADE_REQUIRED
403
The requested activity or feature requires a higher plan tier.
PREMIUM_REQUIRED
403
output=full requires Professional tier or above.
MISSING_PARAM
422
A required query parameter is missing.
INVALID_ACTIVITY
422
The activity ID is not recognized.
INVALID_BODY
422
Request body is not valid JSON (PUT /settings/thresholds).
LOCATION_NOT_FOUND
422
The location could not be geocoded. Try a more specific location string.
WEATHER_UNAVAILABLE
503
The NWS weather data service is temporarily unavailable. Retry after a short delay.
INTERNAL_ERROR
500
An unexpected server-side error occurred. Contact support if it persists.

Code Examples

JavaScript / fetch

const response = await fetch(
  'https://outdoor4cast.com/api/v1/risk?location=Denver,CO&activity=construction',
  { headers: { 'x-api-key': 'od4c_YOUR_KEY_HERE' } }
);
const data = await response.json();

if (!response.ok) {
  console.error(data.error.code, data.error.message);
} else {
  console.log(`Risk: ${data.risk.overall.label} (${data.risk.overall.score}/5)`);
}

Python

import httpx

resp = httpx.get(
    "https://outdoor4cast.com/api/v1/risk",
    params={"location": "Denver, CO", "activity": "construction"},
    headers={"x-api-key": "od4c_YOUR_KEY_HERE"},
)
resp.raise_for_status()
data = resp.json()
print(f"Risk: {data['risk']['overall']['label']} ({data['risk']['overall']['score']}/5)")

Node.js (full workflow)

import { createClient } from '@/lib/outdoor4cast'; // example wrapper

const client = createClient({ apiKey: process.env.OUTDOOR4CAST_API_KEY });

// Get risk score
const risk = await client.risk({ location: 'Denver, CO', activity: 'construction' });

// Get active alerts
const alerts = await client.alerts({ location: 'Denver, CO' });

// Set custom thresholds (Professional+)
await client.setThresholds({
  activity: 'construction',
  thresholds: { wind_gust_mph: 35, precip_pct: 50 },
});

Need help?

Email [email protected] or sign in to your dashboard to manage your keys.