Back to Blog
african stock market apiafrican stock exchange apiafrica stock apipan-african stock data api

How to Build an African Stock Market App with Mansa API

*Last updated: May 9, 2026 | Reading time: ~14 minutes*

March 21, 2026 · 13 min read · Mansa Markets

Last updated: May 9, 2026 | Reading time: ~14 minutes


Building an African stock market app has always meant stitching together unreliable sources — scraping exchange websites in five different formats, maintaining six separate data pipelines, and handling half a dozen currencies. Mansa API changes that. It is a single REST API covering live stock prices, market indices, top movers, forex rates, and commodity prices across 20 African exchanges, all in clean, consistent JSON.

This guide walks you through building a fully functional African stock market dashboard using the Mansa API. By the end you will have a working app that displays live prices from the Nigerian Exchange (NGX), Ghana Stock Exchange (GSE), Nairobi Securities Exchange (NSE), Johannesburg Stock Exchange (JSE), BRVM (Francophone West Africa), Dar es Salaam Stock Exchange (DSE), and Lusaka Securities Exchange (LUSE).


## What You Can Build with the African Stock Market API

Before writing any code, here is what Mansa API gives you across all seven exchanges:

  • Live stock prices and quotes for 1000+ listed equities across 20 African markets
  • Exchange-level snapshots — index value, market cap, volume, advances, declines
  • Top gainers and losers per exchange — updated throughout market hours
  • Pan-African movers — the single biggest gainers and losers across every tracked exchange at once
  • African market indices — ASI, GSI, NSE 20, JSE All Share, BRVM Composite, and more
  • Forex rates for African currencies — NGN, GHS, KES, ZAR, XOF, TZS, ZMW, and others
  • Commodity prices relevant to African producers — crude oil, cocoa, coffee, copper, gold
  • Exchange metadata — market type, settlement cycle, trading hours, regulator

Every response is consistent JSON. The same field names, the same structure, the same error format — whether you are fetching a stock in Lagos or Lusaka.


## Getting Your API Key

Go to mansamarkets.com/developers and request a free key. Authentication uses a Bearer token on every request:

``` Authorization: Bearer mansa_live_sk_xxxxxxxxxxxxxxxxxxxxxxxx ```

The free standard tier gives you enough requests to build and test your app. When you are ready to launch, paid tiers unlock higher daily limits and priority support.


## Step 1 — List All African Exchanges

Start by fetching every tracked exchange. This gives you exchange codes, names, countries, and their latest index snapshot.

```javascript const fetchExchanges = async (apiKey) => { const response = await fetch('https://mansaapi.com/api/v1/markets/exchanges', { headers: { 'Authorization': `Bearer ${apiKey}` } }); const { data } = await response.json(); return data; }; ```

Sample response:

```json [ { "code": "NGX", "name": "Nigerian Exchange", "country": "Nigeria", "currency": "NGN", "index_name": "All Share Index", "index_value": 101245.82, "index_change_pct": 0.34, "market_cap": 58200000000000, "status": "open" }, { "code": "GSE", "name": "Ghana Stock Exchange", "country": "Ghana", "currency": "GHS", "index_name": "GSE Composite Index", "index_value": 4821.44, "index_change_pct": -0.12, "market_cap": 87400000000, "status": "open" }, { "code": "JSE", "name": "Johannesburg Stock Exchange", "country": "South Africa", "currency": "ZAR", "index_name": "JSE All Share", "index_value": 81342.10, "index_change_pct": 0.58, "status": "open" } ] ```

Exchange codes you will use throughout the API: `NGX`, `GSE`, `NSE`, `JSE`, `BRVM`, `DSE`, `LUSE`.


## Step 2 — Get a Single Exchange Overview

For a country-specific market page, fetch one exchange by its code. Returns the latest index snapshot alongside full exchange metadata.

```javascript const fetchExchange = async (apiKey, exchangeCode) => { const response = await fetch( `https://mansaapi.com/api/v1/markets/exchanges/${exchangeCode}`, { headers: { 'Authorization': `Bearer ${apiKey}` } } ); const { data } = await response.json(); return data; };

