HTTP get format for UK Met Office API

Can someone advise me how to structure msg to access the UK Met Office DataPoint API?

The base URL is'

Required parameters are (I think) 'x-ibm-client-id', 'x-ibm-client-secret', 'latitude' and 'longitude' and I believe they should go in msg.headers (I also tried adding them to the URL)

I tried this

msg.url = ''
msg.headers = {};
msg.headers['x-ibm-client-idX-Auth-User'] = 'blah';
msg.headers['x-ibm-client-secret'] = 'blahblah';
msg.headers['latitude'] = 53.105019 
msg.headers['longitude'] = -3.912304
return msg;

which gives

{"httpCode":"401","httpMessage":"Unauthorized","moreInformation":"Client id missing."}

This does not look right.


const lat = 53.105019
const lon = -3.912304
msg.url = `${lat}&longitude=${lon}`
msg.headers = {
    'x-ibm-client-id': "YOUR_CLIENT_ID",
    'x-ibm-client-secret': "YOUR_CLIENT_SECRET",
    accept: "application/json"

built from ideas in this thread: New weather API from the MetOffice (worldwide) - Raspberry Pi Forums

PS: there is really no need for a function node. The URL can be set up in the HTTP Req node as can the headers now too.

That works thanks Steve.

I could not work out the right format from the Met Office's own sample Python script, I was trying to use the cookbook recipe which is for posting not requesting data
I never thought to look in Raspberry forums since strangely there is so little Node-red there.

I know no need for a function, it's just the go-to when one doesn't know what one is doing. :crazy_face:

1 Like

I use a change node to input the parameters for the request node:

This also gets back the location name. Useful because it isn't always obvious what base location the API has chosen - it might not be the input lat/lon.

This is really important somewhere like Sheffield that has lots of micro-climates. The weather can be significantly different just 1/2 mile away.

Ah, love the location! Spent a LOT of time in that area in the past. :slight_smile:

I'm finding the Met Office API hard to get a grip on. I have the single location forecast as in this thread working, though not for that particular location.

Strangely it complained that my latitude and longitude were too precise; I had to knock off a couple of decimal places before it would accept them, and then I think the forecast provided is just for the nearest named location in their database.

It's the weather maps I'm interested in at the moment. You need a different client id to access these, then create an "order" for the maps to be calculated [specially?] for you at 00:00 and 12:00. So it looks like there is no current conditions map.
So far all I have retrieved from this API is this, which I think means they did not calculate my forecast maps yet.

WARNING: The run 0 has been asked for but doesn't appear in the order o091234540
WARNING: The run 12 has been asked for but doesn't appear in the order o091234540
WARNING: No runs for order o091234540were found.  Don't expect any data.

It's all very odd, I asked for a UK & Ireland map, surely they already calculated it for someone else?

In summary, I can't so far see any advantage in the Met Office API over OpenWeatherMap.

"Official" data for any country is limited to recognised observation points (I believe there are around 5,000 for the UK). Typically mostly airports. Of course, some API's also include data ripped from non-official sources such as home weather stations. Also, some API's do interpolation for forecasts and so claim to give localised forecasts down to a few hundred meters. Best to take these with a pinch of salt! I occasionally compare local forecasts and even those over the next few hours rarely agree with each other exactly.

As always, the best forecast is to look out the window and get good at judging things. :grin:

Never tried those with a free account. I expect you've already gone through all this - but in case anyone else wants to try:

The Met Office DataPoint login I have lists a number of products for observations and forecasts. The detailed API info is here: DataPoint API reference (

But as well as the DataPoint API's, there is a separate DataHub set. Home | Weather DataHub (, This contains a lot more data but requires a separate account. They include a lot more imagery.

All our products have a free subscription for long term low volume usage or as an introduction to the product which can be updated to a paid subscription for more data at a later time (if required).

I generally find it to be far more accurate. Not really surprising since it comes direct from some of the most advanced weather models on Earth!

@jbudd Try this flow. Just add your client ID & client secret.

[{"id":"138e060b5da2f1af","type":"http request","z":"03cd1d2be26baaa9","name":"API call","method":"use","ret":"obj","paytoqs":"query","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":400,"y":400,"wires":[["ad4905267f9f1c5b"]]},{"id":"ad4905267f9f1c5b","type":"function","z":"03cd1d2be26baaa9","name":"format data","func":"let fullArray = msg.payload.features[0].properties.timeSeries\nfullArray = fullArray.slice(0, 37)\nconst tempArray = []\nvar code\nvar lookupTable = {\n  0: \"Clear night\",\n  1: \"Sunny day\",\n  2: \"Partly cloudy (night)\",\n  3: \"Partly cloudy (day)\",\n  4: \"Not used\",\n  5: \"Mist\",\n  6: \"Fog\",\n  7: \"Cloudy\",\n  8: \"Overcast\",\n  9: \"Light rain shower (night)\",\n  10: \"Light rain shower (day)\",\n  11: \"Drizzle\",\n  12: \"Light rain\",\n  13: \"Heavy rain shower (night)\",\n  14: \"Heavy rain shower (day)\",\n  15: \"Heavy rain\",\n  16: \"Sleet shower (night)\",\n  17: \"Sleet shower (day)\",\n  18: \"Sleet\",\n  19: \"Hail shower (night)\",\n  20: \"Hail shower (day)\",\n  21: \"Hail\",\n  22: \"Light snow shower (night)\",\n  23: \"Light snow shower (day)\",\n  24: \"Light snow\",\n  25: \"Heavy snow shower (night)\",\n  26: \"Heavy snow shower (day)\",\n  27: \"Heavy snow\",\n  28: \"Thunder shower (night)\",\n  29: \"Thunder shower (day)\",\n  30: \"Thunder\"\n}\n\nfor (let i = 0; i < fullArray.length; i++) {\n    let a = (fullArray[i])\n    let weatherCode = (lookupTable[a.significantWeatherCode])\n    const ts = new Date(a.time).getTime()\n    const tempData = {\n      measurement: \"metoffice\",\n      fields: {\n        temperature: a.screenTemperature,\n        maxTemp:     a.maxScreenAirTemp,\n        minTemp:     a.minScreenAirTemp,\n        feelsLike:   a.feelsLikeTemperature,\n        windGust:    Math.round(a.windGustSpeed10m / 1609.344 * 3600),\n        windSpeed:   Math.round(a.windSpeed10m / 1609.344 * 3600),\n        rainProb:    a.probOfPrecipitation,\n        rainPrecip:  a.totalPrecipAmount,\n        weatherCode: weatherCode,\n        uvIndex:     a.uvIndex\n        },\n      tags: {\n        tag1: 'weather',\n        tag2: 'metoffice'\n      },\n      timestamp: ts\n    }\n        tempArray.push(tempData)\n}\n\nmsg.payload = tempArray\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":570,"y":400,"wires":[["298c8848b85db6eb"]]},{"id":"4d3234f2043a477e","type":"change","z":"03cd1d2be26baaa9","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"excludeParameterMetadata\":true,\"includeLocationName\":true,\"latitude\":53.105019,\"longitude\":-3.912304}","tot":"json"},{"t":"set","p":"headers","pt":"msg","to":"{\"accept\":\"application/json\",\"x-ibm-client-secret\":\"client secret here\",\"x-ibm-client-id\":\"client id here\"}","tot":"json"},{"t":"set","p":"url","pt":"msg","to":"","tot":"str"},{"t":"set","p":"method","pt":"msg","to":"GET","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":275,"y":400,"wires":[["138e060b5da2f1af"]],"l":false,"info":"[]("},{"id":"a8b5e205fd9fdd2a","type":"inject","z":"03cd1d2be26baaa9","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":180,"y":400,"wires":[["4d3234f2043a477e"]]},{"id":"298c8848b85db6eb","type":"debug","z":"03cd1d2be26baaa9","name":"debug 19","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":740,"y":400,"wires":[]}]

EDIT - Sorry, re-read your last post & you already have it working!

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.