Error Handling
Understand API response formats and error codes
Learn how to handle API responses and errors gracefully in your application.
Response Format
All API responses follow a consistent format:
Success Response
json
{
"status": {
"code": 200,
"message": "Success"
},
"data": {
// Response data here
}
}
Error Response
json
{
"status": {
"code": "ERROR_CODE",
"message": "Human-readable error description"
},
"data": null
}
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 is not available | Try a different product or retry later |
VERIFICATION_REQUIRED | Must verify meter/decoder first | Call /purchases/verify before purchasing |
VERIFICATION_FAILED | Meter/decoder verification failed | Check the meter/decoder number |
Provider Errors (400)
| Code | Description | Solution |
|---|---|---|
PROVIDER_ERROR | Upstream provider error | Retry the request or contact support |
PROVIDER_TIMEOUT | Provider request timed out | Retry the request |
PROVIDER_UNAVAILABLE | Provider service is down | Retry later |
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
javascript
// Generate external reference: 10-digit timestamp + 6 alphanumeric
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) {
// Handle specific error codes
switch (data.status.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.status.message);
}
}
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-Afterheader - Use unique references - Prevent duplicate transaction errors
- Verify before purchase - For electricity and cable TV, always verify first
Next Steps
- Rate Limiting - Understand rate limits
- Duplicate Prevention - Prevent duplicate transactions
