{
  "openapi": "3.0.3",
  "info": {
    "title": "weatherAI.io Decision API",
    "version": "1.0.0",
    "description": "One call turns the weather into a decision: a verdict, a 0-100 score, the best time windows and the risks for an activity at a place and date. 1,000 free calls every month, then £0.0012 per successful call from prepaid credit (VAT included). Failed calls are not charged. AI agents can also use the MCP server at https://api.weatherai.io/mcp."
  },
  "servers": [
    {
      "url": "https://api.weatherai.io"
    }
  ],
  "components": {
    "securitySchemes": {
      "ApiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "Window": {
        "type": "object",
        "properties": {
          "start": {
            "type": "string",
            "example": "2026-10-18 07:00"
          },
          "end": {
            "type": "string",
            "example": "2026-10-18 10:00"
          },
          "score": {
            "type": "integer",
            "example": 91
          }
        }
      },
      "Risk": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "rain"
          },
          "severity": {
            "type": "string",
            "enum": [
              "low",
              "moderate",
              "high",
              "severe",
              "extreme"
            ]
          },
          "from": {
            "type": "string"
          },
          "to": {
            "type": "string"
          },
          "detail": {
            "type": "string",
            "example": "70% chance of rain"
          }
        }
      },
      "Decision": {
        "type": "object",
        "properties": {
          "location": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "region": {
                "type": "string"
              },
              "country": {
                "type": "string"
              },
              "lat": {
                "type": "number"
              },
              "lon": {
                "type": "number"
              },
              "tz_id": {
                "type": "string"
              },
              "localtime": {
                "type": "string"
              }
            }
          },
          "activity": {
            "type": "string",
            "enum": [
              "running",
              "cycling",
              "outdoor-event",
              "beach",
              "laundry-drying",
              "stargazing",
              "hiking",
              "golf",
              "tennis",
              "dog-walking",
              "gardening",
              "fishing",
              "sailing",
              "drone-flying",
              "exterior-painting",
              "concrete-pour",
              "roofing",
              "crop-spraying",
              "outdoor-work",
              "driving"
            ]
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "verdict": {
            "type": "string",
            "enum": [
              "great",
              "good",
              "fair",
              "poor",
              "avoid"
            ]
          },
          "score": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "Quality of the best opportunity on that date (best window)."
          },
          "best_windows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Window"
            }
          },
          "risks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Risk"
            }
          },
          "summary": {
            "type": "string",
            "example": "Great conditions for a run in London; best 07:00–10:00. Watch for rain from 14:00."
          },
          "data": {
            "type": "object",
            "additionalProperties": true,
            "description": "Key figures for the hours considered (temperatures, rain, wind, UV, sunrise/sunset; moon for stargazing)."
          },
          "hourly": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "time": {
                  "type": "string"
                },
                "score": {
                  "type": "integer"
                },
                "main_issue": {
                  "type": "string",
                  "nullable": true
                }
              }
            },
            "description": "Only with detail=hourly."
          },
          "billing": {
            "type": "object",
            "properties": {
              "charged": {
                "type": "string",
                "enum": [
                  "free",
                  "credit"
                ]
              },
              "cost_gbp": {
                "type": "string",
                "example": "0.0012"
              },
              "free_remaining": {
                "type": "integer"
              },
              "balance_gbp": {
                "type": "string"
              }
            }
          }
        }
      },
      "DayRanking": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "format": "date"
          },
          "weekday": {
            "type": "string"
          },
          "verdict": {
            "type": "string",
            "enum": [
              "great",
              "good",
              "fair",
              "poor",
              "avoid"
            ]
          },
          "score": {
            "type": "integer"
          },
          "best_window": {
            "$ref": "#/components/schemas/Window"
          },
          "top_risks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Risk"
            }
          },
          "summary": {
            "type": "string"
          }
        }
      },
      "BestDay": {
        "type": "object",
        "properties": {
          "location": {
            "type": "object"
          },
          "activity": {
            "type": "string"
          },
          "days_checked": {
            "type": "integer"
          },
          "summary": {
            "type": "string"
          },
          "best": {
            "$ref": "#/components/schemas/DayRanking"
          },
          "days": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DayRanking"
            }
          },
          "billing": {
            "type": "object"
          }
        }
      }
    }
  },
  "paths": {
    "/v1/decide": {
      "get": {
        "operationId": "decide",
        "summary": "Decide whether and when to do an activity",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "name": "location",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "City name, 'lat,lon', postcode, zip code or IP address.",
            "example": "London"
          },
          {
            "name": "activity",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "running",
                "cycling",
                "outdoor-event",
                "beach",
                "laundry-drying",
                "stargazing",
                "hiking",
                "golf",
                "tennis",
                "dog-walking",
                "gardening",
                "fishing",
                "sailing",
                "drone-flying",
                "exterior-painting",
                "concrete-pour",
                "roofing",
                "crop-spraying",
                "outdoor-work",
                "driving"
              ]
            },
            "description": "See GET /v1/activities. Aliases such as run, bike, bbq, wedding, laundry are accepted."
          },
          {
            "name": "when",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "today (default), tomorrow or yyyy-MM-dd, up to 13 days ahead.",
            "example": "tomorrow"
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 23
            },
            "description": "Only consider hours from this hour (local time)."
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 24
            },
            "description": "Only consider hours before this hour (local time)."
          },
          {
            "name": "units",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "metric",
                "imperial"
              ]
            }
          },
          {
            "name": "detail",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "hourly"
              ]
            },
            "description": "Add an hour-by-hour score."
          }
        ],
        "responses": {
          "200": {
            "description": "Decision. Headers X-Free-Calls-Remaining, X-Credit-Balance-GBP and X-Call-Cost-GBP show billing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Decision"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters (not charged)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Free calls used up and no credit left: top up",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Email not verified or account suspended",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Location not found (not charged)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "No forecast hours left for that date/time range (not charged)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests per second (not charged)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Weather data temporarily unavailable (not charged)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/best-day": {
      "get": {
        "operationId": "bestDay",
        "summary": "Which of the next days is best for an activity",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "parameters": [
          {
            "name": "location",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "City name, 'lat,lon', postcode, zip code or IP address.",
            "example": "London"
          },
          {
            "name": "activity",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "running",
                "cycling",
                "outdoor-event",
                "beach",
                "laundry-drying",
                "stargazing",
                "hiking",
                "golf",
                "tennis",
                "dog-walking",
                "gardening",
                "fishing",
                "sailing",
                "drone-flying",
                "exterior-painting",
                "concrete-pour",
                "roofing",
                "crop-spraying",
                "outdoor-work",
                "driving"
              ]
            },
            "description": "See GET /v1/activities. Aliases such as run, bike, bbq, wedding, laundry are accepted."
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 14,
              "default": 7
            },
            "description": "How many days to compare, starting today."
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 23
            },
            "description": "Only consider hours from this hour (local time)."
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 24
            },
            "description": "Only consider hours before this hour (local time)."
          },
          {
            "name": "units",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "metric",
                "imperial"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The days ranked, with the best one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BestDay"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters (not charged)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Free calls used up and no credit left: top up",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Email not verified or account suspended",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Location not found (not charged)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "No forecast hours left for that date/time range (not charged)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests per second (not charged)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Weather data temporarily unavailable (not charged)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "description": "Compares up to 14 days (default 7, starting today in local time) for one activity and ranks them. Costs one call however many days are compared."
      }
    },
    "/v1/activities": {
      "get": {
        "operationId": "listActivities",
        "summary": "List the activities you can ask about (free, no key)",
        "responses": {
          "200": {
            "description": "Activities"
          }
        }
      }
    },
    "/v1/usage": {
      "get": {
        "operationId": "usage",
        "summary": "Free calls left this month and credit balance (free)",
        "security": [
          {
            "ApiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Usage"
          },
          "401": {
            "description": "Invalid key"
          }
        }
      }
    }
  }
}
