Get naira (NGN) exchange rates by API in Python and JavaScript
Most exchange-rate APIs give you one number per pair — a mid nobody transacts at. For African corridors that number is not the market. This walkthrough fetches what named providers actually quote for GBP→NGN, with spread and an independent mid, in a few lines of Python and JavaScript.
Updated 2 Sep 2026 · 7 min read · by Modan
1. Get a key
Sign up — no card. Your first key is minted automatically and shown under Account → API keys. A free key allows 50 requests per UTC day; Individual allows 250 and Team 1,000. Every request carries the key in the X-API-Key header.
2. The request
One call returns every tracked provider's latest quote on the corridor, each with rate, fee, spread_bps (distance below the best rate of the same kind) and last_updated, plus mid_rate and per-provider vs_mid_bps when the independent mid is available. The response also states its freshness: data_freshness is hourly on Free and Individual keys (with an as_of timestamp) and realtime on Team keys.
curl "https://modan.io/api/v1/rates?from=GBP&to=NGN" \ -H "X-API-Key: mdn_live_YOUR_KEY_HERE"
3. Python
Print the providers ranked by spread, and the mid if there is one.
import os, requests
BASE = "https://modan.io/api/v1"
HEADERS = {"X-API-Key": os.environ["MODAN_API_KEY"]}
r = requests.get(f"{BASE}/rates", params={"from": "GBP", "to": "NGN"}, headers=HEADERS, timeout=15)
r.raise_for_status()
book = r.json()
print(book["corridor"], "as of", book.get("as_of") or book["timestamp"], f"({book['data_freshness']})")
if book.get("mid_rate"):
print("independent mid:", book["mid_rate"], "from", book["mid_source"])
for p in sorted(book["providers"], key=lambda p: p["spread_bps"]):
vs_mid = f'{p["vs_mid_bps"]:+.1f} bps vs mid' if "vs_mid_bps" in p else "no mid"
print(f'{p["provider_name"]:<16} {p["rate"]:>12,.2f} fee {p["fee"]:>6} {p["spread_bps"]:>6.1f} bps {vs_mid}')
print("remaining today:", r.headers.get("X-RateLimit-Remaining"))4. JavaScript
The same call with fetch, in Node 18+ or the browser.
const BASE = "https://modan.io/api/v1";
const headers = { "X-API-Key": process.env.MODAN_API_KEY };
const res = await fetch(`${BASE}/rates?from=GBP&to=NGN`, { headers });
if (res.status === 429) throw new Error(`quota exhausted until ${res.headers.get("X-RateLimit-Reset")}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const book = await res.json();
const executable = book.providers.filter((p) => !["official", "parallel"].includes(p.rate_type ?? "retail"));
const best = executable.reduce((a, b) => (b.rate > a.rate ? b : a));
console.log(`${book.corridor}: best executable ${best.provider_name} ${best.rate} (${book.data_freshness})`);5. Convert an amount, net of fees
/convert?from=GBP&to=NGN&amount=1000 returns, for every provider, the gross converted amount and the amount net of the provider's fee, and names the provider that delivers the most — among executable quotes only. A central bank's official reference is returned and labelled but never chosen as best.
6. History and quotas
/rates/history?from=GBP&to=NGN&period=30d returns timestamped observations (oldest first, paginated); /time-series buckets them daily or hourly; /historical?date=… returns the book as it stood on a past date. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset; past the daily limit you receive 429 until midnight UTC. /status needs no key and does not count.
If your consumer is an AI agent rather than a script, the MCP server exposes the same data as tools, on the same key and quota.
Frequently asked questions
- Is there a free naira exchange rate API?
- Yes. A Modan key is free on signup and allows 50 requests per UTC day, with rates as of the top of the current hour. Paid plans raise the limit to 250 (Individual) and 1,000 (Team, real-time).
- How fresh is the data on the free plan?
- Hourly: responses on Free and Individual keys are as of the top of the current UTC hour and say so in data_freshness and as_of. Team keys receive every observation in real time. Providers themselves are re-checked roughly every 15 minutes.
- Which other currencies are covered?
- Every corridor Modan tracks is listed at /corridors and returned by GET /corridors — 80 pairs at the time of writing across NGN, GHS, KES, XOF, ZAR, EGP and more, plus USDT and USDC corridors.