Embed and API
Put the map in your own page, lay the water on your own map, or read the modelled flood depths of 28 June to 1 July 2026. All of it is free. The embed needs no key; the API takes a free key.
Get a free API key
JavaScript library
One script gives you the embed, the depth calls and map layers for Google Maps and Leaflet:
<script src="https://flood-check.dawnstackai.com/sdk/v1.js"></script>
<script>
LagosFlood.init({ key: 'lfk_your_key' });
// Hourly depth at one point
const d = await LagosFlood.depth(6.5578, 3.3378);
console.log(d.peak.depth_cm, d.peak.time);
// A summary for many points (the library sends them 100 at a time)
const { results } = await LagosFlood.depthMany(listings.map((l) => ({ id: l.id, lat: l.lat, lon: l.lon })));
</script>
Water on a Google map
const map = new google.maps.Map(el, { center: { lat: 6.53, lng: 3.37 }, zoom: 13 });
const flood = LagosFlood.googleMapsLayer(map, { hour: 'peak', opacity: 0.85 });
flood.setHour('2026-07-01T04:00'); // change the hour
flood.remove();
Water on a Leaflet map
const flood = LagosFlood.leafletLayer({ hour: 'peak' }).addTo(map);
flood.setHour(40);
The 3D map, with a controller
const m = LagosFlood.embed('#flood-map', {
hour: 65, imagery: false,
pin: { lat: 6.5578, lon: 3.3378, name: 'Listing 1043' },
});
m.on('pin', (e) => console.log(e.depth_cm, e.peak_cm));
m.setTime('2026-06-30T17:00').play();
m.fly(6.555, 3.34, { distance: 3000 });
m.setLayer('buildings', false);
The map fills the element you give it, so give the element a height. Commands sent before the map is ready wait for it. m.ready is a promise for that moment.
Map tiles
The water as 256 px map tiles, in the grid that Google Maps, Leaflet, Mapbox and MapLibre use. The edge of the water stays smooth at street zoom. Tiles outside the map are clear.
GET /api/v1/tiles/{hour}/{z}/{x}/{y}.png?key=lfk_your_key
GET /api/v1/tiles/peak/15/16693/15791@2x.png?key=lfk_your_key
hour is 0 to 95, a local time like 2026-06-30T17:00, or peak (17:00 on 30 June). Add @2x for 512 px tiles on sharp screens. Zoom 0 to 20.
// MapLibre or Mapbox GL
map.addSource('flood', { type: 'raster', tileSize: 256,
tiles: ['https://flood-check.dawnstackai.com/api/v1/tiles/peak/{z}/{x}/{y}.png?key=lfk_your_key'] });
map.addLayer({ id: 'flood', type: 'raster', source: 'flood', paint: { 'raster-opacity': 0.85 } });
// Leaflet, without the library
L.tileLayer('https://flood-check.dawnstackai.com/api/v1/tiles/peak/{z}/{x}/{y}.png?key=lfk_your_key',
{ opacity: 0.85, attribution: 'Flood Check' }).addTo(map);
Embed the map
Add an iframe, or use LagosFlood.embed from the library. The embed needs no key. This one opens with a pin in Mushin at 17:00 on 30 June, without satellite imagery:
<iframe
src="https://flood-check.dawnstackai.com/?embed=1&at=6.5578,3.3378&t=65&imagery=0"
style="width: 100%; height: 560px; border: 0"
loading="lazy" allow="clipboard-write"
title="Lagos flood map"></iframe>
| Setting | What it does |
|---|---|
embed=1 | Map and timeline only, with a link back to the full map. Needed. |
t=0 to 95 | The hour on the timeline. 0 is 00:00 on 28 June; 65 is 17:00 on 30 June, the peak. |
at=lat,lon&n=Name | Opens with a pin and its depth card. |
imagery=0 | Painted ground instead of satellite imagery. Use it in a commercial product (see Terms). |
ui=none | No timeline: the map only. |
play=1 | Plays the four days when it opens. |
Control the embedded map
Send commands with postMessage, and listen for what the map sends back:
const map = document.querySelector('iframe').contentWindow;
// Drop a pin and fly to it
map.postMessage({ type: 'lagosflood:pin', lat: 6.4432, lon: 3.4721, name: 'Our office' }, '*');
// Set the time, by hour or by local time
map.postMessage({ type: 'lagosflood:time', time: '2026-06-30T17:00' }, '*');
// Play or pause, move the camera, switch a layer
map.postMessage({ type: 'lagosflood:play', on: true }, '*');
map.postMessage({ type: 'lagosflood:fly', lat: 6.555, lon: 3.34, distance: 3000 }, '*');
map.postMessage({ type: 'lagosflood:layer', layer: 'buildings', on: false }, '*');
map.postMessage({ type: 'lagosflood:clearPin' }, '*');
window.addEventListener('message', (e) => {
if (e.data?.type === 'lagosflood:ready') { /* the model has run; commands work now */ }
if (e.data?.type === 'lagosflood:pin') console.log(e.data.depth_cm, e.data.peak_cm, e.data.time);
if (e.data?.type === 'lagosflood:time') console.log(e.data.hour, e.data.time);
});
Layers: flood, receded, imagery, highlight (buildings with water at the door), roadflood, buildings, roads, labels, rain.
API
Base address: https://flood-check.dawnstackai.com/api/v1. Every answer is JSON or PNG, open to any site (CORS), and cached for a day. The depths are the same ones the map shows.
Send your key in a header, or add it to the address:
Authorization: Bearer lfk_your_key
GET /api/v1/depth?lat=6.5578&lon=3.3378&key=lfk_your_key
| Limit | |
|---|---|
| No key | 60 requests a minute from one address. Good to try the API. |
| With a key | 1,200 requests a minute for each user of your app. |
Over the limit, the answer is 429 with Retry-After: 60. A key that is not valid gets 401.
Regions
GET /api/v1/regions
The bounds, grid, hours and peak of the map (one region, Lagos), and the overlay and tile addresses.
Depth at a point
GET /api/v1/depth?lat=6.5578&lon=3.3378
GET /api/v1/depth?lat=6.5578&lon=3.3378&time=2026-06-30T17:00
{
"region": "lagos",
"cell": { "lat": 6.55756, "lon": 3.33764, "size_m": [60, 60] },
"ground_m": 12.2,
"open_water": false,
"unit": "cm",
"timezone": "Africa/Lagos",
"times": ["2026-06-28T00:00", "2026-06-28T01:00", ...],
"depth_cm": [0, 0, ...],
"peak": { "depth_cm": 80, "time": "2026-07-01T04:00" },
"hours_10cm_or_more": 38,
"at": { "time": "2026-06-30T17:00", "depth_cm": 48 },
"attribution": "...",
"disclaimer": "..."
}
Depths are in 2 cm steps. A point outside the map answers 404 with the bounds that are covered.
Overlay image
GET /api/v1/overlay.png?hour=65
GET /api/v1/overlay.png?hour=2026-06-30T17:00&scale=1
A transparent PNG of the water for one hour, pale blue to deep blue as on the map. It covers the map's bounds exactly (also in the X-Bounds header, as south, west, north, east), so it drops straight onto another map:
// Google Maps
new google.maps.GroundOverlay(
'https://flood-check.dawnstackai.com/api/v1/overlay.png?hour=65',
{ south: 6.40, west: 3.18, north: 6.72, east: 3.60 }, { opacity: 0.85 }).setMap(map);
// Leaflet
L.imageOverlay(url, [[6.40, 3.18], [6.72, 3.60]], { opacity: 0.85 }).addTo(map);
// Mapbox GL / MapLibre
map.addSource('flood', { type: 'image', url, coordinates: [[3.18, 6.72], [3.60, 6.72], [3.60, 6.40], [3.18, 6.40]] });
map.addLayer({ id: 'flood', type: 'raster', source: 'flood' });
scale is 1 to 4 pixels per 60 m square; 2 is the default. For a closer look, the map tiles are sharper.
Many points at once
POST /api/v1/depth
Content-Type: application/json
{ "points": [
{ "id": "listing-1042", "lat": 6.4432, "lon": 3.4721 },
{ "id": "listing-1043", "lat": 6.5578, "lon": 3.3378 }
] }
{
"event": "28 June to 1 July 2026",
"unit": "cm",
"results": [
{ "id": "listing-1042", "covered": true, "region": "lagos", "open_water": false,
"ground_m": 3.6, "peak_cm": 0, "peak_time": null, "band": "dry", "hours_10cm_or_more": 0 },
{ "id": "listing-1043", "covered": true, "region": "lagos", "open_water": false,
"ground_m": 12.2, "peak_cm": 80, "peak_time": "2026-07-01T04:00", "band": "thigh deep", "hours_10cm_or_more": 38 }
],
"attribution": "...", "disclaimer": "..."
}
Up to 100 points per request. A point outside the map comes back with "covered": false. band is the depth in plain words: dry, wet ground, ankle, shin, knee, thigh or waist deep, or chest deep or more.
Example: property listings
A listings site can show how each home fared in the June 2026 flood, three ways. Each works on its own.
1. A badge on every listing
When a listing is saved, send its coordinates to POST /api/v1/depth (100 at a time) and store the answer with the listing. It never changes, so there is no need to call again. Show the band and the hours:
const res = await fetch('https://flood-check.dawnstackai.com/api/v1/depth', {
method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: 'Bearer lfk_your_key' },
body: JSON.stringify({ points: listings.map((l) => ({ id: l.id, lat: l.lat, lon: l.lon })) }),
}).then((r) => r.json());
for (const r of res.results) {
if (!r.covered) continue; // not on the map yet: show nothing
saveFloodCheck(r.id, {
label: r.peak_cm < 10 ? 'Stayed dry in the June 2026 flood model' : `Up to ${r.peak_cm} cm (${r.band}) in the June 2026 flood model`,
hours: r.hours_10cm_or_more,
});
}
2. The map on the listing page
Embed the map with a pin on the home, so a buyer can play the four days and see the street around it: ?embed=1&at=6.4432,3.4721&n=Listing%201042&t=65&imagery=0.
3. Water on your search map
Add the map tiles at the peak hour to your own map of results, so buyers see which streets flooded while they browse. With the library it is one line: LagosFlood.googleMapsLayer(map).
Say what it is
Label it as a model of one event, with the credit and a link, for example: "Modelled flood depth, 28 June to 1 July 2026, by Flood Check. A model, not a survey." A dry result is not a promise that a home never floods, and a 60 m square can hold a high plot next to a low road. For homes with a high reading, suggest a site visit or a survey.
A listings product is commercial use: embed with imagery=0. If you need more areas, more events, or a check on demand for each new listing, use the form below.
Terms
- Credit "Flood Check" with a link to flood-check.dawnstackai.com wherever you show the data or the map.
- The modelled depths are CC BY 4.0. They come from rain by ECMWF through Open-Meteo (CC BY 4.0) and ground heights from the Copernicus DEM.
- The embedded map shows Sentinel-2 imagery by EOX under CC BY-NC-SA 4.0, which allows non-commercial use only. In a commercial product, embed with
imagery=0. - Buildings, roads and names in the embed are © OpenStreetMap contributors (ODbL). The API answers contain none of them.
- The depths are a model estimate on 60 m squares, not measurements. Do not use them alone for insurance, safety or property decisions.
- Use one key for each app. Keep answers in a cache; they do not change. A key used to copy the whole data set, or shared in public, can be stopped.