// Usage const nigeria = await fetchExchange(apiKey, 'NGX'); const ghana = await fetchExchange(apiKey, 'GSE'); const kenya = await fetchExchange(apiKey, 'NSE'); ```


## Step 3 — Fetch Stocks for an Exchange

Returns all listed stocks for a given exchange. Supports filtering by sector and sorting by price, change, or volume.

```javascript const fetchStocks = async (apiKey, exchangeCode, options = {}) => { const params = new URLSearchParams({ limit: options.limit ?? 50, offset: options.offset ?? 0, sort_by: options.sortBy ?? 'change_pct', order: options.order ?? 'desc', ...(options.sector ? { sector: options.sector } : {}) });

const response = await fetch( `https://mansaapi.com/api/v1/markets/exchanges/${exchangeCode}/stocks?${params}`, { headers: { 'Authorization': `Bearer ${apiKey}` } } ); const { data } = await response.json(); return data; };

// Get top-moving Nigerian stocks const ngxStocks = await fetchStocks(apiKey, 'NGX', { sortBy: 'change_pct', order: 'desc', limit: 20 });

// Get Kenyan banking stocks const kenyaBanks = await fetchStocks(apiKey, 'NSE', { sector: 'Banking', limit: 50 }); ```

Sample response:

```json [ { "ticker": "DANGCEM", "name": "Dangote Cement Plc", "price": 665.00, "change_pct": 2.31, "volume": 1245890, "sector": "Industrial Goods", "currency": "NGN" }, { "ticker": "GTCO", "name": "Guaranty Trust Holding Co", "price": 52.80, "change_pct": 1.73, "volume": 6523100, "sector": "Financial Services", "currency": "NGN" } ] ```


## Step 4 — Get a Single Stock Quote

For stock detail pages — fetch one stock by exchange code and ticker symbol.

```javascript const fetchStock = async (apiKey, exchangeCode, ticker) => { const response = await fetch( `https://mansaapi.com/api/v1/markets/exchanges/${exchangeCode}/stocks/${ticker}`, { headers: { 'Authorization': `Bearer ${apiKey}` } } ); const { data } = await response.json(); return data; };

// Usage const dangote = await fetchStock(apiKey, 'NGX', 'DANGCEM'); const safaricom = await fetchStock(apiKey, 'NSE', 'SCOM'); const naspers = await fetchStock(apiKey, 'JSE', 'NPN'); const mtnGhana = await fetchStock(apiKey, 'GSE', 'MTNGH'); ```


## Step 5 — Top Movers Per Exchange

Fetch the top gainers and losers for a single exchange. Useful for country-specific market summary screens.

```javascript const fetchMovers = async (apiKey, exchangeCode, type = 'both', limit = 5) => { const params = new URLSearchParams({ type, limit }); const response = await fetch( `https://mansaapi.com/api/v1/markets/exchanges/${exchangeCode}/movers?${params}`, { headers: { 'Authorization': `Bearer ${apiKey}` } } ); const { data } = await response.json(); return data; };

// Get top 5 gainers and losers on the Ghana Stock Exchange const ghanaMovers = await fetchMovers(apiKey, 'GSE', 'both', 5);

// Get top gainers only on the NSE const kenyaGainers = await fetchMovers(apiKey, 'NSE', 'gainers', 10); ```

Sample response:

```json { "gainers": [ { "ticker": "MTNGH", "name": "MTN Ghana", "price": 2.85, "change_pct": 9.62, "volume": 4820100, "currency": "GHS" } ], "losers": [ { "ticker": "GCB", "name": "GCB Bank Ltd", "price": 6.10, "change_pct": -3.17, "volume": 1240000, "currency": "GHS" } ] } ```


## Step 6 — Pan-African Top Movers

This is the standout endpoint in the African stock market API — the biggest gainers and losers across every tracked African exchange at once. No other API provides this. A stock surging in Lusaka shows up alongside one crashing in Lagos, all in a single response.

```javascript const fetchPanAfricanMovers = async (apiKey, limit = 10) => { const params = new URLSearchParams({ limit }); const response = await fetch( `https://mansaapi.com/api/v1/markets/movers/pan-african?${params}`, { headers: { 'Authorization': `Bearer ${apiKey}` } } ); const { data } = await response.json(); return data; };

