KaiCast DEVELOPERS OpenAPI llms.txt kaicast.com Connect MCP

REST API

Base URL: https://kaicast.com. All endpoints are GET, return JSON, and allow cross-origin requests. The machine-readable spec is /openapi.json (OpenAPI 3.1) — import it into Postman, generate a client, or hand it to an agent framework as a tool definition.

EndpointLayerPurpose
GET /api/v1/conditionsFactConditions for one spot over up to 7 days
GET /api/v1/spotsFactSpot directory (metadata only), filter by region / text / nearest
GET /api/v1/coverageFactCovered regions, decision support, activities
GET /api/v1/recommendationsDecisionRanked spot picks for an activity, area and day
GET /api/v1/best-dayDecisionBest day in a window for an activity at a destination

Conventions #

Authentication #

None required today. Facts are free and keyless; decisions are free during early access.

Keys aren't issued yet (API keys). When they are, they'll be required for decisions once metering starts, sent like this — the header is already accepted and ignored, so you can wire it in now:

http
Authorization: Bearer kc_live_xxxxxxxx_xxxxxxxxxxxxxxxx

or X-KaiCast-Key: kc_live_…. Test keys start with kc_test_. Keys never go in the URL.

GET /api/v1/conditions #

Facts for one spot, up to 7 days, in 3-hour periods.

ParameterTypeNotes
spotstringSpot id, e.g. electric-beach. Or use lat + lon.
lat, lonnumberMatched to the nearest covered spot within 30 km; the response echoes query.resolved_from with the distance.
fromdateSpot-local YYYY-MM-DD. Default today.
todateInclusive. Default from + 6. At most 7 days.
bash
curl "https://kaicast.com/api/v1/conditions?spot=electric-beach&from=2026-10-06&to=2026-10-06"
json
{
  "schema": "kaicast.conditions.v1",
  "spot": {
    "id": "electric-beach", "name": "Electric Beach",
    "region": { "id": "us-hi-oahu", "name": "Oʻahu", "country": "US", "admin1": "Hawaii" },
    "lat": 21.355, "lon": -158.131, "timezone": "Pacific/Honolulu"
  },
  "query": { "spot": "electric-beach", "lat": null, "lon": null, "from": "2026-10-06", "to": "2026-10-06", "resolved_from": null },
  "range": { "from": "2026-10-06", "to": "2026-10-06", "available_from": "2026-10-05", "available_to": "2026-10-12" },
  "generated_at": "2026-10-05T10:00:00-10:00",
  "next_update_after": "2026-10-05T11:15:00-10:00",
  "data_quality": { "visibility_source": "satellite", "freshness": "live" },
  "tide_datum": "MLLW",
  "now": null,
  "periods": [
    {
      "kind": "forecast",
      "start": "2026-10-06T08:00:00-10:00", "end": "2026-10-06T11:00:00-10:00",
      "rating": "great", "score": 72,
      "visibility_ft": 45, "visibility_m": 13.7,
      "swell_height_ft": 2.1, "swell_height_m": 0.6, "swell_period_s": 12, "swell_from_deg": 310,
      "wind_speed_kt": 8, "wind_gust_kt": 12, "wind_from_deg": 65,
      "tide_state": "rising", "tide_height_ft": 0.9,
      "water_temp_c": 27.4,
      "confidence": 0.82, "visibility_source": "satellite",
      "why": "Clean water and light trades; small northwest swell wraps in but stays under the shelf."
    }
  ],
  "tide_events": [{ "type": "high", "time": "2026-10-06T13:42:00-10:00", "height_ft": 1.8 }],
  "sources": ["ndbc:51201", "noaa-tides:1612340", "pacioos-ww3", "openweather"],
  "links": { "page": "https://kaicast.com/conditions/electric-beach", "app": "https://kaicast.com/spot/electric-beach", "docs": "https://kaicast.com/openapi.json" },
  "attribution": "KaiCast (https://kaicast.com). …"
}

now is the current-hour nowcast when today is in range. The period containing now is kept; fully elapsed periods are dropped.

GET /api/v1/spots #

The spot directory — ids, names, regions, coordinates. No conditions.

ParameterNotes
regionRegion id, e.g. us-hi-maui
qName search
lat, lonSort by distance; each spot gets distance_km
bash
curl "https://kaicast.com/api/v1/spots?region=us-hi-maui"

GET /api/v1/coverage #

