# Nigeria Flood Early Warning (`zainab77/nigeria-flood-early-warning`) Actor

- **URL**: https://apify.com/zainab77/nigeria-flood-early-warning.md
- **Developed by:** [zainab fadeyi](https://apify.com/zainab77) (community)
- **Stats:** 3 total users, 0 monthly users, 95.5% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Nigeria Flood Early Warning

Turns Nigeria's official flood forecasts and river discharge data into a
clear, community-level answer: **how likely is this specific place to
flood in the next 7 or 30 days, and why?**

### The problem

Nigeria's 2026 Annual Flood Outlook (NIHSA) lists over 14,000 high-risk
communities across 33 states. The warnings are real, but they're published as
PDFs, press briefings and technical advisories — not in a form a resident,
NGO, insurer, or logistics team can act on for one specific town. This Actor
answers the one question that matters: *what's the risk here, right now?*

### What it does

For each location you give it, the Actor combines:

- **NIHSA's Annual Flood Outlook** — the official baseline risk category for
  that community
- **NIHSA flood advisories** — active state-level warnings
- **GloFAS river discharge forecasts** (via Open-Meteo) — is the river
  forecast to run higher than its historical norm for this time of year?
- **Rainfall forecasts** (via Open-Meteo) — is heavy rain expected?
- **Known dam releases** — e.g. Cameroon's Lagdo Dam, which affects the
  Benue River

...into a single `NONE | LOW | MODERATE | HIGH | SEVERE` risk level per
7-day and 30-day window, with **every contributing reason spelled out** —
this Actor never gives a risk level without explaining exactly which signals
produced it.

### Coverage (MVP)

Riverine flooding along the Niger–Benue confluence system, plus one South
West pilot community on the Ogun River — 14 communities across 5 states:

| State | Communities |
|---|---|
| Kogi | Lokoja, Ajaokuta, Onyedega (Ibaji) |
| Benue | Makurdi, Obagaji (Agatu), Gbajimba (Guma) |
| Adamawa | Yola/Jimeta, Numan, Demsa |
| Anambra | Onitsha, Atani (Ogbaru), Nzam (Anambra West), Otuocha/Aguleri (Anambra East) |
| Lagos | Ikorodu |

Ikorodu is a pilot for expanding beyond the Niger-Benue system: it has a real
riverine signal (the Ogun River, affected by Oyan Dam releases), unlike most
of Lagos, which floods mainly from overwhelmed urban drainage — a mechanism
this Actor doesn't model. Ikorodu's result draws on the same GloFAS/outlook
signals as every other community; other Lagos LGAs (Lekki, Victoria Island,
etc.) are not yet covered and would need the same per-community verification
work before being added.

A location outside this list (or too far from any of these communities) gets
a clear "outside current coverage" result rather than a guess. Coverage is
expected to expand over time.

**Not covered:** urban flash flooding (e.g. Lagos-style drainage flooding) —
the underlying river-discharge model doesn't capture it. See Limitations.

### Input

```json
{
  "locations": [
    { "community": "Lokoja", "lga": "Lokoja", "state": "Kogi" },
    { "latitude": 7.7322, "longitude": 8.5391, "label": "Near Makurdi" }
  ],
  "horizons": [7, 30],
  "includeSources": true,
  "mode": "check"
}
```

| Field | Type | Description |
|---|---|---|
| `locations` | array (required) | Each entry is **either** `{ community, lga?, state? }` (matched against the 14 known communities by name, then LGA, then state) **or** `{ latitude, longitude, label? }` (matched to the nearest known community within 25km). |
| `horizons` | `[7\|30]` | Which forecast windows to return. Default: both. |
| `includeSources` | boolean | Attach the source URLs used for each result. Default: `true`. |
| `mode` | `"check"` | `"ingest"` | `"check"` scores the given locations (default). `"ingest"` additionally attempts to refresh the NIHSA outlook from source before scoring. |

### Output

One dataset item per input location:

```json
{
  "location": { "label": "Lokoja", "community": "Lokoja", "lga": "Lokoja", "state": "Kogi", "latitude": 7.8, "longitude": 6.74 },
  "matchedCommunity": "kogi-lokoja",
  "windows": [
    {
      "days": 7,
      "riskLevel": "MODERATE",
      "confidence": "high",
      "reasons": [
        "NIHSA's Annual Flood Outlook classifies this community as high risk.",
        "An active NIHSA flood advisory covers this community's state."
      ]
    },
    { "days": 30, "riskLevel": "MODERATE", "confidence": "high", "reasons": ["..."] }
  ],
  "recommendedActions": ["Avoid unnecessary travel near riverbanks and low-lying roads.", "..."],
  "sources": [{ "name": "Open-Meteo Flood API (GloFAS)", "url": "...", "retrievedAt": "..." }],
  "limitations": ["This is an independent early-warning tool, not an official government warning. Follow local authority guidance.", "..."],
  "warnings": [],
  "generatedAt": "2026-09-25T00:00:00.000Z"
}
```

See `SCORING.md` in the source repo for the exact scoring rules and weights,
and `sources/README.md` for how every data source was verified.

### Limitations & disclaimer

- **This is an independent early-warning tool, not an official government
  warning.** Always follow guidance from NIHSA, NEMA and your local
  authorities. Every result repeats this disclaimer.
- **Riverine flooding only.** Urban/flash flooding from local drainage is not
  modelled.
- **NIHSA's own website has been unreachable throughout this Actor's
  development.** Outlook categories currently come from independently
  verified news coverage of NEMA/state-emergency-agency/NIHSA statements
  naming each community's LGA directly (source cited per community in the
  code), not the official PDF — the Actor automatically tries the real PDF
  first on every run and will use it the moment NIHSA's site is reachable
  again, with no config change needed.
