{
 "openapi": "3.1.0",
 "info": {
  "title": "Pohoda API",
  "version": "1.0.0",
  "summary": "Climate, seasonal outlook and crowd data for trip planning",
  "description": "Read-only API from pohoda.app. Every endpoint takes a latitude and longitude. Climate values are 30-year normals in 26 fortnights, not a forecast. Credit pohoda.app and the Copernicus Climate Change Service; each response lists the exact attribution lines.",
  "termsOfService": "https://pohoda.app/legal.html"
 },
 "servers": [
  {
   "url": "https://api.pohoda.app"
  }
 ],
 "security": [
  {},
  {
   "apiKey": []
  },
  {
   "bearer": []
  }
 ],
 "paths": {
  "/v1/point": {
   "get": {
    "operationId": "getPoint",
    "summary": "Normal climate and trip match scores at a location",
    "description": "Use for 'best time to visit X' and 'what is the weather normally like in X in May'. A point on water near the shore is moved to the nearest land cell.",
    "parameters": [
     {
      "name": "lat",
      "in": "query",
      "required": true,
      "description": "Latitude in decimal degrees. Data covers about -60 to 75.",
      "schema": {
       "type": "number",
       "minimum": -90,
       "maximum": 90
      },
      "example": 38.72
     },
     {
      "name": "lon",
      "in": "query",
      "required": true,
      "description": "Longitude in decimal degrees.",
      "schema": {
       "type": "number",
       "minimum": -180,
       "maximum": 180
      },
      "example": -9.14
     },
     {
      "name": "trip",
      "in": "query",
      "required": false,
      "description": "Trip type. Omit for all four.",
      "schema": {
       "type": "string",
       "enum": [
        "beach",
        "city",
        "outdoor",
        "snow"
       ]
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Climate and scores",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Point"
        }
       }
      }
     },
     "400": {
      "description": "Invalid, unknown or repeated parameter",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "API key not recognised",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "403": {
      "description": "Key not allowed from this web origin",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Outside coverage, or open sea far from land",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit reached",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      },
      "headers": {
       "Retry-After": {
        "schema": {
         "type": "integer"
        },
        "description": "Seconds to wait"
       }
      }
     },
     "502": {
      "description": "Data temporarily unavailable",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/v1/outlook": {
   "get": {
    "operationId": "getOutlook",
    "summary": "Seasonal outlook for the coming months",
    "description": "Chances of a colder, normal or warmer (drier, normal or wetter) month than usual for the next 3 months, on a 1 degree grid (nearest cell). Use only months with moderate or high confidence.",
    "parameters": [
     {
      "name": "lat",
      "in": "query",
      "required": true,
      "description": "Latitude in decimal degrees. Data covers about -60 to 75.",
      "schema": {
       "type": "number",
       "minimum": -90,
       "maximum": 90
      },
      "example": 38.72
     },
     {
      "name": "lon",
      "in": "query",
      "required": true,
      "description": "Longitude in decimal degrees.",
      "schema": {
       "type": "number",
       "minimum": -180,
       "maximum": 180
      },
      "example": -9.14
     }
    ],
    "responses": {
     "200": {
      "description": "Outlook",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Outlook"
        }
       }
      }
     },
     "400": {
      "description": "Invalid, unknown or repeated parameter",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "API key not recognised",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "403": {
      "description": "Key not allowed from this web origin",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit reached",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      },
      "headers": {
       "Retry-After": {
        "schema": {
         "type": "integer"
        },
        "description": "Seconds to wait"
       }
      }
     },
     "502": {
      "description": "Data temporarily unavailable",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/v1/region": {
   "get": {
    "operationId": "getRegion",
    "summary": "Crowd seasonality, sweet spot and tourism density",
    "description": "How busy the region around a location is through the year relative to its own average. Crowd figures compare dates within one region and must not be compared between regions.",
    "parameters": [
     {
      "name": "lat",
      "in": "query",
      "required": true,
      "description": "Latitude in decimal degrees. Data covers about -60 to 75.",
      "schema": {
       "type": "number",
       "minimum": -90,
       "maximum": 90
      },
      "example": 38.72
     },
     {
      "name": "lon",
      "in": "query",
      "required": true,
      "description": "Longitude in decimal degrees.",
      "schema": {
       "type": "number",
       "minimum": -180,
       "maximum": 180
      },
      "example": -9.14
     },
     {
      "name": "trip",
      "in": "query",
      "required": false,
      "description": "Trip type. Omit for all four.",
      "schema": {
       "type": "string",
       "enum": [
        "beach",
        "city",
        "outdoor",
        "snow"
       ]
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Region answer",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Region"
        }
       }
      }
     },
     "400": {
      "description": "Invalid, unknown or repeated parameter",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "API key not recognised",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "403": {
      "description": "Key not allowed from this web origin",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Outside coverage",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit reached",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      },
      "headers": {
       "Retry-After": {
        "schema": {
         "type": "integer"
        },
        "description": "Seconds to wait"
       }
      }
     },
     "502": {
      "description": "Data temporarily unavailable",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/v1/health": {
   "get": {
    "operationId": "getHealth",
    "summary": "Liveness check",
    "security": [
     {}
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "status": {
           "type": "string"
          },
          "version": {
           "type": "string"
          }
         }
        }
       }
      }
     }
    }
   }
  }
 },
 "components": {
  "securitySchemes": {
   "apiKey": {
    "type": "apiKey",
    "in": "header",
    "name": "X-API-Key"
   },
   "bearer": {
    "type": "http",
    "scheme": "bearer"
   }
  },
  "schemas": {
   "Error": {
    "type": "object",
    "properties": {
     "error": {
      "type": "object",
      "properties": {
       "code": {
        "type": "string"
       },
       "message": {
        "type": "string"
       },
       "param": {
        "type": "string"
       },
       "docs": {
        "type": "string"
       }
      },
      "required": [
       "code",
       "message"
      ]
     }
    },
    "required": [
     "error"
    ]
   },
   "Fortnight": {
    "type": "object",
    "properties": {
     "index": {
      "type": "integer",
      "minimum": 1,
      "maximum": 26
     },
     "label": {
      "type": "string"
     }
    },
    "required": [
     "index",
     "label"
    ]
   },
   "Point": {
    "type": "object",
    "properties": {
     "cell": {
      "type": "object",
      "description": "The grid cell the answer is for",
      "properties": {
       "lat": {
        "type": "number"
       },
       "lon": {
        "type": "number"
       },
       "resolution_deg": {
        "type": "number"
       },
       "surface": {
        "type": "string",
        "enum": [
         "land",
         "sea"
        ]
       },
       "snapped_to_land_km": {
        "type": [
         "integer",
         "null"
        ],
        "description": "Set when the point was on water and the nearest land cell was used"
       }
      }
     },
     "basis": {
      "type": "string"
     },
     "fortnights": {
      "type": "array",
      "minItems": 26,
      "maxItems": 26,
      "items": {
       "type": "object",
       "properties": {
        "index": {
         "type": "integer"
        },
        "label": {
         "type": "string"
        },
        "start_doy": {
         "type": "integer"
        },
        "end_doy": {
         "type": "integer"
        }
       }
      }
     },
     "climate": {
      "type": "object",
      "description": "Keyed by variable: temp_max, temp_min, temp_mean, temp_range, precipitation, wet_days, sunshine, wind, humidity, sea_temp, snow_days, snow_depth, frost_days",
      "additionalProperties": {
       "type": "object",
       "properties": {
        "unit": {
         "type": [
          "string",
          "null"
         ]
        },
        "about": {
         "type": "string"
        },
        "values": {
         "type": "array",
         "minItems": 26,
         "maxItems": 26,
         "items": {
          "type": [
           "number",
           "null"
          ]
         }
        }
       }
      }
     },
     "trip_scores": {
      "type": [
       "object",
       "null"
      ],
      "description": "Keyed by trip type. Null at sea.",
      "additionalProperties": {
       "type": "object",
       "properties": {
        "scores": {
         "type": "array",
         "minItems": 26,
         "maxItems": 26,
         "items": {
          "type": [
           "number",
           "null"
          ]
         },
         "description": "0 to 100 per fortnight"
        },
        "peak": {
         "description": "First fortnight with the highest score; null when nothing scores above 0",
         "oneOf": [
          {
           "type": "null"
          },
          {
           "allOf": [
            {
             "$ref": "#/components/schemas/Fortnight"
            },
            {
             "type": "object",
             "properties": {
              "score": {
               "type": "integer"
              }
             }
            }
           ]
          }
         ]
        },
        "good_periods": {
         "type": "array",
         "description": "Runs of consecutive fortnights scoring at least 60, joined across the turn of the year",
         "items": {
          "type": "object",
          "properties": {
           "from": {
            "$ref": "#/components/schemas/Fortnight"
           },
           "to": {
            "$ref": "#/components/schemas/Fortnight"
           },
           "fortnights": {
            "type": "integer"
           }
          }
         }
        },
        "never_suitable": {
         "type": "object",
         "description": "Present when no fortnight scores above 0",
         "properties": {
          "because": {
           "type": "string",
           "description": "The climate variable whose requirement rules out most of the year"
          },
          "rules_out_fortnights": {
           "type": "integer"
          },
          "of": {
           "type": "integer"
          }
         }
        }
       }
      }
     },
     "score_scale": {
      "type": "string"
     },
     "attribution": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "method": {
      "type": "string"
     },
     "docs": {
      "type": "string"
     }
    }
   },
   "OutlookValue": {
    "type": [
     "object",
     "null"
    ],
    "properties": {
     "prob_below": {
      "type": "integer"
     },
     "prob_normal": {
      "type": "integer"
     },
     "prob_above": {
      "type": "integer"
     },
     "anomaly_c": {
      "type": "number",
      "description": "Expected departure from normal, degrees C. Not given for precipitation."
     },
     "skill": {
      "type": [
       "number",
       "null"
      ]
     },
     "confidence": {
      "type": "string",
      "enum": [
       "low",
       "moderate",
       "high"
      ]
     },
     "headline": {
      "type": "string"
     }
    }
   },
   "Outlook": {
    "type": "object",
    "properties": {
     "cell": {
      "type": "object",
      "properties": {
       "lat": {
        "type": "number"
       },
       "lon": {
        "type": "number"
       },
       "resolution_deg": {
        "type": "number"
       }
      }
     },
     "issued": {
      "type": "string",
      "description": "Month the forecast was issued, YYYY-MM"
     },
     "stale": {
      "type": "boolean",
      "description": "True when the forecast was issued before the current month; all confidence is then low"
     },
     "models": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "months": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "month": {
         "type": "string"
        },
        "lead_months": {
         "type": "integer"
        },
        "temperature": {
         "$ref": "#/components/schemas/OutlookValue"
        },
        "precipitation": {
         "$ref": "#/components/schemas/OutlookValue"
        },
        "sea_temperature": {
         "$ref": "#/components/schemas/OutlookValue"
        }
       }
      }
     },
     "how_to_read": {
      "type": "string"
     },
     "attribution": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "method": {
      "type": "string"
     },
     "docs": {
      "type": "string"
     }
    }
   },
   "Region": {
    "type": "object",
    "properties": {
     "country": {
      "type": [
       "string",
       "null"
      ],
      "description": "ISO 3166-1 alpha-2"
     },
     "region": {
      "type": [
       "object",
       "null"
      ],
      "properties": {
       "code": {
        "type": "string"
       },
       "level": {
        "type": "string",
        "enum": [
         "nuts2",
         "admin1",
         "park"
        ]
       },
       "nearby": {
        "type": "boolean"
       },
       "name": {
        "type": "string"
       },
       "distance_km": {
        "type": "integer"
       }
      }
     },
     "crowd": {
      "type": [
       "object",
       "null"
      ],
      "properties": {
       "index": {
        "type": "array",
        "minItems": 26,
        "maxItems": 26,
        "items": {
         "type": [
          "number",
          "null"
         ]
        }
       },
       "scale": {
        "type": "string"
       },
       "busiest": {
        "type": "object",
        "properties": {
         "index": {
          "type": "integer",
          "minimum": 1,
          "maximum": 26
         },
         "label": {
          "type": "string"
         }
        },
        "required": [
         "index",
         "label"
        ]
       },
       "quietest": {
        "type": "object",
        "properties": {
         "index": {
          "type": "integer",
          "minimum": 1,
          "maximum": 26
         },
         "label": {
          "type": "string"
         }
        },
        "required": [
         "index",
         "label"
        ]
       },
       "quality": {
        "type": "object",
        "properties": {
         "tier": {
          "type": [
           "string",
           "null"
          ],
          "description": "A = observed for this region; B = national figure applied to the region"
         },
         "metric": {
          "type": [
           "string",
           "null"
          ]
         },
         "weak": {
          "type": "boolean"
         }
        }
       },
       "source": {
        "type": [
         "string",
         "null"
        ]
       }
      }
     },
     "crowd_unavailable_reason": {
      "type": [
       "string",
       "null"
      ]
     },
     "sweet_spot": {
      "type": [
       "object",
       "null"
      ],
      "description": "Keyed by trip type; null where no fortnight scores at least 60",
      "additionalProperties": {
       "type": [
        "object",
        "null"
       ]
      }
     },
     "sweet_spot_rule": {
      "type": "string"
     },
     "tourism_density": {
      "type": [
       "object",
       "null"
      ],
      "properties": {
       "region": {
        "type": "string"
       },
       "nights_per_resident": {
        "type": "number"
       },
       "rank": {
        "type": [
         "integer",
         "null"
        ]
       },
       "of": {
        "type": [
         "integer",
         "null"
        ]
       },
       "year": {
        "type": "integer"
       },
       "about": {
        "type": "string"
       }
      }
     },
     "attribution": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "method": {
      "type": "string"
     },
     "docs": {
      "type": "string"
     }
    }
   }
  }
 }
}