Errors and limits
Error JSON and rate limits that the service actually returns.
Rate Limit Rules & Quotas
Global API Limit: 300 requests per 15 minutes per IP address across all standard endpoints.
Payment Processing Routes (/payment/*): 15 requests per minute per IP address to safeguard upstream telecom networks.
Authentication Routes (/auth/login, /auth/verify-otp, /auth/refresh): 10 requests per 15 minutes per IP address to prevent brute-force attempts.
When quota is exceeded, the server returns HTTP 429 with JSON body: { "status": "fail", "message": "Too many requests from this IP, please try again later" }.
Common Integration Mistakes Checklist
1. Forgetting Bearer prefix: Always send 'Authorization: Bearer <token>' with a space.
2. Payer is merchant: Calling the MTN collect routes with the merchant's own registered phone number will be rejected with HTTP 400.
3. Missing X-Requested-With: When using browser cookies instead of the Authorization header, you must send 'X-Requested-With: XMLHttpRequest'.
4. Rapid polling: Polling payment status faster than once every 3-5 seconds will trigger the 15 req/min rate limit (HTTP 429).
5. Unverified KYC for Orders: Creating card/bank orders requires a VERIFIED merchant account. Complete KYC in the dashboard first.
6. Waiting for outbound merchant notifications: Status resolution uses the status API (polling). Do not wait for inbound merchant callbacks.
Auth middleware and many handlers use { status: "fail" or "error", message }. status is fail for 4xx and error for 5xx. HTTP 500 messages from this handler are "Internal server error".
MTN controllers often return { error: message } instead, including 401 { error: "User not authenticated" } and 404 { error: "Payment not found" }.
TransactPay handlers return { message }. Airtel validation returns { message }, and Airtel failures return { error: "Error processing Airtel Money payment" }.
There is no error.code field. Do not expect a request id.
Status codes
| HTTP Status | Error Type / Code | Meaning | Suggested Fix |
|---|---|---|---|
| 400 | validation_error / duplicate_value | Validation failed or duplicate value submitted. Message details the first Zod schema issue. | Verify payload format against endpoint parameter specifications and ensure unique fields (e.g., email) do not collide. |
| 401 | unauthorized | Missing or invalid session, expired JWT bearer token, or invalid API key/secret. | Include a valid Authorization: Bearer <token> header or valid x-api-key and x-api-secret headers. |
| 403 | forbidden | A cookie-authenticated mutation did not send X-Requested-With, or order creation was called before merchant KYC is verified. | Send X-Requested-With: XMLHttpRequest when using cookies, or verify merchant KYC in dashboard. |
| 404 | not_found | Resource not found, including missing payment, unknown order, or non-existent recipient. | Verify the reference ID or resource identifier matches an existing record under your account. |
| 429 | rate_limit_exceeded | IP rate limit exceeded. Global: 300 req/15min. Payment routes: 15 req/min. Auth routes: 10 req/15min. | Implement exponential backoff and adhere to recommended 3-5 second polling intervals. |
| 500 | internal_server_error | Unexpected internal server error or unhandled failure. | Retry request with exponential backoff. If persistent, contact developer support with timestamps. |
| 503 | service_unavailable | Upstream telecom provider (MTN/Airtel) or payment processor gateway failed to respond. | Wait and retry after a short delay. Check network status of upstream provider. |
Notes
- Not every status above is returned by every route. Check the specific endpoint page for route-specific status mappings.