const continentMovers = await fetchPanAfricanMovers(apiKey, 10); ```

Sample response:

```json { "gainers": [ { "ticker": "PUMA", "exchange": "BRVM", "name": "Puma Energy CI", "price": 3250.00, "change_pct": 10.00, "currency": "XOF" }, { "ticker": "ZCCM", "exchange": "LUSE", "name": "ZCCM Investments", "price": 42.50, "change_pct": 7.80, "currency": "ZMW" } ], "losers": [ { "ticker": "EABL", "exchange": "NSE", "name": "East African Breweries", "price": 148.25, "change_pct": -4.90, "currency": "KES" } ] } ```

If you are building a continental market dashboard — the kind that shows what is moving across Africa right now — this is your headline endpoint.


## Step 7 — African Market Indices

Fetch all tracked African market indices in one call. Returns the latest index value, daily change, and the exchange each index belongs to.

```javascript const fetchIndices = async (apiKey) => { const response = await fetch('https://mansaapi.com/api/v1/markets/indices', { headers: { 'Authorization': `Bearer ${apiKey}` } }); const { data } = await response.json(); return data; }; ```

Sample response:

```json [ { "name": "NGX All Share Index", "exchange": "NGX", "value": 101245.82, "change_pct": 0.34 }, { "name": "GSE Composite Index", "exchange": "GSE", "value": 4821.44, "change_pct": -0.12 }, { "name": "NSE 20 Share Index", "exchange": "NSE", "value": 1842.30, "change_pct": 0.21 }, { "name": "JSE All Share", "exchange": "JSE", "value": 81342.10, "change_pct": 0.58 }, { "name": "BRVM Composite", "exchange": "BRVM","value": 271.44, "change_pct": -0.08 }, { "name": "DSE All Share", "exchange": "DSE", "value": 2184.55, "change_pct": 0.14 }, { "name": "LUSE All Share", "exchange": "LUSE","value": 8421.90, "change_pct": 1.02 } ] ```


## Step 8 — Forex Rates for African Currencies

Live forex rates covering the currencies of every tracked market. Essential for any multi-exchange app that needs to display normalized values or compare performance across borders.

```javascript const fetchForex = async (apiKey) => { const response = await fetch('https://mansaapi.com/api/v1/markets/forex', { headers: { 'Authorization': `Bearer ${apiKey}` } }); const { data } = await response.json(); return data; }; ```

Sample response:

```json [ { "pair": "USD/NGN", "rate": 1580.50, "change_pct": 0.12 }, { "pair": "USD/GHS", "rate": 15.84, "change_pct": -0.08 }, { "pair": "USD/KES", "rate": 129.40, "change_pct": 0.05 }, { "pair": "USD/ZAR", "rate": 18.72, "change_pct": 0.31 }, { "pair": "USD/XOF", "rate": 615.00, "change_pct": 0.00 }, { "pair": "USD/TZS", "rate": 2680.00, "change_pct": -0.04 }, { "pair": "USD/ZMW", "rate": 27.10, "change_pct": 0.18 } ] ```


## Step 9 — Commodity Prices

African equity markets are deeply tied to commodity prices — crude oil drives NGX energy stocks, cocoa moves the GSE, coffee shapes the NSE, copper swings the LUSE. This endpoint surfaces the commodity prices most relevant to African producers and traders.

```javascript const fetchCommodities = async (apiKey) => { const response = await fetch('https://mansaapi.com/api/v1/markets/commodities', { headers: { 'Authorization': `Bearer ${apiKey}` } }); const { data } = await response.json(); return data; }; ```

Displaying commodity prices alongside stock data gives your users the macro context that explains why certain sectors are moving. An investor in SEPLAT or Oando (NGX energy names) cares about the Brent crude price every bit as much as the stock price.


## Step 10 — Exchange Metadata

Returns structured reference data for each exchange — market type, settlement cycle, trading hours, website, regulator. Useful for exchange detail pages and developer onboarding flows.

```javascript const fetchExchangeMetadata = async (apiKey) => { const response = await fetch('https://mansaapi.com/api/v1/markets/exchange-metadata', { headers: { 'Authorization': `Bearer ${apiKey}` } }); const { data } = await response.json(); return data; }; ```


## Building the Full Multi-Exchange Dashboard

Here is a complete React component that pulls from multiple Mansa API endpoints in parallel to build an African market dashboard:

```jsx import { useState, useEffect } from 'react';

