TL;DR: The most common NGN Market API integration mistakes are hitting the wrong endpoint paths, misreading error codes, burning through quota on failed requests, and polling outside market hours. This guide covers each one with the exact fix so you can stop losing calls to preventable errors.
Building with a new API always has a breaking-in period. You read the docs, write your first fetch call, and then spend the next hour wondering why everything is returning 404. It happens with every API, and the NGN Market API is no different.
After watching how developers actually integrate the API in practice, certain mistakes come up over and over. The same wrong endpoint paths, the same misread error responses, the same quota draining patterns. None of them are obvious until someone points them out.
This guide covers the ones that cause the most pain, and the fix for each.
Mistake 1: Calling /v1/stocks/:symbol instead of /v1/companies/:symbol
This is the single most common mistake, and it makes complete sense why it happens. If you are coming from any other stock market API — Alpha Vantage, Polygon, Twelve Data — the path for fetching a stock is almost always something like /stocks/:symbol or /quote/:symbol. It is the convention the industry has drifted toward.
The NGN Market API uses /companies/:symbol.
So if you have written something like this:
const res = await fetch('https://api.ngnmarket.com/v1/stocks/DANGCEM', {
headers: { 'Authorization': `Bearer ${API_KEY}` }
});
Change it to this:
const res = await fetch('https://api.ngnmarket.com/v1/companies/DANGCEM', {
headers: { 'Authorization': `Bearer ${API_KEY}` }
});
The reasoning behind the naming is that the API covers more than just stock prices. Companies have profiles, financials, dividends, disclosures, and news — all scoped under the same /companies/:symbol namespace. Stocks are one view of a company, not the whole thing.
The same pattern applies throughout. The full list of equities is at /companies, not /stocks. Historical price data is at /companies/:symbol/chart, not /stocks/:symbol/history. Once you have the namespace right, everything else follows.
Mistake 2: Calling /v1/forex/rates instead of /v1/forex/current
The second most common path error. Developers expecting a /rates endpoint because that is what most forex APIs call it. The NGN Market API splits forex into two endpoints:
/v1/forex/current for live NGN exchange rates (free plan)
/v1/forex/history for historical rates (Hobby plan and above)
const res = await fetch('https://api.ngnmarket.com/v1/forex/rates', ...);
const res = await fetch('https://api.ngnmarket.com/v1/forex/current', ...);
const res = await fetch('https://api.ngnmarket.com/v1/forex/history?source=USD&from=2026-01-01', ...);
If you have been hitting /v1/forex/rates and getting 404s, check your quota. Every failed request still counts against your monthly allowance. The calls are not refunded for 404s.
Mistake 3: Not reading the error response body
When a request fails, most developers look at the HTTP status code and stop there. A 403 means forbidden, a 404 means not found, move on. But the NGN Market API returns a detailed error object in the response body that tells you exactly what went wrong and sometimes exactly what to do about it.
This is what a plan restriction error actually looks like:
{
"success": false,
"error": {
"code": "PLAN_REQUIRED",
"message": "This endpoint requires a starter plan or higher.",
"required_plan": "starter",
"current_plan": "free"
}
}
That required_plan field tells you exactly which plan you need. You do not have to guess, check the docs, or open a support ticket. The response tells you.
Similarly, a quota error looks like this:
{
"success": false,
"error": {
"code": "QUOTA_EXCEEDED",
"message": "Monthly call limit reached."
},
"meta": {
"plan": "free",
"calls_used": 3000,
"calls_remaining": 0,
"reset_at": "2026-07-13T00:00:00.000Z"
}
}
Notice that even on a quota error, the meta object is still present. You can read meta.reset_at and tell your users exactly when the API will be available again rather than showing a generic error message.
Here is a proper error handler that reads all of this:
async function apiRequest(path) {
const res = await fetch(`https://api.ngnmarket.com/v1${path}`, {
headers: { 'Authorization': `Bearer ${API_KEY}` }
});
const body = await res.json();
if (!body.success) {
const { code, message, required_plan, current_plan } = body.error;
switch (code) {
case 'PLAN_REQUIRED':
console.error(
`Plan required: you are on ${current_plan}, this endpoint needs ${required_plan}`
);
break;
case 'QUOTA_EXCEEDED':
console.error(
`Quota exceeded. Resets at ${body.meta.reset_at}`
);
break;
case 'RATE_LIMITED':
console.warn('Rate limited — back off for 60 seconds');
break;
case 'INVALID_API_KEY':
console.error('Invalid API key — check your key in the dashboard');
break;
case 'NOT_FOUND':
console.error(`Endpoint not found: ${path} — check the docs for the correct path`);
break;
default:
console.error(`API error: ${message}`);
}
throw new Error(message);
}
return body;
}
Reading the error code rather than just the HTTP status turns a confusing failure into a clear diagnosis.
Mistake 4: Burning quota on failed requests without realising it
This one hurts the most because by the time you notice, the damage is done.
Every request to the API counts against your monthly quota, regardless of whether it succeeds. A 404 from a wrong path costs one call. A 403 from a plan gate costs one call. If you have an integration that is silently failing and you do not catch it early, you can burn through a significant portion of your monthly allowance on requests that return nothing useful.
Two habits that prevent this:
Check meta.calls_remaining on every response. It is on every successful response and on quota error responses. Log it or surface it in your admin dashboard so you can see the trend. A sudden drop in remaining calls that does not match your expected usage is a signal that something in your integration is failing silently.
const body = await apiRequest('/market/snapshot');
console.log(`Calls remaining: ${body.meta.calls_remaining}`);
console.log(`Resets at: ${body.meta.reset_at}`);
Test with one symbol before looping. If you are building something that calls the API in a loop — fetching prices for a list of companies, scanning multiple tickers — test the single call first and confirm it returns a successful response before running the loop. A wrong endpoint path in a loop that runs 50 times costs 50 calls, all 404s.
Mistake 5: Using Bloomberg/Reuters-style symbol notation
If you are coming from a financial data background or building an app that integrates with Bloomberg or Reuters data, you may be used to symbol notation that includes the exchange suffix, like DANGCEM:NGX or DANGCEM.LG.
The NGN Market API uses plain ticker symbols only. No exchange suffix, no dot notation.
/v1/companies/DANGCEM:NGX
/v1/companies/DANGCEM.LG
/v1/companies/DANGCEM%3ANGX
/v1/companies/DANGCEM
If you are ingesting symbols from an external data source that uses exchange-suffixed notation, strip the suffix before passing the symbol to the API:
function cleanSymbol(raw) {
return raw.split(/[:.\/]/)[0].toUpperCase();
}
cleanSymbol('DANGCEM:NGX')
cleanSymbol('GTCO.LG')
cleanSymbol('MTNN:NGX')
The same applies to index symbols. The correct format is NGX30, NGXASI, NGXPENSION — not NGX:30 or NSENG:NGX30.
Mistake 6: Polling outside market hours
The NGX trades Monday to Friday, 09:00 to 16:00 West Africa Time. Outside those hours, prices do not change. The snapshot, company prices, and market data all stay at the last session close until the next trading day opens.
If your app polls the API every few minutes around the clock, you are making hundreds of calls per day that return identical data to the previous call. On the free plan with 3,000 calls per month, this can exhaust your quota before the month is half over without your app having done anything useful with the data.
The fix is a simple market hours check before each poll:
function isMarketOpen() {
const now = new Date();
const day = now.getUTCDay();
const hour = now.getUTCHours();
return day >= 1 && day <= 5 && hour >= 8 && hour < 15;
}
const TWENTY_MINUTES = 20 * 60 * 1000;
setInterval(() => {
if (isMarketOpen()) {
fetchMarketData();
}
}, TWENTY_MINUTES);
You can also call /v1/market/status once on app load to get the live open/closed state rather than computing it yourself. Pair that with a check on /v1/market/available-dates to know whether today is a public holiday — the NGX closes on Nigerian public holidays and a day-of-week check alone will not catch those.
Mistake 7: Generating a new API key when the old one "stops working"
A key working fine yesterday and returning errors today is rarely a key problem. It is almost always one of these:
- Monthly quota has been reached (
QUOTA_EXCEEDED)
- A recent code change introduced a wrong endpoint path (
NOT_FOUND)
- The endpoint requires a higher plan than the key's account is on (
PLAN_REQUIRED)
Revoking and regenerating a key in response to errors does not fix any of these. The new key has the same plan and the same quota state as the old one. And if you have hardcoded the old key in multiple places, regenerating it means tracking down every place it was used and updating them.
Before regenerating a key, call /v1/account/usage to see your current quota state:
const res = await fetch('https://api.ngnmarket.com/v1/account/usage', {
headers: { 'Authorization': `Bearer ${API_KEY}` }
});
const body = await res.json();
console.log(`Plan: ${body.data.plan}`);
console.log(`Calls used: ${body.data.calls_used}`);
console.log(`Calls remaining: ${body.data.calls_remaining}`);
console.log(`Resets at: ${body.data.reset_at}`);
If calls_remaining is 0, the quota is exhausted and a new key will not help. If it is healthy, the problem is somewhere in your request, not your key.
Quick reference: correct endpoint paths
A summary of the paths that most commonly trip people up:
| What you want |
Wrong path |
Correct path |
| Single company price and profile |
/v1/stocks/DANGCEM |
/v1/companies/DANGCEM |
| All NGX companies |
/v1/stocks |
/v1/companies |
| Live forex rates |
/v1/forex/rates |
/v1/forex/current |
| Historical forex rates |
/v1/forex/history/USD |
/v1/forex/history?source=USD |
| Company price history |
/v1/stocks/DANGCEM/history |
/v1/companies/DANGCEM/chart |
| Market summary |
/v1/market |
/v1/market/snapshot |
| Top gainers/losers |
/v1/market/gainers |
/v1/market/movers |
The full list of available endpoints with request and response examples is in the API documentation.
Frequently asked questions
Do failed requests (404, 403) count against my monthly quota?
Yes. Every authenticated request that reaches the API counts against your quota, regardless of the response status. The only exceptions are RATE_LIMITED (429) and auth failures (INVALID_API_KEY, MISSING_API_KEY) — those are rejected before quota is incremented. This is why catching integration mistakes early matters — silent failures can drain quota quickly.
I am getting 403 on an endpoint I think I should have access to. What do I check first?
Read the response body. A PLAN_REQUIRED error will tell you exactly which plan the endpoint needs and which plan your account is currently on. If the plan looks right, check that the key you are using belongs to the correct account — it is easy to accidentally use a key from a test account against a production endpoint.
My integration was working yesterday and now everything is 429. What happened?
A sudden switch to all-429 responses means you have hit your monthly quota. Check meta.reset_at in the error response to see when your quota resets. If the reset date is weeks away and you have run out of calls unexpectedly, check your request logs for a polling loop that may have been running more frequently than intended, or a batch job that ran against a wrong endpoint repeatedly.
Can I get my quota back if I spent calls on 404 errors by mistake?
Contact support through the dashboard with the details of what happened. For cases where a clear integration error caused a large number of failed requests, the team reviews quota adjustments on a case-by-case basis.
If you are still stuck after working through this guide, the documentation has the full request and response schema for every endpoint. The getting started guide in particular covers authentication, the response envelope format, and how to read meta in detail.