- **GloFAS resolution is ~5km.** River points were placed and, where the
  first placement returned implausibly low discharge, corrected by checking
  neighbouring grid cells against known real-world discharge figures — not
  by walking the riverbank. Treat individual `warnings` entries as the
  authoritative caveat for any given result.
- A `NONE`/`LOW` result means *no elevated signal was found in the sources
  this Actor checks* — it is not a guarantee of safety.

### Pricing

Pay-per-event:

- **$0.00005** per run (`actor-start`) — a small flat fee, once per run regardless of how many locations you check
- **$0.003** per location checked (`location-risk-check`) — billed for every location, whether it resolves to a known community or comes back as outside current coverage, since either way you get a complete, explained answer

### How to run it

**From the Apify Store / Console:** open the Actor, go to the **Input** tab
(it's prefilled with a working example — Lokoja, Makurdi, and Yola/Jimeta by
name, plus a raw-coordinate example), and click **Start**. Results appear as
they're computed under the **Output** tab and in the run's dataset.

**Via the Apify API/CLI**, once you have an API token:

```bash
curl -X POST "https://api.apify.com/v2/acts/zainab77~nigeria-flood-early-warning/run-sync-get-dataset-items?token=<APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"locations": [{"community": "Lokoja", "lga": "Lokoja", "state": "Kogi"}]}'
```

`run-sync-get-dataset-items` runs the Actor and returns the dataset directly
in the response — the simplest way to call it programmatically. For a
longer-running batch (many locations), use the regular `runs` endpoint and
poll, or set up a Schedule + Webhook in Console to run it automatically (see
`bot/src/README.md` for how the companion bot does exactly that).

No environment variables are required to run it — see `sources/README.md`
for the one optional `LLM_PROVIDER`/`LLM_API_KEY` pair, which only affects a
currently-unreachable ingest path (NIHSA's site has been down throughout
development) and has a safe no-op fallback either way.

### Technologies used

