v1
latestOpenAPI 3.1.0MIT2026-07-1461669.5 KBRadar
Returns radar rainfall data with 1 km spatial and 5 minute temporal resolution, including a forecast for the next two hours.
Radar data is recorded on a 1200 km (North-South) x 1100 km (East-West) grid, with each pixel representing 1 km². That's quite a lot of data, so use lat/lon or bbox whenever you can (see below). Past radar records are kept for six hours.
Bright Sky can return the data in a few formats. Use 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.
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
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 (reprojection) will be inaccurate! Check out our radar demo for an example of correctly reprojecting the radar data using OpenLayers. Alternatively, take a look at the dwd:RV-Produkt layer on the DWD's open GeoServer for ready-made tiles you can overlay onto a map.
- The proj-string for the grid projection is +proj=stere +lat_0=90 +lat_ts=60 +lon_0=10 +a=6378137 +b=6356752.3142451802 +no_defs +x_0=543196.83521776402 +y_0=3622588.8619310018. The radar pixels range from -500 (left) to 1099500 (right) on the x-axis, and from 500 (top) to -1199500 (bottom) on the y-axis, each radar pixel a size of 1000x1000 (1 km²).
- 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
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:
Code 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 Javascript using pako:
fetch(
'https://api.brightsky.dev/radar'
).then((resp) => resp.json()
).then((respData) => {
const raw = respData.radar[0].precipitation_5;
const compressed = Uint8Array.from(atob(raw), c => c.charCodeAt(0));
const rawBytes = pako.inflate(compressed).buffer;
const precipitation = new Uint16Array(rawBytes);
});
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 `1200` and `1100` 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']
Additional resources
Query parameters
Bounding box (top, left, bottom, right) in pixels, edges are inclusive. (Defaults to full 1200x1100 grid.)
Bounding box (top, left, bottom, right) in pixels, edges are inclusive. (Defaults to full 1200x1100 grid.)
Alternative way to set a bounding box, must be used together with lat and lon. Data will reach distance meters to each side of this location, but is possibly cut off at the edges of the radar grid.
Alternative way to set a bounding box, must be used together with lat and lon. Data will reach distance meters to each side of this location, but is possibly cut off at the edges of the radar grid.
Latitude in decimal degrees.
Latitude in decimal degrees.
Longitude in decimal degrees.
Longitude in decimal degrees.
Timestamp of first record to retrieve, in ISO 8601 format. May contain time and/or UTC offset. (Defaults to 1 hour before latest measurement.)
Timestamp of first record to retrieve, in ISO 8601 format. May contain time and/or UTC offset. (Defaults to 1 hour before latest measurement.)
Timestamp of last record to retrieve, in ISO 8601 format. May contain time and/or UTC offset. (Defaults to 2 hours after date.)
Timestamp of last record to retrieve, in ISO 8601 format. May contain time and/or UTC offset. (Defaults to 2 hours after date.)
Determines how the precipitation data is encoded into the precipitation_5 field:
- compressed: base64-encoded, zlib-compressed bytestring of 2-byte integers
- bytes: base64-encoded bytestring of 2-byte integers
- plain: Nested array of integers
Determines how the precipitation data is encoded into the precipitation_5 field:
- compressed: base64-encoded, zlib-compressed bytestring of 2-byte integers
- bytes: base64-encoded bytestring of 2-byte integers
- plain: Nested array of integers
Timezone in which record timestamps will be presented, as <a href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones">tz database name</a>. Will also be used as timezone when parsing date and last_date, unless these have explicit UTC offsets. If omitted but date has an explicit UTC offset, that offset will be used as timezone. Otherwise will default to UTC.
Timezone in which record timestamps will be presented, as <a href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones">tz database name</a>. Will also be used as timezone when parsing date and last_date, unless these have explicit UTC offsets. If omitted but date has an explicit UTC offset, that offset will be used as timezone. Otherwise will default to UTC.
Response
Successful Response
Example response
{
"radar": [
{
"timestamp": "2023-08-07T08:00:00+00:00",
"source": "RADOLAN::RV::2023-08-08T11:45:00+00:00",
"precipitation_5": "eF5jGAWjYBTQEQAAA3IAAQ=="
}
],
"geometry": {
"coordinates": [
[
7.44365,
52.08712
],
[
7.45668,
51.90644
],
[
7.7487,
51.914
],
[
7.73716,
52.09473
]
],
"type": "Polygon"
},
"bbox": [
100,
100,
300,
300
],
"latlon_position": {
"x": 10.244,
"y": 10.088
}
}