Skip to main content
Every API has limits. Limits protect every partner on the platform — including you — from one application monopolising shared infrastructure. Understanding how our limits work prevents surprises in production.

Two Types of Limits

Rate Limits

The maximum number of requests allowed within a short time window (seconds or minutes). Rate limits prevent burst abuse — for example, a loop in your code that fires 500 requests in one second.

Quotas

The maximum number of requests allowed within your billing period (typically one month). Quotas are determined by your partner plan. Hitting a quota means you have used your allocation for the period, not just a short burst.
A rate limit violation returns HTTP 429 Too Many Requests. A quota exhaustion returns HTTP 402 Payment Required. Both include headers that tell you exactly what happened and when you can retry.

Rate Limits by Endpoint

These limits apply per API key unless otherwise noted. “Per IP” limits apply per source IP address regardless of key.
EndpointLimitWindowBurst
POST /api/Auth/register10 requests1 minute per IP3 / second
POST /api/Auth/verifyEmailCode5 requests15 minutes per IP2 / second
POST /api/v1/accounts60 requests1 minute per key10 / second
POST /api/v1/payments/transfer30 requests1 minute per key5 / second
POST /api/v1/payments/account-enquiry60 requests1 minute per key10 / second
POST /api/v1/kyc/bvn/verify100 requests1 hour per key10 / second
POST /api/v1/kyc/nin/verify100 requests1 hour per key10 / second
POST /api/v1/kyc/cac/lookup200 requests1 hour per key20 / second
GET /api/v1/accounts/*120 requests1 minute per key20 / second
Burst is the maximum number of requests per second within the window. Even if you have quota remaining, sending requests faster than the burst rate will trigger a 429.
The /api/Auth/ endpoints use IP-based limits, not key-based limits, because they are called before you have authenticated. If you have multiple services behind a single NAT IP, they share this limit.

Response Headers to Monitor

Every API response includes headers that tell you where you stand. Read these in your application — do not rely on hitting a 429 to detect a limit.
HeaderWhat It Means
X-Quota-RemainingThe number of calls left in your current billing period. When this reaches zero, you will receive 402 responses.
X-Quota-ResetThe date and time when your quota resets, in WAT (West Africa Time, UTC+1). Format: ISO 8601.
X-RateLimit-RemainingRequests remaining in the current rate limit window.
X-RateLimit-ResetWhen the current rate limit window resets (Unix timestamp).
Retry-AfterPresent only on 429 responses. The number of seconds you must wait before retrying.
Log X-Quota-Remaining in your application metrics. Set an alert when it falls below 20% so you have time to contact us before you are completely blocked.

What to Do When Rate Limited (HTTP 429)

A 429 response means you sent requests too fast. The right response is exponential backoff with jitter: wait a little, retry, wait a little longer if it fails again, and so on. Do not retry immediately in a tight loop — that makes the problem worse.
async function callWithRetry(requestFn, maxRetries = 5) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      const response = await requestFn();

      if (response.status === 429) {
        const retryAfter = parseInt(response.headers.get("Retry-After") || "1", 10);
        // Exponential backoff: 1s, 2s, 4s, 8s, 16s — plus random jitter
        const jitter = Math.random() * 1000; // up to 1 extra second
        const delay = Math.min(retryAfter * 1000, (2 ** attempt) * 1000) + jitter;

        console.warn(`Rate limited. Retrying in ${Math.round(delay / 1000)}s (attempt ${attempt + 1}/${maxRetries})`);
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }

      return response;
    } catch (err) {
      if (attempt === maxRetries) throw err;
    }
  }
}

// Usage
const response = await callWithRetry(() =>
  fetch("https://sandbox.api.unionbank.ng/api/v1/payments/transfer", {
    method: "POST",
    headers: {
      "Authorization": `ApiKey ${process.env.UBN_API_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify(payload)
  })
);
The jitter (random extra delay) is important. Without it, all your retry attempts fire at the same moment, creating a “thundering herd” that immediately triggers another rate limit. Jitter spreads the retries out in time.

What to Do When Quota Is Exhausted (HTTP 402)

A 402 Payment Required response means you have used all the requests in your plan for this billing period. Unlike a rate limit, waiting and retrying will not help — you need to either wait for the reset date (shown in X-Quota-Reset) or upgrade your plan. To discuss your current quota, upgrade your plan, or request a temporary increase for a high-volume event (such as a product launch), email baas-support@unionbank.ng with:
  • Your Partner ID
  • Your current plan
  • The volume you expect and the timeframe
Give us at least 5 business days’ notice for quota increases. Same-day increases are not guaranteed.

Sandbox vs Production Limits

Sandbox has the same rate limits as production. This is intentional. If sandbox had no limits, you would build and test your application without backoff logic — and then hit production limits immediately on launch. The sandbox limits ensure that your retry and backoff logic is tested and working before you go live.
Test your backoff logic explicitly in the sandbox. Write a test that sends requests in a tight loop until it gets a 429, then verify that your application handles it gracefully and recovers.