- **TypeScript** (strict, ESM, Node 20+), built with the Apify SDK for JS
- **[Open-Meteo Flood API](https://open-meteo.com/en/docs/flood-api)**
  (GloFAS v4) for river discharge forecasts and historical baselines
- **Open-Meteo Weather Forecast API** for rainfall forecasts
- **NIHSA** (Nigeria Hydrological Services Agency) Annual Flood Outlook and
  advisories — real per-community data sourced from verified news coverage
  while nihsa.gov.ng is unreachable, with an LLM-assisted PDF-extraction
  pipeline (Anthropic Claude, behind a swappable `LlmProvider` interface)
  ready to take over automatically once the site is back
- **zod** for runtime validation of all external data (API responses, dataset
  items, LLM output)
- **pdf-parse** for PDF text extraction
- npm workspaces monorepo, shared between this Actor and a companion Express
  - Twilio WhatsApp bot (`bot/`, deployed separately, not part of this Store
    listing)

### Companion WhatsApp bot

This Actor is the data engine behind a WhatsApp alert bot (not part of this
Actor's Store listing) that lets residents opt in to be notified by WhatsApp,
in their own language, only when a subscribed community's risk level rises.
It runs a daily scheduled check against this Actor and only messages someone
when their area's risk genuinely rises — not on every run.

#### Try it

**[Open the one-tap setup page](https://claude.ai/artifact/7gXysj7MTZnzYvycvm2yPw)**
— two links to tap on your phone, no typing required.

Or manually: the bot currently runs on Twilio's WhatsApp Sandbox (a shared
testing number, not yet a dedicated business line), so there's a one-time
step before it can message you:

1. On WhatsApp, send `join inch-motion` to **+1 415 523 8886**. You'll get a
   confirmation that you're connected to the sandbox.
2. Send `JOIN` to the same number to start.
3. Pick a language (English, Hausa, Yoruba, Igbo or Pidgin), then reply with
   your community or LGA (e.g. `Lokoja`), or share a WhatsApp location pin.
4. You're subscribed. You'll only hear from it again when your area's risk
   level actually changes. Reply `1` anytime for full details on the latest
   alert, `STOP` to unsubscribe, or `CHANGE` to update your language or
   location.

Coverage matches this Actor's: the 14 communities listed above.

### Source code

The full source for both this Actor and the WhatsApp bot is open on GitHub:
**[github.com/Zainabfadeyi/nigeria-flood-early-warning](https://github.com/Zainabfadeyi/nigeria-flood-early-warning)**

# Actor input Schema

## `locations` (type: `array`):

REQUIRED. One or more locations to check, each EITHER { "community": "Lokoja", "lga": "Lokoja", "state": "Kogi" } (lga/state are optional fallbacks used if the community name isn't recognised) OR { "latitude": 7.7322, "longitude": 8.5391, "label": "Near Makurdi" } (matched to the nearest covered community within 25km). Coverage is currently 14 communities in Kogi, Benue, Adamawa, Anambra and Lagos — see the README for the full list. A location outside coverage still returns a valid, explained result rather than an error.

## `horizons` (type: `array`):

Which forecast windows to score: 7, 30, or both. Defaults to both.

## `includeSources` (type: `boolean`):

Attach the source references used for each result.

## `mode` (type: `string`):

'check' scores the given locations. 'ingest' refreshes outlook, advisory and baseline data first.

## Actor input object example

```json
{
  "locations": [
    {
      "community": "Lokoja",
      "lga": "Lokoja",
      "state": "Kogi"
    },
    {
      "community": "Makurdi",
      "lga": "Makurdi",
      "state": "Benue"
    },
    {
      "latitude": 9.2792,
      "longitude": 12.4458,
      "label": "Yola/Jimeta"
    },
    {
      "community": "Ikorodu",
      "lga": "Ikorodu",
      "state": "Lagos"
    }
  ],
  "horizons": [
    7,
    30
  ],
  "includeSources": true,
  "mode": "check"
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "locations": [
        {
            "community": "Lokoja",
            "lga": "Lokoja",
            "state": "Kogi"
        },
        {
            "community": "Makurdi",
            "lga": "Makurdi",
            "state": "Benue"
        },
        {
            "latitude": 9.2792,
            "longitude": 12.4458,
            "label": "Yola/Jimeta"
        },
        {
            "community": "Ikorodu",
            "lga": "Ikorodu",
            "state": "Lagos"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("zainab77/nigeria-flood-early-warning").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "locations": [
        {
            "community": "Lokoja",
            "lga": "Lokoja",
            "state": "Kogi",
        },
        {
            "community": "Makurdi",
            "lga": "Makurdi",
            "state": "Benue",
        },
        {
            "latitude": 9.2792,
            "longitude": 12.4458,
            "label": "Yola/Jimeta",
        },
        {
            "community": "Ikorodu",
            "lga": "Ikorodu",
            "state": "Lagos",
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("zainab77/nigeria-flood-early-warning").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "locations": [
    {
      "community": "Lokoja",
      "lga": "Lokoja",
      "state": "Kogi"
    },
    {
      "community": "Makurdi",
      "lga": "Makurdi",
      "state": "Benue"
    },
    {
      "latitude": 9.2792,
      "longitude": 12.4458,
      "label": "Yola/Jimeta"
    },
    {
      "community": "Ikorodu",
      "lga": "Ikorodu",
      "state": "Lagos"
    }
  ]
}' |
apify call zainab77/nigeria-flood-early-warning --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zainab77/nigeria-flood-early-warning"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/sepbeTR8u548zKhoi/builds/gKrBW1n1zxjQ6sqmJ/openapi.json
