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

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.

json
{
  "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

CodeMeaning
200Success
201Created (for new resources)
400Bad Request - Invalid parameters
401Unauthorized - Invalid or missing API key
403Forbidden - Insufficient permissions
404Not Found - Resource doesn't exist
429Too Many Requests - Rate limit exceeded
500Server Error - Something went wrong on our end

Error Codes Reference

Authentication Errors (401)

CodeDescriptionSolution
AUTHENTICATION_REQUIREDNo API key providedInclude Authorization: Bearer header
INVALID_CREDENTIALSAPI key is invalidCheck your API key is correct
TOKEN_REVOKEDAPI key has been revokedGenerate a new API key
TOKEN_EXPIREDAPI key has expiredGenerate a new API key

Authorization Errors (403)

CodeDescriptionSolution
INSUFFICIENT_SCOPEMissing required scopeAdd the required scope to your API key
API_DISABLEDAPI access is disabledEnable API access in account settings
IP_NOT_ALLOWEDIP address not whitelistedAdd your IP to the whitelist or remove restrictions
ENVIRONMENT_MISMATCHWrong environmentUse sk_test_* for sandbox, sk_live_* for production

Validation Errors (400)

CodeDescriptionSolution
VALIDATION_ERRORGeneral validation failureCheck the error message for details
MISSING_PARAMETERRequired parameter missingInclude all required parameters
INVALID_PARAMETERParameter format invalidCheck parameter format requirements
INVALID_PHONE_NUMBERPhone number format wrongUse Nigerian format: 080, 081, 070, 090, 091
INVALID_AMOUNTAmount outside allowed rangeCheck product min/max amounts
DUPLICATE_REFERENCEexternal_reference already usedUse a unique reference for each transaction
INSUFFICIENT_BALANCEAccount balance too lowFund your account before purchasing
PRODUCT_UNAVAILABLEProduct or provider unavailableTry 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)

CodeDescriptionSolution
PROVIDER_ERRORUpstream provider errorRetry the request or contact support
PURCHASE_FAILEDPurchase was rejectedCheck the message, then retry

Not Found Errors (404)

CodeDescriptionSolution
PRODUCT_NOT_FOUNDProduct doesn't existCheck the product ID
TRANSACTION_NOT_FOUNDTransaction doesn't existCheck the transaction ID
PURCHASE_NOT_FOUNDPurchase doesn't existCheck the purchase/transaction ID

Status Errors (400)

CodeDescriptionSolution
INVALID_STATUSTransaction status doesn't allow this actionOnly pending transactions can be requeried

Rate Limit Errors (429)

CodeDescriptionSolution
RATE_LIMIT_EXCEEDEDToo many requestsWait 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) {
      // Match on error.code (status.code is the numeric HTTP status)
      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

  1. Always check the status code - Don't assume success
  2. Log error responses - Include the full response for debugging
  3. Handle rate limits gracefully - Use the Retry-After header
  4. Use unique references - Prevent duplicate transaction errors
  5. Verify before purchase - For electricity and cable TV, always verify first

Next Steps