Build on real-time power outage and restoration data from our reporter network across all 36 states and the FCT. Filter by state, LGA, area, or event type.
Pass your API key in the X-API-Key header. All responses are JSON.
# Get latest 10 outages in Lagos curl "https://djurokbkkqrmpafcxlez.supabase.co/functions/v1/nepawatch-api?state=Lagos&type=out&limit=10" \ -H "X-API-Key: YOUR_API_KEY"
The API currently exposes one endpoint for reading outage reports. More endpoints are planned.
| Parameter | Type | Description |
|---|---|---|
| state | string | State name — case-insensitive and punctuation-insensitive. lagos, Cross River and abuja (maps to FCT) all work. Unknown states return UNKNOWN_STATE with a valid-states list. |
| lga | string | Alias for area. Our data is area-level, so both match the same field. |
| area | string | Filter by area/neighbourhood name (partial match) |
| type | string | out for outages, bck for restorations |
| source | string | crowd for human-submitted reports only, ai for model predictions only. Omit to get both — every row is labelled. |
| since | ISO date | Return reports at or after this timestamp. E.g. 2026-01-01T00:00:00Z |
| until | ISO date | Return reports at or before this timestamp |
| order | string | desc (newest first, default) or asc |
| limit | integer | Results per page — max 100, default 50 |
| offset | integer | Pagination offset, default 0 |
{
"data": [
{
"id": "8f3a1c2d-...",
"type": "out",
"location_name": "Lekki Phase 1",
"location_area": "Lekki Phase 1",
"location_state": "Lagos",
"confirms": 7,
"source": "crowd",
"created_at": "2026-06-12T08:32:14Z"
}
],
"meta": {
"count": 1,
"total": 248,
"limit": 50,
"offset": 0,
"has_more": true,
"source": "all",
"order": "desc",
"requests_today": 4,
"requests_remaining": 96,
"daily_limit": 100,
"tier": "free"
}
}
Every error returns JSON with a stable code. Branch on the code, not the message — messages may be reworded, codes will not.
| Status | Code | Meaning |
|---|---|---|
| 401 | MISSING_API_KEY | No X-API-Key header was sent |
| 403 | INVALID_API_KEY | Key not recognised |
| 403 | KEY_PENDING | Key exists but payment is still being confirmed — completes in seconds |
| 403 | KEY_REVOKED | Key was revoked |
| 400 | UNKNOWN_STATE | State not recognised. The response lists every valid state in valid_states. |
| 400 | INVALID_TYPE | type must be out or bck |
| 400 | INVALID_DATE | since/until is not a valid ISO 8601 timestamp |
| 429 | RATE_LIMITED | Daily limit hit. Wait the seconds given in the Retry-After header. |
Present on every response, so you can back off before you are cut off.
X-RateLimit-Limit: 100 X-RateLimit-Remaining: 96 X-RateLimit-Reset: 2026-08-05T23:59:59Z
NEPAWatch returns two kinds of reports. Every row tells you which is which via the source field.
| Source | What it is | Coverage |
|---|---|---|
crowd |
Reports submitted by real users, with confirmation counts from others in the same area | Strongest in Lagos and Abuja; thinner elsewhere |
ai |
Predictions from our model, based on historical patterns, DISCO rotation cycles, NERC tariff bands and weather. Includes an ai_probability score |
All 291 mapped areas nationwide |
If your use case needs verified human reports only, pass ?source=crowd. Our model is trained exclusively on human reports — it never trains on its own output.
Rows with source: "ai" include the model's confidence. Crowd rows do not have this field.
{
"id": "c41f0b7e-...",
"type": "out",
"location_area": "Gwarinpa",
"location_state": "FCT",
"confirms": 1,
"source": "ai",
"ai_probability": 0.67,
"created_at": "2026-08-05T14:10:02Z"
}Start free with 100 requests/day. Upgrade to Pro for high-volume use cases.
| Tier | Daily Limit | Rate Window | Price |
|---|---|---|---|
| Free FREE | 100 requests/day | Resets at midnight UTC | Free forever |
| Pro PRO | 10,000 requests/day | Resets at midnight UTC | ₦5,000/mo Sign in to upgrade → |
Rate limit headers are included in every response via the meta object. Exceeding your limit returns HTTP 429.
Sign in (or create an account), generate your free key instantly, pick a paid plan when you need more — everything in one place.