Which regions are covered, whether each supports decisions (decision_support: "full") or facts only ("conditions_only"), its spots and bounding box, and the activities with how directly each is modeled.

bash
curl "https://kaicast.com/api/v1/coverage"

GET /api/v1/recommendations #

Ranked picks for one activity, in one area, on one spot-local day.

ParameterTypeNotes
activityenumRequired. snorkel, dive, freedive, spearfish, surf
region / spot / lat+lonOne location. Picks never span regions.
radius_kmnumberAround lat/lon. Default 40, max 100.
datedateUp to 6 days ahead. Default today — or tomorrow once today's daylight windows have passed, flagged by date_rolled_forward: true and a message. An explicit date is never moved.
limitintegerDefault 3, max 5.
bash
curl "https://kaicast.com/api/v1/recommendations?region=us-hi-oahu&activity=snorkel&date=2026-10-06&limit=2"
json
{
  "schema": "kaicast.recommendation.v1",
  "status": "ok",
  "activity": { "id": "snorkel", "label": "Snorkeling", "model": "native", "note": "Scored by the KaiCast condition model, which is calibrated for snorkeling." },
  "date": "2026-10-06",
  "date_label": "Tomorrow",
  "coverage": { "status": "covered", "regions": ["us-hi-oahu"] },
  "picks": [
    {
      "rank": 1,
      "spot": { "id": "electric-beach", "name": "Electric Beach", "region": { "id": "us-hi-oahu", "name": "Oʻahu" }, "lat": 21.355, "lon": -158.131 },
      "distance_km": null,
      "date": "2026-10-06",
      "score": 72, "rating": "great",
      "best_window": { "start": "2026-10-06T08:00:00-10:00", "end": "2026-10-06T11:00:00-10:00", "start_local": "08:00", "end_local": "11:00", "score": 72, "rating": "great" },
      "why": {
        "summary": "Great: 45 ft visibility, 2.1 ft swell at 12s from the NW, 8 kt wind from the NE.",
        "factors": ["45 ft visibility", "2.1 ft swell at 12s from the NW", "8 kt wind from the NE", "rising tide"],
        "model_note": "Clean water and light trades; small northwest swell wraps in but stays under the shelf."
      },
      "metrics": { "visibility_ft": 45, "swell_height_ft": 2.1, "swell_period_s": 12, "swell_from": "NW", "wind_speed_kt": 8, "wind_from": "NE", "water_temp_f": 81, "tide_state": "rising" },
      "confidence": { "value": 0.74, "label": "high", "factors": { "model": 0.82, "visibility_source": 1, "freshness": 1, "lead_time": 0.95, "activity_fit": 1 } },
      "caveats": [],
      "card": { "…": "see Spot card below" }
    }
  ],
  "cards": ["…"],
  "card_markdown": "**Electric Beach** · Oʻahu — Tomorrow\nGreat (72/100) · best 08:00–11:00\n…",
  "disclaimer": "KaiCast forecasts are model estimates, not a safety guarantee. …",
  "pricing": { "layer": "decision", "plan": "free_early_access" }
}