const API_KEY = process.env.NEXT_PUBLIC_MANSA_API_KEY; const BASE_URL = 'https://mansaapi.com/api/v1'; const headers = { 'Authorization': `Bearer ${API_KEY}` };

const EXCHANGES = ['NGX', 'GSE', 'NSE', 'JSE', 'BRVM', 'DSE', 'LUSE'];

export default function AfricanMarketDashboard() { const [exchanges, setExchanges] = useState([]); const [panMovers, setPanMovers] = useState(null); const [indices, setIndices] = useState([]); const [forex, setForex] = useState([]); const [loading, setLoading] = useState(true);

useEffect(() => { const loadData = async () => { try { const [exchangesRes, moversRes, indicesRes, forexRes] = await Promise.all([ fetch(`${BASE_URL}/markets/exchanges`, { headers }), fetch(`${BASE_URL}/markets/movers/pan-african`, { headers }), fetch(`${BASE_URL}/markets/indices`, { headers }), fetch(`${BASE_URL}/markets/forex`, { headers }), ]);

const [exchangesData, moversData, indicesData, forexData] = await Promise.all([ exchangesRes.json(), moversRes.json(), indicesRes.json(), forexRes.json(), ]);

setExchanges(exchangesData.data ?? []); setPanMovers(moversData.data ?? null); setIndices(indicesData.data ?? []); setForex(forexData.data ?? []); } catch (error) { console.error('Failed to load Mansa market data:', error); } finally { setLoading(false); } };

loadData(); const interval = setInterval(loadData, 60000); return () => clearInterval(interval); }, []);

if (loading) return <div>Loading African market data...</div>;

return ( <div className="africa-dashboard">

{/ Index Strip /} <div className="index-strip"> {indices.map(idx => ( <div key={idx.name} className="index-item"> <span className="index-name">{idx.exchange}</span> <span className="index-value">{idx.value?.toLocaleString()}</span> <span className={idx.change_pct >= 0 ? 'up' : 'down'}> {idx.change_pct >= 0 ? '+' : ''}{idx.change_pct?.toFixed(2)}% </span> </div> ))} </div>

{/ Pan-African Top Movers /} {panMovers && ( <div className="pan-movers"> <div className="movers-col"> <h3>Top Gainers — All Africa</h3> {panMovers.gainers?.map(stock => ( <div key={`${stock.exchange}-${stock.ticker}`} className="mover-row"> <span className="exchange-badge">{stock.exchange}</span> <span className="ticker">{stock.ticker}</span> <span className="change up">+{stock.change_pct?.toFixed(2)}%</span> </div> ))} </div> <div className="movers-col"> <h3>Top Losers — All Africa</h3> {panMovers.losers?.map(stock => ( <div key={`${stock.exchange}-${stock.ticker}`} className="mover-row"> <span className="exchange-badge">{stock.exchange}</span> <span className="ticker">{stock.ticker}</span> <span className="change down">{stock.change_pct?.toFixed(2)}%</span> </div> ))} </div> </div> )}

{/ Exchange Cards /} <div className="exchange-grid"> {exchanges.map(ex => ( <div key={ex.code} className="exchange-card"> <h4>{ex.name} <span className="country">({ex.country})</span></h4> <div className="exchange-stat"> <label>{ex.index_name}</label> <span>{ex.index_value?.toLocaleString()}</span> <span className={ex.index_change_pct >= 0 ? 'up' : 'down'}> {ex.index_change_pct >= 0 ? '+' : ''}{ex.index_change_pct?.toFixed(2)}% </span> </div> <div className="exchange-stat"> <label>Currency</label> <span>{ex.currency}</span> </div> </div> ))} </div>

{/ Forex Strip /} <div className="forex-strip"> <h3>African Forex Rates</h3> <div className="forex-grid"> {forex.map(rate => ( <div key={rate.pair} className="forex-item"> <span className="pair">{rate.pair}</span> <span className="rate">{rate.rate?.toLocaleString()}</span> <span className={rate.change_pct >= 0 ? 'up' : 'down'}> {rate.change_pct >= 0 ? '+' : ''}{rate.change_pct?.toFixed(2)}% </span> </div> ))} </div> </div>

</div> ); } ```


## Rate Limiting and Best Practices

Building on a multi-exchange API introduces patterns that single-exchange developers do not always consider:

Respect market hours per exchange. Each African exchange has different trading hours and different local holidays. The Lusaka Securities Exchange operates on Zambia Standard Time; the BRVM operates on West Africa Time and is closed for Francophone holidays that mean nothing to a Lagos-based developer. Use the exchange metadata endpoint to know when each market is live before polling.

Cache at the exchange level, not the app level. If your app shows NGX and GSE, cache them separately. NGX updates every 30 seconds during market hours; GSE may update less frequently. A single global cache TTL will either leave you polling a closed market or serving stale data from an active one.

Normalise currencies on your server, not in the client. Do not expose raw currency conversion logic in the browser. Pull the forex endpoint server-side, apply it to stock prices, and serve your users normalised values (USD equivalent or their local currency) from your own API layer.

Store your API key server-side. Never put `mansa_live_sk_...` in client-side JavaScript. Use environment variables and proxy requests through your own backend — the same discipline applies whether you are building with Next.js, Express, FastAPI, or anything else.

Handle partial failures gracefully. If the BRVM data is temporarily unavailable, your dashboard should still render the NGX, GSE, NSE, and JSE cards. Use `Promise.allSettled` instead of `Promise.all` for resilience, and show a clear per-exchange unavailable state rather than crashing the whole app.

```javascript // Resilient multi-exchange fetch const results = await Promise.allSettled( EXCHANGES.map(code => fetch(`${BASE_URL}/markets/exchanges/${code}`, { headers }).then(r => r.json()) ) );

