---
title: Error Handling
description: 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

| 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

```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

- [Rate Limiting](/docs/core-concepts/rate-limiting) - Understand rate limits
- [Duplicate Prevention](/docs/core-concepts/duplicate-prevention) - Prevent duplicate transactions