{
  "openapi": "3.1.0",
  "info": {
    "title": "cloudiusz open API",
    "version": "1.0.0",
    "summary": "Hour-by-hour weather for all of Europe, straight from the DWD ICON-EU model.",
    "description": "Keyless, read-only, rate-limited per client address. Every answer carries a `meta` block with the attribution the data licence (CC BY 4.0) asks you to show. Human documentation with examples: `/api`.",
    "license": { "name": "Forecast data: DWD ICON-EU, CC BY 4.0", "url": "https://creativecommons.org/licenses/by/4.0/" }
  },
  "servers": [ { "url": "/api/v1" } ],
  "tags": [
    { "name": "discovery", "description": "What this API is and what it currently serves" },
    { "name": "catalogue", "description": "Releases, layers, legends and condition highlights" },
    { "name": "places", "description": "The bundled GeoNames gazetteer" },
    { "name": "weather", "description": "Forecasts at a point" },
    { "name": "tiles", "description": "Weather layers as PNG map tiles" }
  ],
  "paths": {
    "/": {
      "get": {
        "tags": ["discovery"], "operationId": "index", "summary": "Index: links, current rate limits, attribution",
        "responses": { "200": { "description": "OK", "headers": { "$ref": "#/components/x-rateLimitHeaders" }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/IndexResponse" } } } },
          "403": { "$ref": "#/components/responses/Forbidden" }, "429": { "$ref": "#/components/responses/RateLimited" }, "503": { "$ref": "#/components/responses/Disabled" } }
      }
    },
    "/status": {
      "get": {
        "tags": ["discovery"], "operationId": "status", "summary": "Current release, serve window and domain",
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StatusResponse" } } } },
          "403": { "$ref": "#/components/responses/Forbidden" }, "429": { "$ref": "#/components/responses/RateLimited" }, "503": { "$ref": "#/components/responses/Disabled" } }
      }
    },
    "/releases": {
      "get": {
        "tags": ["catalogue"], "operationId": "releases", "summary": "Readable model runs",
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReleasesResponse" } } } },
          "429": { "$ref": "#/components/responses/RateLimited" }, "503": { "$ref": "#/components/responses/Disabled" } }
      }
    },
    "/releases/{id}": {
      "get": {
        "tags": ["catalogue"], "operationId": "release", "summary": "One release with every frame time it holds",
        "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "example": "icon-eu-2026091600-r1" } ],
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReleaseDetail" } } } },
          "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" } }
      }
    },
    "/layers": {
      "get": {
        "tags": ["catalogue"], "operationId": "layers", "summary": "Tile layers, their field and unit, the maximum zoom",
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LayersResponse" } } } },
          "429": { "$ref": "#/components/responses/RateLimited" } }
      }
    },
    "/legends": {
      "get": {
        "tags": ["catalogue"], "operationId": "legends", "summary": "Colour stops of each layer's default palette",
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LegendsResponse" } } } },
          "429": { "$ref": "#/components/responses/RateLimited" } }
      }
    },
    "/conditions": {
      "get": {
        "tags": ["catalogue"], "operationId": "conditions", "summary": "Condition highlights (wet, dry, heavy rain, freezing, strong wind, snow)",
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConditionsResponse" } } } },
          "429": { "$ref": "#/components/responses/RateLimited" } }
      }
    },
    "/places": {
      "get": {
        "tags": ["places"], "operationId": "places", "summary": "Search the bundled gazetteer",
        "parameters": [ { "name": "q", "in": "query", "required": true, "description": "A place name prefix, or `lat, lon`", "schema": { "type": "string" }, "example": "Warsz" } ],
        "responses": { "200": { "description": "Up to 8 matches", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlacesResponse" } } } },
          "429": { "$ref": "#/components/responses/RateLimited" } }
      }
    },
    "/places/near": {
      "get": {
        "tags": ["places"], "operationId": "placeNear", "summary": "The nearest known place to a point",
        "parameters": [ { "$ref": "#/components/parameters/lat" }, { "$ref": "#/components/parameters/lon" } ],
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceResponse" } } } },
          "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" } }
      }
    },
    "/forecast": {
      "get": {
        "tags": ["weather"], "operationId": "forecast", "summary": "Every forecast hour of a release at a point",
        "parameters": [
          { "$ref": "#/components/parameters/lat" }, { "$ref": "#/components/parameters/lon" }, { "$ref": "#/components/parameters/release" },
          { "name": "from", "in": "query", "description": "First hour to return (ISO-8601 instant); default = the run time", "schema": { "type": "string", "format": "date-time" } },
          { "name": "hours", "in": "query", "description": "How many hours to return; capped at the configured maximum (78)", "schema": { "type": "integer", "minimum": 1 } }
        ],
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ForecastResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" }, "404": { "$ref": "#/components/responses/NotFound" },
          "422": { "$ref": "#/components/responses/NoCoverage" }, "429": { "$ref": "#/components/responses/RateLimited" }, "503": { "$ref": "#/components/responses/Unavailable" } }
      }
    },
    "/forecast/daily": {
      "get": {
        "tags": ["weather"], "operationId": "forecastDaily", "summary": "The forecast folded into UTC days, as the Forecast view's day cards",
        "parameters": [ { "$ref": "#/components/parameters/lat" }, { "$ref": "#/components/parameters/lon" }, { "$ref": "#/components/parameters/release" } ],
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DailyForecastResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" }, "404": { "$ref": "#/components/responses/NotFound" },
          "422": { "$ref": "#/components/responses/NoCoverage" }, "429": { "$ref": "#/components/responses/RateLimited" }, "503": { "$ref": "#/components/responses/Unavailable" } }
      }
    },
    "/weather": {
      "get": {
        "tags": ["weather"], "operationId": "weather", "summary": "One hour at a point",
        "parameters": [
          { "$ref": "#/components/parameters/lat" }, { "$ref": "#/components/parameters/lon" },
          { "name": "time", "in": "query", "description": "The hour (ISO-8601 instant, exact hour); default = the top of the current hour", "schema": { "type": "string", "format": "date-time" } },
          { "$ref": "#/components/parameters/release" }
        ],
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WeatherResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" }, "404": { "$ref": "#/components/responses/NotFound" },
          "422": { "$ref": "#/components/responses/NoCoverage" }, "429": { "$ref": "#/components/responses/RateLimited" }, "503": { "$ref": "#/components/responses/Unavailable" } }
      }
    },
    "/tiles/{layer}/{z}/{x}/{y}.png": {
      "get": {
        "tags": ["tiles"], "operationId": "tile", "summary": "A weather tile (Web Mercator, 256 px)",
        "description": "Outside the ICON-EU domain a transparent 1×1 PNG is returned, never an error. `X-Tile-Source` says whether it was stored, rendered or outside the domain. Counted against the tile budget, not the JSON one.",
        "parameters": [
          { "name": "layer", "in": "path", "required": true, "schema": { "type": "string", "enum": ["precipitation", "temperature", "wind", "clouds", "pressure", "snow"] } },
          { "name": "z", "in": "path", "required": true, "schema": { "type": "integer", "minimum": 0, "maximum": 8 } },
          { "name": "x", "in": "path", "required": true, "schema": { "type": "integer", "minimum": 0 } },
          { "name": "y", "in": "path", "required": true, "schema": { "type": "integer", "minimum": 0 } },
          { "name": "time", "in": "query", "required": true, "description": "The hour (ISO-8601 instant, exact hour)", "schema": { "type": "string", "format": "date-time" } },
          { "$ref": "#/components/parameters/release" },
          { "name": "palette", "in": "query", "description": "`dr-v1` (default) or a stop list such as a condition's `palette`", "schema": { "type": "string" } },
          { "name": "fill_bound", "in": "query", "description": "Fill beyond the last stop; set with a condition palette", "schema": { "type": "boolean" } }
        ],
        "responses": {
          "200": { "description": "PNG", "headers": { "X-Tile-Source": { "schema": { "type": "string", "enum": ["stored", "rendered", "coalesced", "outside-domain"] } }, "ETag": { "schema": { "type": "string" } } }, "content": { "image/png": { "schema": { "type": "string", "format": "binary" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" }, "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }, "503": { "$ref": "#/components/responses/Unavailable" }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "tags": ["discovery"], "operationId": "openapi", "summary": "This document",
        "description": "Not counted against the rate limit and served even while the API is switched off.",
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object" } } } } }
      }
    }
  },
  "components": {
    "x-rateLimitHeaders": {
      "RateLimit-Limit": { "schema": { "type": "integer" }, "description": "Requests allowed per window for this budget" },
      "RateLimit-Remaining": { "schema": { "type": "integer" }, "description": "What is left in the trailing window" },
      "RateLimit-Reset": { "schema": { "type": "integer" }, "description": "Seconds until the current minute ends" },
      "RateLimit-Policy": { "schema": { "type": "string" }, "description": "`<limit>;w=60`" }
    },
    "parameters": {
      "lat": { "name": "lat", "in": "query", "required": true, "schema": { "type": "number", "minimum": -90, "maximum": 90 }, "example": 52.23 },
      "lon": { "name": "lon", "in": "query", "required": true, "schema": { "type": "number", "minimum": -180, "maximum": 180 }, "example": 21.01 },
      "release": { "name": "release", "in": "query", "description": "A release id from `/releases`; default = the current one", "schema": { "type": "string" } }
    },
    "responses": {
      "BadRequest": { "description": "A parameter is missing or malformed (`TIME_OUT_OF_RANGE` for a time the release does not hold)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
      "Forbidden": { "description": "The address is banned (`IP_BANNED`)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
      "NotFound": { "description": "`RELEASE_UNAVAILABLE`, `FRAME_UNAVAILABLE`, `LAYER_UNAVAILABLE` or `PLACE_NOT_FOUND`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
      "NoCoverage": { "description": "The point is outside the ICON-EU domain (`NO_COVERAGE`)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
      "RateLimited": {
        "description": "Over the per-address budget (`RATE_LIMITED`); wait `Retry-After` seconds",
        "headers": { "Retry-After": { "schema": { "type": "integer" } }, "RateLimit-Limit": { "schema": { "type": "integer" } }, "RateLimit-Remaining": { "schema": { "type": "integer" } }, "RateLimit-Reset": { "schema": { "type": "integer" } } },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } }
      },
      "Unavailable": { "description": "No forecast is published yet (`RELEASE_UNAVAILABLE`); retry after `Retry-After`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
      "Disabled": { "description": "An operator switched the API off (`API_DISABLED`)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } }
    },
    "schemas": {
      "ErrorEnvelope": {
        "type": "object",
        "required": ["timestamp", "status", "error", "message", "path", "requestId"],
        "properties": {
          "timestamp": { "type": "string", "format": "date-time" }, "status": { "type": "integer" }, "error": { "type": "string" },
          "message": { "type": "string" }, "path": { "type": "string" }, "requestId": { "type": "string" },
          "details": { "type": "object", "properties": { "code": { "type": "string" }, "retryAfterSeconds": { "type": "string" } }, "additionalProperties": { "type": "string" } },
          "retryAfterSeconds": { "type": "integer", "description": "Only on 429" }
        }
      },
      "Meta": {
        "type": "object", "required": ["api", "attribution", "licenseUrl", "docs"],
        "properties": { "api": { "type": "string", "const": "v1" }, "attribution": { "type": "string" }, "licenseUrl": { "type": "string", "format": "uri" }, "docs": { "type": "string", "format": "uri" } }
      },
      "Source": {
        "type": "object", "required": ["model", "releaseId", "runTime", "stale", "current"],
        "properties": { "model": { "type": "string" }, "releaseId": { "type": "string" }, "runTime": { "type": "string", "format": "date-time" }, "publishedAt": { "type": ["string", "null"], "format": "date-time" }, "stale": { "type": "boolean", "description": "Past the freshness limit; a newer run is due" }, "current": { "type": "boolean" } }
      },
      "Location": { "type": "object", "properties": { "lat": { "type": "number" }, "lon": { "type": "number" }, "place": { "type": ["string", "null"], "description": "Nearest gazetteer place within 30 km" } } },
      "ServeWindow": { "type": "object", "properties": { "start": { "type": "string", "format": "date-time" }, "end": { "type": "string", "format": "date-time" }, "windowHours": { "type": "integer" }, "pastGraceHours": { "type": "integer" } } },
      "Domain": { "type": "object", "properties": { "lonMin": { "type": "number" }, "latMin": { "type": "number" }, "lonMax": { "type": "number" }, "latMax": { "type": "number" }, "model": { "type": "string" } } },
      "RateLimits": { "type": "object", "properties": { "perMinute": { "type": "integer" }, "tilesPerMinute": { "type": "integer" }, "windowSeconds": { "type": "integer" } } },
      "IndexResponse": { "type": "object", "properties": { "name": { "type": "string" }, "version": { "type": "string" }, "documentation": { "type": "string", "format": "uri" }, "openapi": { "type": "string", "format": "uri" }, "endpoints": { "type": "object", "additionalProperties": { "type": "string" } }, "rateLimits": { "$ref": "#/components/schemas/RateLimits" }, "attribution": { "type": "string" }, "licenseUrl": { "type": "string" } } },
      "StatusResponse": { "type": "object", "properties": { "meta": { "$ref": "#/components/schemas/Meta" }, "enabled": { "type": "boolean" }, "release": { "oneOf": [ { "$ref": "#/components/schemas/Source" }, { "type": "null" } ] }, "serveWindow": { "$ref": "#/components/schemas/ServeWindow" }, "domain": { "$ref": "#/components/schemas/Domain" }, "now": { "type": "string", "format": "date-time" } } },
      "ReleaseSummary": { "type": "object", "properties": { "releaseId": { "type": "string" }, "state": { "type": "string", "enum": ["PUBLISHED", "RETIRED"] }, "runTime": { "type": "string", "format": "date-time" }, "current": { "type": "boolean" }, "leadCount": { "type": "integer" }, "firstFrame": { "type": ["string", "null"], "format": "date-time" }, "lastFrame": { "type": ["string", "null"], "format": "date-time" } } },
      "ReleasesResponse": { "type": "object", "properties": { "meta": { "$ref": "#/components/schemas/Meta" }, "currentReleaseId": { "type": ["string", "null"] }, "releases": { "type": "array", "items": { "$ref": "#/components/schemas/ReleaseSummary" } } } },
      "LeadInfo": { "type": "object", "properties": { "lead": { "type": "integer", "description": "Hours after the run time" }, "frameTime": { "type": "string", "format": "date-time" } } },
      "ReleaseDetail": { "type": "object", "properties": { "meta": { "$ref": "#/components/schemas/Meta" }, "release": { "$ref": "#/components/schemas/ReleaseSummary" }, "leads": { "type": "array", "items": { "$ref": "#/components/schemas/LeadInfo" } } } },
      "LayerInfo": { "type": "object", "properties": { "id": { "type": "string" }, "field": { "type": "string" }, "unit": { "type": "string" }, "vector": { "type": "boolean" }, "base": { "type": "boolean" } } },
      "LayersResponse": { "type": "object", "properties": { "meta": { "$ref": "#/components/schemas/Meta" }, "maxZoom": { "type": "integer" }, "palettes": { "type": "array", "items": { "type": "string" } }, "layers": { "type": "array", "items": { "$ref": "#/components/schemas/LayerInfo" } } } },
      "LegendStop": { "type": "object", "properties": { "value": { "type": "string" }, "css": { "type": "string", "description": "rgba(...)" } } },
      "LegendInfo": { "type": "object", "properties": { "layer": { "type": "string" }, "unit": { "type": "string" }, "stops": { "type": "array", "items": { "$ref": "#/components/schemas/LegendStop" } } } },
      "LegendsResponse": { "type": "object", "properties": { "meta": { "$ref": "#/components/schemas/Meta" }, "legends": { "type": "array", "items": { "$ref": "#/components/schemas/LegendInfo" } } } },
      "Condition": { "type": "object", "properties": { "id": { "type": "string" }, "label": { "type": "string" }, "description": { "type": "string" }, "layer": { "type": "string" }, "field": { "type": "string" }, "op": { "type": "string", "enum": ["GE", "LT"] }, "threshold": { "type": "number" }, "unit": { "type": "string" }, "palette": { "type": "string", "description": "Pass as `palette` with `fill_bound=true` to draw the condition as a tile overlay" }, "fillBound": { "type": "boolean" }, "cssColor": { "type": "string" } } },
      "ConditionsResponse": { "type": "object", "properties": { "meta": { "$ref": "#/components/schemas/Meta" }, "conditions": { "type": "array", "items": { "$ref": "#/components/schemas/Condition" } } } },
      "Place": { "type": "object", "properties": { "id": { "type": "integer" }, "name": { "type": "string" }, "asciiName": { "type": "string" }, "country": { "type": "string" }, "admin1": { "type": ["string", "null"] }, "population": { "type": "integer" }, "lat": { "type": "number" }, "lon": { "type": "number" }, "source": { "type": "string", "enum": ["gazetteer", "coords"] } } },
      "PlacesResponse": { "type": "object", "properties": { "meta": { "$ref": "#/components/schemas/Meta" }, "query": { "type": "string" }, "places": { "type": "array", "items": { "$ref": "#/components/schemas/Place" } } } },
      "PlaceResponse": { "type": "object", "properties": { "meta": { "$ref": "#/components/schemas/Meta" }, "place": { "$ref": "#/components/schemas/Place" } } },
      "WeatherData": {
        "type": "object", "description": "The served values; every field may be null when the frame lacks it",
        "properties": {
          "tempC": { "type": ["number", "null"] }, "feelsLikeC": { "type": ["number", "null"] }, "humidity": { "type": ["number", "null"], "description": "%" },
          "windSpeedKmh": { "type": ["number", "null"] }, "windGustKmh": { "type": ["number", "null"] }, "windDirectionDeg": { "type": ["number", "null"] },
          "weatherCode": { "type": ["integer", "null"], "description": "OpenWeatherMap-style condition code" }, "wwCode": { "type": ["integer", "null"], "description": "WMO present-weather code from the model" }, "description": { "type": ["string", "null"] },
          "pressure": { "type": ["number", "null"], "description": "hPa, sea level" }, "visibility": { "type": ["number", "null"], "description": "km" },
          "precipitation": { "type": ["number", "null"], "description": "mm in the hour ending at the frame time" }, "snowPrecipitation": { "type": ["number", "null"] },
          "precipitationType": { "type": ["string", "null"], "enum": ["NONE", "RAIN", "SNOW", "MIXED", null] }, "chanceOfRain": { "type": ["number", "null"], "description": "Always null: ICON-EU is deterministic" },
          "cloudsPercent": { "type": ["number", "null"] }
        }
      },
      "RawFields": { "type": "object", "description": "The twelve stored fields: PRECIP_MM, RAIN_MM, SNOW_MM (mm), T2M_C (°C), RH (%), U10, V10, GUST_KMH (km/h), CLCT (%), VIS_KM (km), WW (code), PMSL_HPA (hPa)", "additionalProperties": { "type": ["number", "null"] } },
      "Conditions": { "type": "object", "description": "One verdict per condition id; null when the data to decide is missing", "additionalProperties": { "type": ["boolean", "null"] } },
      "ForecastHour": { "type": "object", "properties": { "lead": { "type": "integer" }, "time": { "type": "string", "format": "date-time" }, "insideServeWindow": { "type": "boolean" }, "weather": { "$ref": "#/components/schemas/WeatherData" }, "raw": { "$ref": "#/components/schemas/RawFields" }, "conditions": { "$ref": "#/components/schemas/Conditions" } } },
      "ForecastResponse": { "type": "object", "properties": { "meta": { "$ref": "#/components/schemas/Meta" }, "source": { "$ref": "#/components/schemas/Source" }, "location": { "$ref": "#/components/schemas/Location" }, "serveWindow": { "$ref": "#/components/schemas/ServeWindow" }, "hours": { "type": "array", "items": { "$ref": "#/components/schemas/ForecastHour" } } } },
      "DailySummary": { "type": "object", "properties": { "date": { "type": "string", "format": "date", "description": "UTC day" }, "hours": { "type": "integer" }, "tempMaxC": { "type": ["number", "null"] }, "tempMinC": { "type": ["number", "null"] }, "precipitationMm": { "type": "number" }, "snowMm": { "type": "number" }, "windGustMaxKmh": { "type": ["number", "null"] }, "summary": { "type": ["string", "null"], "enum": ["clear", "partly", "cloudy", "fog", "drizzle", "rain", "snow", "thunder", null], "description": "The worst daytime (06–18 UTC) condition" }, "weatherCode": { "type": ["integer", "null"] }, "conditionHours": { "type": "object", "additionalProperties": { "type": "integer" } } } },
      "DailyForecastResponse": { "type": "object", "properties": { "meta": { "$ref": "#/components/schemas/Meta" }, "source": { "$ref": "#/components/schemas/Source" }, "location": { "$ref": "#/components/schemas/Location" }, "days": { "type": "array", "items": { "$ref": "#/components/schemas/DailySummary" } } } },
      "WeatherResponse": { "type": "object", "properties": { "meta": { "$ref": "#/components/schemas/Meta" }, "source": { "$ref": "#/components/schemas/Source" }, "location": { "$ref": "#/components/schemas/Location" }, "lead": { "type": "integer" }, "time": { "type": "string", "format": "date-time" }, "weather": { "$ref": "#/components/schemas/WeatherData" }, "raw": { "$ref": "#/components/schemas/RawFields" }, "conditions": { "$ref": "#/components/schemas/Conditions" } } }
    }
  }
}
