With v2.0.2, a new /radar endpoint landed in Bright Sky. 🎉
This endpoint contains
- rainfall radar data
- on a 1200 km (height) x 1100 km (width) grid
- with each pixel corresponding to 1 km²
- in 5-minute intervals
- including a forecast for the next two hours.
This data comes from the DWD's RV radar composite product.
Please feel very welcome to add feedback and suggestions to this issue!
Quickstart
This request will get you radar data near Münster, reaching 200 km to the East/West/North/South, as a two-dimensional grid of integers:
https://api.brightsky.dev/radar?lat=52&lon=7.6&format=plain
API structure
-
The new endpoint is available at https://api.brightsky.dev/radar
-
The response looks roughly like:
{
"radar": [
{
"timestamp": "2023-05-09T11:05:00+00:00",
"source": "RADOLAN::RV::2023-05-09T11:05:00+00:00",
"precipitation_5": "... base64-encoded bytestring or array of integers, see below ..."
},
...
],
"geometry": {
"type": "Geometry",
"coordinates": [
[1.4633,55.86209],
[3.56699,45.69643],
[16.58087,45.68461],
[18.73162,55.84544],
]
}
}
-
Allowed parameters (all of them optional):
format: determines how the precipitation data is encoded into the precipitation_5 field (default: compressed):
compressed: base64-encoded, zlib-compressed bytestring of 2-byte integers
bytes: base64-encoded bytestring of 2-byte integers
plain: Nested array of integers
bbox: bounding box top, left, bottom, right in pixels, edges are inclusive (default: full 1200x1100 grid)
lat / lon / distance: alternative way to set a bounding box, where lat / lon will lay in the center pixel of the returned radar data, which will reach distance meters to each side of this pixel (see example request below). distance can be omitted and defaults to 200000 (i.e. 200 km). The exact x-y-position of the supplied lat/lon will be returned as latlon_position
date: ISO8601 timestamp of first record (default: 1 hour before latest measurement)
last_date: ISO8601 timestamp of last record (default: 2 hours after date)
tz: timezone to be used for timestamps (default: UTC)
Parsing examples
The radar data is quite big (naively unpacking the default 25-frames response into Python integer arrays will eat roughly 125 MB of memory), so use bbox whenever you can.
compressed format
With Python using numpy:
import base64
import zlib
import numpy as np
import requests
resp = requests.get('https://api.brightsky.dev/radar')
raw = resp.json()['radar'][0]['precipitation_5']
raw_bytes = zlib.decompress(base64.b64decode(raw))
data = np.frombuffer(
raw_bytes,
dtype='i2',
).reshape(
# Adjust this to the height/width of your bbox
(1200, 1100),
)
With Python using the standard library's array:
import array
# [... load raw_bytes as above ...]
data = array.array('H')
data.frombytes(raw_bytes)
data = [
# Adjust `1200` and `1100` to the height/width of your bbox
data[row*1100:(row+1)*1100]
for row in range(1200)
]
Simple plot using matplotlib:
import matplotlib.pyplot as plt
# [... load data as above ...]
plt.imshow(data, vmax=50)
plt.show()
bytes format
Same as for compressed, but add ?format=bytes to the URL and remove the call to zlib.decompress, using just raw_bytes = base64.b64decode(raw) instead.
plain format
This is obviously a lot simpler than the compressed format. It is, however, also a lot slower. Nonetheless, if you have a small-ish bbox the performance difference becomes manageable, so just using the plain format and not having to deal with unpacking logic can be a good option in this case.
With Python:
import requests
resp = requests.get('https://api.brightsky.dev/radar?format=plain')
data = resp.json()['radar'][0]['precipitation_5']
Content
- The grid is a polar stereographic projection of Germany and the regions bordering it. This is different from the mercator projection used for most consumer-facing maps like OpenStreetMap or Google Maps, and overlaying the radar data onto such a map without conversion will be inaccurate!
- The DWD data does not cover the whole grid! Many areas near the edges will always be
0.
- Values represent 0.01 mm / 5 min. I.e., if a pixel has a value of
45, then 0.45 mm of precipitation fell in the corresponding square kilometer in the past five minutes.
- The four corners of the grid are as follows:
- Northwest: Latitude
55.86208711, Longitude 1.463301510
- Northeast: Latitude
55.84543856, Longitude 18.73161645
- Southeast: Latitude
45.68460578, Longitude 16.58086935
- Southwest: Latitude
45.69642538, Longitude 3.566994635
- The latitude/longitude for the center of any grid cell (pixel) can be found in this file.
You can find details and more information in the DWD's RV product info (German only). Below is an example visualization of the rainfall radar data taken from this document, using the correct projection and showing the radar coverage:

