{
  "openapi": "3.0.3",
  "info": {
    "title": "Should I Fly a Kite? API",
    "description": "Free, open kite-flying weather API. Live forecasts, baked monthly climate averages for 500+ cities, and geocoding. No API key, no cookies, no tracking. Content pages can also be fetched as markdown by sending `Accept: text/markdown`.",
    "version": "1.0.0",
    "contact": {
      "name": "Should I Fly a Kite",
      "url": "https://shouldiflyakite.com"
    }
  },
  "servers": [
    {
      "url": "https://shouldiflyakite.com"
    }
  ],
  "paths": {
    "/api/weather": {
      "get": {
        "summary": "Live kite-flying forecast for a coordinate",
        "description": "Proxies the Open-Meteo forecast for a latitude/longitude in mph: current conditions plus a two-day hourly timeline (temperature, weather code, wind speed, wind gusts, precipitation probability). Compute a 0-100 kite score from wind 45%, gusts 25%, rain 20%, temperature 10%.",
        "operationId": "getWeather",
        "parameters": [
          {
            "name": "lat",
            "in": "query",
            "required": true,
            "schema": { "type": "number" },
            "description": "Latitude, e.g. 51.5074"
          },
          {
            "name": "lon",
            "in": "query",
            "required": true,
            "schema": { "type": "number" },
            "description": "Longitude, e.g. -0.1278"
          }
        ],
        "responses": {
          "200": {
            "description": "Open-Meteo forecast JSON (current + hourly for 2 days), in mph"
          },
          "400": {
            "description": "Missing lat or lon"
          },
          "502": {
            "description": "Upstream Open-Meteo unreachable"
          }
        }
      }
    },
    "/api/climate": {
      "get": {
        "summary": "Baked monthly climate averages for all cities",
        "description": "Returns the full climate dataset as an array of CityClimate records, or a single city's monthly data when ?city is given. Data is baked at build time from ~3 years of daily Open-Meteo data, so it never rate-limits or blocks crawlers.",
        "operationId": "getClimate",
        "parameters": [
          {
            "name": "city",
            "in": "query",
            "required": false,
            "schema": { "type": "string" },
            "description": "City slug, e.g. london. Omit for all cities."
          }
        ],
        "responses": {
          "200": {
            "description": "Array of CityClimate records, or a single CityClimate when ?city is set"
          },
          "404": {
            "description": "Unknown city slug"
          }
        }
      }
    },
    "/api/geocode": {
      "get": {
        "summary": "City search / geocoding",
        "description": "Proxies the Open-Meteo geocoding search by place name, returning candidate cities with coordinates.",
        "operationId": "getGeocode",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": { "type": "string" },
            "description": "Place name to search, e.g. San Francisco"
          }
        ],
        "responses": {
          "200": {
            "description": "Open-Meteo geocoding results JSON"
          },
          "400": {
            "description": "Missing q"
          },
          "502": {
            "description": "Upstream Open-Meteo unreachable"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "CityClimate": {
        "type": "object",
        "properties": {
          "name": { "type": "string" },
          "country": { "type": "string" },
          "slug": { "type": "string" },
          "source": { "type": "string", "description": "Description of the underlying data window" },
          "months": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/ClimateMonth" }
          }
        }
      },
      "ClimateMonth": {
        "type": "object",
        "properties": {
          "month": { "type": "integer", "minimum": 1, "maximum": 12 },
          "temp_c": { "type": "number" },
          "precip_mm": { "type": "number" },
          "wind_mph": { "type": "number" },
          "gust_mph": { "type": "number" },
          "good_days": { "type": "integer", "description": "Days with wind 8-25 mph, gusts under 31 mph and under 2 mm rain" }
        }
      }
    }
  }
}