const exchanges = results .filter(r => r.status === 'fulfilled') .map(r => r.value.data); ```


## What to Build Next

Once your core African stock market app is running, these features drive the most user value:

Per-exchange stock screener — use the `/stocks` endpoint with `sector` and `sort_by` filters to let users slice and filter within each market. An investor who only cares about Kenyan banking stocks or South African mining names can go straight to the view they need.

Pan-African portfolio tracker — a user holds DANGCEM on the NGX, Safaricom on the NSE, and Naspers on the JSE. Let them input holdings across all three exchanges and calculate a consolidated portfolio value using live prices and the forex endpoint to normalize into one currency.

Commodity-equity correlation view — display commodity prices (cocoa, crude oil, copper) alongside the stocks most exposed to each. A rising cocoa price is a direct signal for GSE-listed AKPRAWN and CFAO; a crude rally moves SEPLAT and TOTAL on the NGX. This kind of contextual display is rare in African fintech and immediately differentiates your product.

BRVM coverage for the Francophone market — the BRVM covers 8 West African countries (Côte d'Ivoire, Senegal, Mali, Burkina Faso, and more) under one exchange using the CFA franc (XOF). Most African market apps ignore it entirely. Building BRVM coverage in gives you automatic reach across the WAEMU region with no additional data integration cost.


## How to Get Started with the African Stock Market API

Get your free Mansa API key at mansamarkets.com/developers. The free tier gives you enough requests to build and test a complete multi-exchange dashboard. Upgrade when you are ready to launch to production.

Full API documentation, response schemas, and code examples in Python, JavaScript, and cURL are at the same link. There is also an OpenAPI spec at `/openapi.json` and an `llms.txt` for AI agent integrations.

If you are building something on African market data — a portfolio tracker, a stock screener, a fintech dashboard, a quant model — email hello@mansamarkets.com. We actively support developers integrating the API and have helped teams across Lagos, Accra, Nairobi, and Johannesburg build production applications on African market data.

> Track live prices across all 20 African exchanges on Mansa Markets — the continent's most comprehensive stock market data terminal, covering 1000+ stocks in one place.