Notes and Questions for Feedback
- This is an experimental feature and the interface is very much subject to change. Please regularly check this issue if you're using it.
- Use a
bbox and the default compressed format if possible -- this'll get you the fastest response times by far and reduce load on the server. If you have a small-ish bounding box (e.g. 250 x 250 pixels), using the plain format should be fine.
- I'm not sure how useful the
bytes format is. The idea is to save the hassle of parsing a giant JSON array on the client side, but maybe that's actually not too bad?
- The polar-stereographic projection that this data uses makes a lot of sense from a data perspective (as each pixel represents the same area) but is rarely used in maps. If you're looking for tiles you can overlay on a map, take a look at the
dwd:RV-Produkt layer on the DWD's open GeoServer.
- The conversion between pixel x/y coordinates and lat/lon coordinates is quite unwieldy. Many users will probably just want to know "what's the rainfall like close to my location", so maybe alternative parameters for setting the bounding box by supplying the lat/lon of its center and a maximum distance could be a simple interface for this?
Resources
With v2.0.2, a new
/radarendpoint landed in Bright Sky. 🎉This endpoint contains
This data comes from the DWD's RV radar composite product.
Please feel very welcome to add feedback and suggestions to this issue!
Quickstart
This request will get you radar data near Münster, reaching 200 km to the East/West/North/South, as a two-dimensional grid of integers:
https://api.brightsky.dev/radar?lat=52&lon=7.6&format=plainAPI structure
The new endpoint is available at
https://api.brightsky.dev/radarThe response looks roughly like:
Allowed parameters (all of them optional):
format: determines how the precipitation data is encoded into theprecipitation_5field (default:compressed):compressed: base64-encoded, zlib-compressed bytestring of 2-byte integersbytes: base64-encoded bytestring of 2-byte integersplain: Nested array of integersbbox: bounding boxtop, left, bottom, rightin pixels, edges are inclusive (default: full 1200x1100 grid)lat/lon/distance: alternative way to set a bounding box, wherelat/lonwill lay in the center pixel of the returned radar data, which will reachdistancemeters to each side of this pixel (see example request below).distancecan be omitted and defaults to200000(i.e. 200 km). The exact x-y-position of the suppliedlat/lonwill be returned aslatlon_positiondate: ISO8601 timestamp of first record (default: 1 hour before latest measurement)last_date: ISO8601 timestamp of last record (default: 2 hours afterdate)tz: timezone to be used for timestamps (default: UTC)Parsing examples
The radar data is quite big (naively unpacking the default 25-frames response into Python integer arrays will eat roughly 125 MB of memory), so use
bboxwhenever you can.compressedformatWith Python using
numpy:With Python using the standard library's
array:Simple plot using
matplotlib:bytesformatSame as for
compressed, but add?format=bytesto the URL and remove the call tozlib.decompress, using justraw_bytes = base64.b64decode(raw)instead.plainformatThis is obviously a lot simpler than the
compressedformat. It is, however, also a lot slower. Nonetheless, if you have a small-ishbboxthe performance difference becomes manageable, so just using theplainformat and not having to deal with unpacking logic can be a good option in this case.With Python:
Content
0.45, then 0.45 mm of precipitation fell in the corresponding square kilometer in the past five minutes.55.86208711, Longitude1.46330151055.84543856, Longitude18.7316164545.68460578, Longitude16.5808693545.69642538, Longitude3.566994635You can find details and more information in the DWD's
RV product info(German only). Below is an example visualization of the rainfall radar data taken from this document, using the correct projection and showing the radar coverage:Notes and Questions for Feedback
bboxand the defaultcompressedformat if possible -- this'll get you the fastest response times by far and reduce load on the server. If you have a small-ish bounding box (e.g. 250 x 250 pixels), using theplainformat should be fine.bytesformat is. The idea is to save the hassle of parsing a giant JSON array on the client side, but maybe that's actually not too bad?dwd:RV-Produktlayer on the DWD's open GeoServer.Resources
RVproduct (German)