# data-as-a-service — weather data API (LLM reference) > Read high-resolution weather data over western Europe from a REST API: one key, one POST call, > columnar JSON back, billed per datapoint. ## Objective Read high-resolution weather data, and discover what is available, from the data-as-a-service REST API. Every request is authenticated with an API key and returns JSON. ## How to use - Base URL: https://api.daas.serenodata.com - Auth: send the header `x-api-key: ` on every request. Missing/unknown key -> 401. - Billable unit: the "datapoint" = one parameter, at one coordinate, for one datetime. A request reads (parameters x coordinates x datetimes) datapoints. - Limits: - <= 100000 datapoints per request, else 413. - ~1 request/second per key (short bursts absorbed) on top of a per-IP guard, else 429. On 429, back off briefly and retry. - A monthly datapoint quota per account, resetting at the UTC month start, with a 5% grace band. - Errors: JSON body `{ "error": "", "request_id": "" }`. The request id is also the `X-Request-Id` response header. An unexpected internal failure is reported generically as `{ "error": "internal error", "request_id": "" }` (detail is in the server logs, keyed by id). Status codes: 400 invalid request, 401 bad key, 403 quota/subscription, 404 unknown provider or dataset, 413 too many datapoints, 429 rate limited, 500 internal error. ## Time window and billing - Future bound: datetimes up to now (UTC) + 48 h can be requested. AROME runs extend to +51 h but publish with a 3-5 h delay; the 48 h cap keeps the promise dependable at any time of day. - Past bound: arome-001 serves a short rolling window of recent data; older datetimes are unavailable and return null (not billed). - Datetimes beyond now (UTC) + 48 h are rejected (400 invalid request). - Missing data (an unavailable datetime, a coordinate hole in the domain, a parameter hole) is returned as null and is not billed. 0.0 is a value and is billed. - Usage decrements by `effective` (= `requested` - `missing`). - Authorisation is checked against `requested`; billing decrements by `effective`. ## Parameters (code | meaning | unit) - t2m | temperature at 2 m | °C - rh2m | relative humidity at 2 m | % - sp | surface pressure (at ground level) | hPa - ws10m | wind speed at 10 m | m/s - wd10m | wind direction at 10 m | degree - gs10m | gust speed at 10 m | m/s - gd10m | gust direction at 10 m | degree - rr | rainfall | mm Values are decoded to one decimal. A missing value is returned as null. ## Semantics (per timestep; 1 h for arome-001) - Instantaneous at the datetime: t2m, rh2m, sp, ws10m, wd10m. - Over the hour ending at the datetime: rr is the accumulation in mm, de-accumulated from the run's cumulative rainfall field; gs10m is the maximum gust speed over that hour and gd10m the direction of that gust, derived from the model's gust vector components. - sp is the local pressure at the cell's ground elevation, not reduced to sea level: sites at different elevations are not directly comparable (a 300 m site reads ~35 hPa below a coastal one). Per-site pressure tendency is unaffected. The code `msl` is reserved for future datasets that natively provide mean sea-level pressure. - Directions are in degrees from north, meteorological convention: the direction the wind or gust comes from. ## Datetimes RFC3339, in UTC, on the hour. Provide them in exactly one of three forms: - single: {"datetime": "2026-07-06T12:00:00Z"} - set: {"datetime": ["2026-07-06T00:00:00Z", "2026-07-06T06:00:00Z"]} - range: {"from": "2026-07-06T00:00:00Z", "to": "2026-07-06T23:00:00Z"} (inclusive, expanded hourly) ## Endpoint: read datapoints POST /v1/providers/mf/datasets/arome-001/datapoints Body: { "parameters": string[], // parameter codes, see above "coordinates": [{ "lat": number, "lon": number }], // echoed and counted as sent; values read from the nearest 0.01° grid cell "datetimes": } Response: { "data": { "parameters": string[], // in request order "coordinates": [{ "lat": number, "lon": number }], "datetimes": string[], "values": (number | null)[] // flat cross-product, see ordering below }, "stats": { ... } // see glossary below } ### Values ordering (response) `values` is the flat cross-product of the request: parameter-major, then coordinate, then datetime (datetime varies fastest). Zero-based: index(p, c, d) = p*(C*D) + c*D + d with requested = P*C*D Worked example (arome-001, observed): parameters ["rr","t2m"], coordinates [A,B], datetimes [T0,T1] A = {47.49179,-1.24794}, B = {48.8566,2.3522}, T0 = 2026-07-14T12:00:00Z, T1 = T0+1h values = [rr A T0, rr A T1, rr B T0, rr B T1, t2m A T0, t2m A T1, t2m B T0, t2m B T1] = [0.0, 0.0, 0.0, 0.0, 31.7, 33.1, 33.0, 34.4] ### Stats glossary - requested: parameters x coordinates x datetimes. missing: nulls in the response. effective: requested - missing; this is what is billed. - quota_used_this_period / quota_remaining: consumption against the plan's monthly allowance (UTC month). Both read 0 when no subscription is active. - extra_usage_remaining: the prepaid pool (pay-as-you-go credits and the welcome gift). Draw order: plan allowance, then the 5% grace band, then this pool. - extra_usage_used: of the effective count, how much THIS request drew from that pool; 0 when the request stayed within quota. - chunks_read / bytes_read: storage chunks touched and bytes scanned to serve the request, shown for transparency. Requests clustered in space and aligned to UTC days are the efficient path: a full day at one point costs no more I/O than a single hour. - req_timestamp / resp_timestamp / execution_duration_ms: wall-clock request handling, not data times. - The request id is returned in the `X-Request-Id` header on every response, and in the JSON body of error responses. ### Example (observed) curl -X POST "https://api.daas.serenodata.com/v1/providers/mf/datasets/arome-001/datapoints" \ -H "x-api-key: $YOUR_API_KEY" \ -H "content-type: application/json" \ -d '{"parameters":["t2m","rr","ws10m","wd10m","gs10m","gd10m"],"coordinates":[{"lat":47.49179,"lon":-1.24794}],"datetimes":{"datetime":["2026-07-11T00:00:00Z"]}}' -> { "data": { "parameters": ["t2m", "rr", "ws10m", "wd10m", "gs10m", "gd10m"], "coordinates": [{ "lat": 47.49179, "lon": -1.24794 }], "datetimes": ["2026-07-11T00:00:00Z"], "values": [27.4, 0.0, 3.7, 47.9, 7.6, 47.4] }, "stats": { "requested": 6, "effective": 6, "missing": 0, "chunks_read": 6, "bytes_read": 2880000, "quota_used_this_period": 0, "quota_remaining": 0, "extra_usage_used": 6, "extra_usage_remaining": 23994, "req_timestamp": "2026-07-13T12:46:25.32917Z", "resp_timestamp": "2026-07-13T12:46:26.489915Z", "execution_duration_ms": 1160.745 } } Notes on this response: rr 0.0 is a real, billed value (null would be absence); gust 7.6 m/s against wind 3.7 m/s shows max-over-hour vs instantaneous; req/resp timestamps are wall-clock, distinct from the queried datetime. ## Endpoint: list providers GET /v1/providers -> { "providers": [ { "id": "mf", "name": "Météo-France", "availability": "available", "_links": { "datasets": { "href": "/v1/providers/mf/datasets" } } }, { "id": "dwd", "name": "Deutscher Wetterdienst", "availability": "planned", "_links": { "datasets": { "href": "/v1/providers/dwd/datasets" } } }, { "id": "ecmwf", "name": "ECMWF", "availability": "planned", "_links": { "datasets": { "href": "/v1/providers/ecmwf/datasets" } } } ], "_links": { "self": { "href": "/v1/providers" } } } ## Endpoint: list a provider's datasets GET /v1/providers/{provider}/datasets -> { "provider": "mf", "provider_name": "Météo-France", "datasets": [ { "id": "arome-001", "name": "AROME 0.01°", "availability": "available", "_links": { "self": { "href": "/v1/providers/mf/datasets/arome-001" }, "datapoints": { "href": "/v1/providers/mf/datasets/arome-001/datapoints" } } }, { "id": "arome-ens-0025", "name": "AROME ensemble 0.025°", "availability": "planned", "_links": { "self": { "href": "/v1/providers/mf/datasets/arome-ens-0025" } } } ], "_links": { "self": { "href": "/v1/providers/mf/datasets" }, "providers": { "href": "/v1/providers" } } } ## Endpoint: dataset descriptor GET /v1/providers/{provider}/datasets/{dataset} -> { "provider": "mf", "provider_name": "Météo-France", "id": "arome-001", "name": "AROME 0.01°", "availability": "available", "description": "Météo-France AROME high-resolution model at 0.01° over Western Europe.", "parameters": [ { "code": "t2m", "name": "temperature at 2 m", "unit": "°C" }, ... 8 total, see Parameters above ... ], "coverage": { "bounding_box": { "min_latitude": 37.5, "max_latitude": 55.4, "min_longitude": -12.0, "max_longitude": 16.0 }, "geojson": "Exact geojson definition coming soon...", "note": "Bounding box of the model grid; the exact data domain is smaller and will be published as GeoJSON." }, "timesteps": { "granularity": "hourly", "alignment": "on the hour, UTC", "format": "RFC3339 UTC", "selection": { "single": "...", "set": "...", "range": "..." } }, "licence": { "name": "Licence Ouverte / Open Licence 1.0", "url": "https://www.etalab.gouv.fr/wp-content/uploads/2014/05/Licence_Ouverte.pdf", "producer": "Météo-France", "attribution": "Attribute Météo-France and the data date (the datetime you queried) when you display or redistribute this data." }, "_links": { "self": { "href": "/v1/providers/mf/datasets/arome-001" }, "datasets": { "href": "/v1/providers/mf/datasets" }, "datapoints": { "href": "/v1/providers/mf/datasets/arome-001/datapoints" } } } Licence: AROME data is Météo-France, under the Etalab Licence Ouverte 1.0 (https://www.etalab.gouv.fr/wp-content/uploads/2014/05/Licence_Ouverte.pdf). Reuse, derivatives and commercial use are permitted; when you display or redistribute the data, credit "Météo-France" and the data date (the datetime you queried). ## Catalogue - mf / arome-001 AROME 0.01° available (licence: Licence Ouverte / Open Licence 1.0) - mf / arome-ens-0025 AROME ensemble 0.025° planned - dwd / icon-eu-00625 ICON-EU 0.0625° planned - dwd / icon-01 ICON 0.1° planned - ecmwf / ifs-025 IFS 0.25° planned - ecmwf / ifs-01 IFS 0.1° planned (premium yearly / enterprise only) Notes: - Only arome-001 is served today; the others appear in discovery as "planned" and have no datapoints link yet. - The coverage bounding box is the coarse model-grid extent; the exact data domain is smaller (GeoJSON coming soon).