TL;DR: This guide shows you how to build a scrolling stock price ticker for Nigerian Exchange (NGX) stocks and embed it on any website. You only need the NGN Market API free plan to do it — no framework required, just HTML, CSS, and vanilla JavaScript. The full code is at the bottom.
You have seen them on financial websites — that horizontal scrolling strip at the top of the page showing stock symbols, prices, and percentage changes ticking by in real time. For global markets, there are dozens of widgets that do this out of the box. For Nigerian stocks, you are mostly on your own.
This guide builds one from scratch. By the end you will have a working NGX stock ticker you can drop into any website — a WordPress blog, a brokerage landing page, a news site, a fintech app. It fetches live prices from the NGN Market API, scrolls continuously, turns green or red based on price direction, and refreshes automatically every 20 minutes to stay in sync with NGX market updates.
No npm, no bundler, no framework. Just a single HTML file with embedded CSS and JavaScript. If you prefer React, there is a component version at the end.
What you need
A free NGN Market API key. Sign up at ngnmarket.com, open the developer dashboard, and generate one. Your key starts with ngm_live_ and is the only thing you need to get started — 3,000 calls per month, no card required.
That is genuinely enough for a ticker. A typical stock ticker showing 50 companies and refreshing every 20 minutes uses around 70 API calls per day — well within the free plan's monthly allowance.
How it works
The ticker pulls a list of all NGX-listed companies from the /companies endpoint. For each company it shows the ticker symbol, current price in Naira, and the day's percentage change. The strip scrolls continuously using a CSS animation. When prices update — which the NGX does every 20 minutes during trading hours — the ticker fetches fresh data and the new prices replace the old ones seamlessly.
The whole thing is around 120 lines of code.
Fetch the company data
The /companies endpoint returns live price data for every NGX-listed equity. You can fetch up to 200 companies in a single call. Here is the fetch function:
const API_KEY = 'ngm_live_YOUR_KEY';
async function fetchNGXPrices() {
const response = await fetch(
'https://api.ngnmarket.com/v1/companies?limit=200&sort=market_cap&order=desc',
{
headers: { 'Authorization': `Bearer ${API_KEY}` }
}
);
const body = await response.json();
if (!body.success) {
console.error('NGN Market API error:', body.error.message);
return [];
}
return body.data.data;
}
Each company object in the response includes everything the ticker needs:
{
"symbol": "DANGCEM",
"name": "Dangote Cement Plc",
"price": 1180.00,
"price_change": 0.00,
"price_change_percent": 0.00,
"volume": 284729,
"last_updated": "2026-06-13T16:00:00.000Z"
}
Build the ticker strip
The scrolling effect is pure CSS. The idea is simple: you render the list of stocks twice side by side, then animate them moving left. When the first copy scrolls off-screen, the second copy has taken its place and the loop looks seamless.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>NGX Stock Ticker</title>
<style>
.ticker-wrapper {
width: 100%;
overflow: hidden;
background: #0f1117;
padding: 10px 0;
border-bottom: 1px solid #1e2130;
}
.ticker-track {
display: flex;
width: max-content;
animation: ticker-scroll 60s linear infinite;
}
.ticker-wrapper:hover .ticker-track {
animation-play-state: paused;
}
@keyframes ticker-scroll {
0% { transform: translateX(0); }
100% { transform: translateX(-50%); }
}
.ticker-item {
display: flex;
align-items: center;
gap: 6px;
padding: 0 24px;
font-family: 'Inter', system-ui, sans-serif;
font-size: 13px;
white-space: nowrap;
border-right: 1px solid #1e2130;
}
.ticker-symbol {
font-weight: 600;
color: #ffffff;
letter-spacing: 0.04em;
}
.ticker-price {
color: #a0aec0;
}
.ticker-change {
font-weight: 500;
min-width: 52px;
text-align: right;
}
.ticker-change.up { color: #48bb78; }
.ticker-change.down { color: #fc8181; }
.ticker-change.flat { color: #718096; }
</style>
</head>
<body>
<div class="ticker-wrapper">
<div class="ticker-track" id="ticker-track">
<div class="ticker-item">
<span class="ticker-symbol">Loading NGX...</span>
</div>
</div>
</div>
<script>
const API_KEY = 'ngm_live_YOUR_KEY';
async function fetchNGXPrices() {
const res = await fetch(
'https://api.ngnmarket.com/v1/companies?limit=200&sort=market_cap&order=desc',
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
);
const body = await res.json();
if (!body.success) return [];
return body.data.data;
}
function buildTickerItem(company) {
const pct = company.price_change_percent ?? 0;
const dir = pct > 0 ? 'up' : pct < 0 ? 'down' : 'flat';
const sign = pct > 0 ? '+' : '';
const price = company.price.toLocaleString('en-NG', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
return `
<div class="ticker-item">
<span class="ticker-symbol">${company.symbol}</span>
<span class="ticker-price">₦${price}</span>
<span class="ticker-change ${dir}">${sign}${pct.toFixed(2)}%</span>
</div>
`;
}
async function renderTicker() {
const companies = await fetchNGXPrices();
if (!companies.length) return;
const track = document.getElementById('ticker-track');
const html = companies.map(buildTickerItem).join('');
track.innerHTML = html + html;
const duration = Math.max(30, companies.length * 1.2);
track.style.animationDuration = `${duration}s`;
}
function isMarketOpen() {
const now = new Date();
const day = now.getUTCDay();
const hour = now.getUTCHours();
return day >= 1 && day <= 5 && hour >= 8 && hour < 15;
}
renderTicker();
setInterval(() => {
if (isMarketOpen()) renderTicker();
}, 20 * 60 * 1000);
</script>
</body>
</html>
The hover pause is a small detail worth keeping — when someone mouses over the ticker to read a price, the animation stops. It restarts when they move away. This is standard behaviour on financial tickers and users expect it.
Customising the ticker
Change the scroll speed. The animationDuration is calculated from the number of companies, but you can override it. A lower number scrolls faster. For a prominent homepage ticker you probably want something between 40s and 80s. For a narrow sidebar, slow it down to 90s or more so prices are readable.
Show only certain sectors. The /companies endpoint accepts a ?sector= filter. To show only Financial Services stocks:
'https://api.ngnmarket.com/v1/companies?limit=200§or=Financial%20Services&sort=market_cap&order=desc'
Valid sector values include Financial Services, Industrial Goods, Consumer Goods, Oil & Gas, ICT, Healthcare, Conglomerates, Agriculture, and Utilities.
Show only the most active stocks. Sort by volume instead of market cap to surface the most traded stocks today:
'https://api.ngnmarket.com/v1/companies?limit=50&sort=volume&order=desc'
Add the company name. Some tickers show the full name alongside the symbol. Just add company.name to the buildTickerItem function — it is already in the response, so no extra call is needed.
Change the colour scheme. The CSS variables are all in the .ticker-wrapper and .ticker-item blocks. Swap #0f1117 for white and adjust the text colours for a light-mode ticker.
Adding it to WordPress
If your site is on WordPress and you want to embed the ticker without touching theme files, paste the full HTML into a Custom HTML block in the block editor. The script tag will execute and the ticker will render inline.
For a full-width ticker that spans the header, add the HTML to your theme's header.php right after the opening <body> tag, or use a header injection plugin to do it without editing theme files directly.
React version
If you are building on React or Next.js, here is a self-contained component that does the same thing:
import { useState, useEffect } from 'react';
const API_KEY = process.env.NEXT_PUBLIC_NGN_MARKET_API_KEY;
function TickerItem({ company }) {
const pct = company.price_change_percent ?? 0;
const dir = pct > 0 ? 'up' : pct < 0 ? 'down' : 'flat';
const sign = pct > 0 ? '+' : '';
const price = company.price.toLocaleString('en-NG', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
return (
<div className="ticker-item">
<span className="ticker-symbol">{company.symbol}</span>
<span className="ticker-price">₦{price}</span>
<span className={`ticker-change ${dir}`}>
{sign}{pct.toFixed(2)}%
</span>
</div>
);
}
export default function NGXTicker({ sector, limit = 100 }) {
const [companies, setCompanies] = useState([]);
const [paused, setPaused] = useState(false);
const fetchPrices = async () => {
const params = new URLSearchParams({
limit,
sort: 'market_cap',
order: 'desc',
...(sector && { sector }),
});
const res = await fetch(
`https://api.ngnmarket.com/v1/companies?${params}`,
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
);
const body = await res.json();
if (body.success) setCompanies(body.data.data);
};
useEffect(() => {
fetchPrices();
const isMarketOpen = () => {
const now = new Date();
const day = now.getUTCDay();
const hour = now.getUTCHours();
return day >= 1 && day <= 5 && hour >= 8 && hour < 15;
};
const interval = setInterval(() => {
if (isMarketOpen()) fetchPrices();
}, 20 * 60 * 1000);
return () => clearInterval(interval);
}, [sector, limit]);
const duration = Math.max(30, companies.length * 1.2);
return (
<div
className="ticker-wrapper"
onMouseEnter={() => setPaused(true)}
onMouseLeave={() => setPaused(false)}
>
<div
className="ticker-track"
style={{
animationDuration: `${duration}s`,
animationPlayState: paused ? 'paused' : 'running',
}}
>
{[...companies, ...companies].map((c, i) => (
<TickerItem key={`${c.symbol}-${i}`} company={c} />
))}
</div>
</div>
);
}
Use it like this:
<NGXTicker />
<NGXTicker sector="Financial Services" limit={30} />
<NGXTicker limit={20} />
Add the CSS from the vanilla version to your global stylesheet or a CSS module alongside the component.
A note on data freshness
The NGX market updates prices every 20 minutes during trading hours. Outside those hours — after 16:00 WAT on weekdays, and all weekend — the prices stay at the last session close until the next session opens.
This matters for the ticker because there is no point fetching new data at 11pm on a Friday. The isMarketOpen() check in both versions handles this. On a typical weekday with the market open for seven hours, the ticker makes around 21 API calls — well within any plan's limits.
If your site has high traffic, proxy the API request through your own backend and cache the response for 20 minutes. That way a thousand concurrent visitors share one API call rather than each making their own.
Frequently asked questions
Can I use this ticker on a commercial website?
Yes. The NGN Market API terms allow commercial use. If your site generates revenue and you need more than 3,000 calls per month, the Hobby plan at ₦15,000/month gives you 10,000 calls — enough for a busy site with several widgets running.
What if a company has no price data for the day?
The API returns the last known close price with a price_change_percent of 0 for companies that did not trade on the current session. The ticker handles this with the flat CSS class — the change shows as 0.00% in grey rather than green or red.
Can I show only gainers or losers in the ticker?
Yes, filter the companies array before rendering:
const gainers = companies.filter(c => c.price_change_percent > 0);
const losers = companies.filter(c => c.price_change_percent < 0);
Then pass whichever array you want to the ticker renderer.
Does this work on mobile?
Yes. The CSS animation uses transform: translateX which is GPU-accelerated on mobile browsers. The ticker scrolls smoothly on iOS and Android. The hover-pause will not trigger on touch screens (there is no hover state), which is actually fine — on mobile, users would not be trying to pause and read individual items.
Can I show ETF or bond prices instead of equities?
ETF prices are available via the /etfs endpoint and bonds via /bonds. Both return the same price fields. Swap the fetch URL in the fetchNGXPrices function and the ticker will render ETF or bond data instead.
The full API documentation covers every field returned by the /companies endpoint in detail, including the 7-day and 52-week change percentages, volume, value traded, and market cap — all of which you can add to the ticker if you want to show more than just the daily move.