How picks are made. Each spot's value for the day is its best daylight period (starting 06:00–17:59 local). Spots are ranked by that period's score; ties break on what matters most for the activity (calmer surface for snorkeling, clearer water for diving). Spots unsuited to the activity are excluded and listed in considered.excluded (e.g. deep boat dives aren't snorkel picks).

GET /api/v1/best-day #

Which day in a window is best at a destination.

ParameterTypeNotes
activityenumRequired.
spot / region / lat+lonOne destination. A region or radius picks the best spot per day.
from, todateDefault today … +6. At most 7 days; a past from is clamped to today.
bash
curl "https://kaicast.com/api/v1/best-day?spot=hanauma-bay&activity=snorkel"

The response has best (a full pick, plus why.versus_next — e.g. "9 points ahead of the next-best option (2026-10-05, 72/100)"), runner_up, and days[] with every day's best score, spot and window.

Statuses — honest answers, not errors #

Decision endpoints return 200 with a status for anything that isn't a malformed request:

statusMeaningWhat to do
okPicks are present.Show them, with caveats.
no_dataCovered, but no daylight forecast for that date (e.g. an explicit date of today, asked after dark).Try another date.
not_coveredOutside KaiCast coverage. coverage.nearest_covered names the closest covered spot and distance.Tell the user KaiCast doesn't cover it yet.
conditions_onlyRegion has facts but no calibrated visibility model.Use /api/v1/conditions.
activity_not_modeledE.g. surf — KaiCast doesn't model breaks. facts_hint points to the conditions endpoint.Use swell/wind facts directly.

Activities #

ActivityModelMeaning
snorkelnativeThe condition score is calibrated for snorkeling.
diveproxySame score; caveat added; confidence × 0.85. Conditions at depth aren't scored.
freediveproxySame score; visibility breaks ties.
spearfishproxySame score. Spots in Hawaiʻi DAR areas closed to spearfishing are excluded (considered.excluded, reason closed_to_spearfishing); bag limits and seasons are not modeled — a caveat says so.
surfunsupportedNot ranked. Use the facts.

Confidence and caveats #

confidence.value (0–1) is the product of its factors — the model's own visibility confidence, visibility source (satellite 1.0, estimated 0.8), input freshness, lead time (−5%/day), and activity fit (native 1.0, proxy 0.85). label is high ≥ 0.65, medium ≥ 0.4, else low.

caveats[] items have a stable code and a human message: visibility_estimated, stale_inputs, long_lead_time, marginal_conditions, location_snapped, proxy_activity_model, verify_fishing_rules, regulated_area, regulations_not_checked.

Spot card #

Every decision pick carries card, and the response carries cards[] and card_markdown. Render it as-is so users see the same card they'd see in the KaiCast app:

json
{
  "version": 1, "spot_id": "electric-beach", "spot_name": "Electric Beach", "region_name": "Oʻahu",
  "date": "2026-10-06", "date_label": "Tomorrow", "activity": "snorkel",
  "tier": "great", "tier_label": "Great", "tier_color": "#27D667", "score": 72,
  "headline": "Great: 45 ft visibility, 2.1 ft swell at 12s from the NW, 8 kt wind from the NE.",
  "best_window": { "start_local": "08:00", "end_local": "11:00" },
  "metrics": { "visibility": "45 ft", "swell": "2.1 ft @ 12s", "wind": "8 kt NE", "water_temp": "81°F" },
  "image_url": "https://kaicast.com/generateSpotShareImage?spot=electric-beach&date=2026-10-06",
  "links": {
    "share": "https://kaicast.com/s/electric-beach/2026-10-06?utm_source=agent&utm_medium=api&utm_campaign=spot_card_snorkel",
    "app": "kaicast://spot/electric-beach/2026-10-06",
    "forecast": "https://kaicast.com/spot/electric-beach?utm_source=agent&utm_medium=api&utm_campaign=spot_card_snorkel"
  },
  "cta": { "label": "Full forecast in KaiCast", "text": "Hour-by-hour windows, 7-day outlook and live hazard alerts in the KaiCast app.", "url": "https://kaicast.com/spot/electric-beach?…" },
  "attribution": "Conditions by KaiCast",
  "credits_text": "Weather data provided by OpenWeather · Buoy observations: NOAA NDBC · Data provided by PacIOOS (www.pacioos.org)"
}

credits_text is the licence-required upstream credit line — render it with the card. Each pick also carries data_credits (structured), and the response carries their union.

image_url is a 1200×630 PNG of the same card, for surfaces that show images. tier_color values: excellent #09A1FB, great #27D667, good #FFD321, fair #FF9D25, no-go #F73726.

Errors #

Malformed requests return RFC 9457 application/problem+json with a stable code:

json
{
  "type": "https://kaicast.com/openapi.json#problem-unknown_activity",
  "title": "unknown activity",
  "status": 400,
  "detail": "Unknown activity \"kitesurf\". Use one of snorkel, dive, freedive, spearfish, surf.",
  "code": "unknown_activity"
}
StatusCodes
400missing_location, ambiguous_location, invalid_coordinates, invalid_date, invalid_range, range_too_long, missing_activity, unknown_activity, invalid_limit, invalid_radius, one_spot_per_request
401invalid_key, key_revoked, key_required (decisions, once metering starts)
402free_allotment_exhausted — body includes upgrade_url
404unknown_spot, no_spot_nearby (conditions with lat/lon)
422outside_horizon — dates beyond the 7-day forecast
429rate_limited — honor Retry-After
503data_unavailable / unavailable — retry after Retry-After

Limits #

KaiCast serves answers point by point — one spot, one region, or one radius per call, for at most 7 days. There is no bulk export.

Data refreshes hourly (next_update_after); polling faster than that returns the same answer.