Learn how to handle API responses and errors gracefully in your application.
Response Format
All API responses follow a consistent format:
Success Response
{
"status": {
"code": 200,
"message": "Success"
},
"data": {
}
}
Error Response
Errors carry a separate error object. status.code is the numeric HTTP
status; the machine-readable code is error.code. Match on error.code,
never on the message text.
{
"status": {
"code": 400,
"message": "Bad Request"
},
"error": {
"code": "VALIDATION_ERROR",
"message": "Please check your input and try again.",
"details": {}
}
}
The details object is included only when there are field-level details.
HTTP Status Codes
| Code | Meaning |
|---|
200 | Success |
201 | Created (for new resources) |
400 | Bad Request - Invalid parameters |
401 | Unauthorized - Invalid or missing API key |
403 | Forbidden - Insufficient permissions |
404 | Not Found - Resource doesn't exist |
429 | Too Many Requests - Rate limit exceeded |
500 | Server Error - Something went wrong on our end |
Error Codes Reference
Authentication Errors (401)
| Code | Description | Solution |
|---|
AUTHENTICATION_REQUIRED | No API key provided | Include Authorization: Bearer header |
INVALID_CREDENTIALS | API key is invalid | Check your API key is correct |
TOKEN_REVOKED | API key has been revoked | Generate a new API key |
TOKEN_EXPIRED | API key has expired | Generate a new API key |
Authorization Errors (403)
| Code | Description | Solution |
|---|
INSUFFICIENT_SCOPE | Missing required scope | Add the required scope to your API key |
API_DISABLED | API access is disabled | Enable API access in account settings |
IP_NOT_ALLOWED | IP address not whitelisted | Add your IP to the whitelist or remove restrictions |
ENVIRONMENT_MISMATCH | Wrong environment | Use sk_test_* for sandbox, sk_live_* for production |
Validation Errors (400)
| Code | Description | Solution |
|---|
VALIDATION_ERROR | General validation failure | Check the error message for details |
MISSING_PARAMETER | Required parameter missing | Include all required parameters |
INVALID_PARAMETER | Parameter format invalid | Check parameter format requirements |
INVALID_PHONE_NUMBER | Phone number format wrong | Use Nigerian format: 080, 081, 070, 090, 091 |
INVALID_AMOUNT | Amount outside allowed range | Check product min/max amounts |
DUPLICATE_REFERENCE | external_reference already used | Use a unique reference for each transaction |
INSUFFICIENT_BALANCE | Account balance too low | Fund your account before purchasing |
PRODUCT_UNAVAILABLE | Product or provider unavailable | Try a different product or retry later |
Meter and decoder verification failures return VALIDATION_ERROR with a
message describing the problem: there is no dedicated verification error
code. A purchase submitted before verifying, or with details that do not
match, also returns VALIDATION_ERROR (or PURCHASE_FAILED when the
purchase itself is rejected downstream).
Provider Errors (400)
| Code | Description | Solution |
|---|
PROVIDER_ERROR | Upstream provider error | Retry the request or contact support |
PURCHASE_FAILED | Purchase was rejected | Check the message, then retry |
Not Found Errors (404)
| Code | Description | Solution |
|---|
PRODUCT_NOT_FOUND | Product doesn't exist | Check the product ID |
TRANSACTION_NOT_FOUND | Transaction doesn't exist | Check the transaction ID |
PURCHASE_NOT_FOUND | Purchase doesn't exist | Check the purchase/transaction ID |
Status Errors (400)
| Code | Description | Solution |
|---|
INVALID_STATUS | Transaction status doesn't allow this action | Only pending transactions can be requeried |
Rate Limit Errors (429)
| Code | Description | Solution |
|---|
RATE_LIMIT_EXCEEDED | Too many requests | Wait and retry, check Retry-After header |
Handling Errors in Code
function generateReference() {
const timestamp = Math.floor(Date.now() / 1000);
const chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let suffix = "";
for (let i = 0; i < 6; i++) {
suffix += chars.charAt(Math.floor(Math.random() * chars.length));
}
return `${timestamp}${suffix}`;
}
async function makePurchase(productId, phoneNumber, amount) {
try {
const response = await fetch(
"https://my.rizpay.app/api/partners/v1/purchases",
{
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
product_id: productId,
phone_number: phoneNumber,
amount: amount,
external_reference: generateReference(),
}),
}
);
const data = await response.json();
if (!response.ok) {
switch (data.error?.code) {
case "INSUFFICIENT_BALANCE":
throw new Error("Please fund your account");
case "INVALID_PHONE_NUMBER":
throw new Error("Please enter a valid phone number");
case "DUPLICATE_REFERENCE":
throw new Error("This transaction was already processed");
case "RATE_LIMIT_EXCEEDED":
const retryAfter = response.headers.get("Retry-After");
throw new Error(
`Too many requests. Retry after ${retryAfter} seconds`
);
default:
throw new Error(data.error?.message || "Request failed");
}
}
return data.data;
} catch (error) {
console.error("Purchase failed:", error.message);
throw error;
}
}
Best Practices
- Always check the status code - Don't assume success
- Log error responses - Include the full response for debugging
- Handle rate limits gracefully - Use the
Retry-After header
- Use unique references - Prevent duplicate transaction errors
- Verify before purchase - For electricity and cable TV, always verify first
Next Steps