Errors

The Ping API uses conventional HTTP status codes and a consistent error response format to indicate success or failure. Understanding these patterns will help you debug issues quickly.


Error response format

All Ping API errors follow a consistent JSON structure. Every error response includes:

  • result - Always "failed" for errors
  • message - Human-readable error description
  • code - Optional machine-readable error code
  • error_info - Optional additional details

This consistent format makes it easy to handle errors programmatically across all endpoints.

Error response envelope

{
  "result": "failed",
  "message": "Invalid phone number format. Use E.164 format (e.g., +263771234567)",
  "code": "INVALID_PHONE_FORMAT",
  "error_info": "Phone number must start with country code"
}

Minimal error

{
  "result": "failed",
  "message": "Missing required field: message"
}

Status codes

The Ping API uses standard HTTP status codes to indicate the outcome of requests.

Success codes

  • Name
    200 OK
    Description

    Request succeeded. Response body contains the requested data.

  • Name
    201 Created
    Description

    Resource successfully created (e.g., new recipient, template).

Client error codes

  • Name
    400 Bad Request
    Description

    Invalid request parameters or missing required fields.

  • Name
    401 Unauthorized
    Description

    Missing or invalid authentication credentials (API key or JWT token).

  • Name
    403 Forbidden
    Description

    Valid credentials but insufficient permissions for the requested operation.

  • Name
    404 Not Found
    Description

    The requested resource does not exist.

  • Name
    409 Conflict
    Description

    Request conflicts with existing state (e.g., duplicate resource).

  • Name
    429 Too Many Requests
    Description

    Rate limit exceeded. See rate limiting documentation.

Server error codes

  • Name
    500 Internal Server Error
    Description

    Something went wrong on Ping's end. Contact support if this persists.


Common errors

Authentication errors

401 Unauthorized

  • Missing X-Ping-Api-Key header
  • Invalid or expired API key
  • Missing Authorization header for JWT endpoints

403 Forbidden

  • API key lacks required permission (e.g., sms permission for SMS endpoint)
  • User not a member of the target business
  • JWT token valid but user lacks access to resource

Validation errors

400 Bad Request

  • Missing required fields (to_phone, message, etc.)
  • Invalid phone number format (must be E.164: +263771234567)
  • Invalid email address format
  • Template not found or not active
  • Sender ID not approved

Rate limiting

429 Too Many Requests

  • Authentication endpoints: 5 requests per minute
  • Exceeded other endpoint-specific rate limits
  • Response includes Retry-After header when available

Business errors

Insufficient credits

{
  "result": "failed",
  "message": "Insufficient credits. Required: $0.50, Available: $0.20"
}

Unverified business

{
  "result": "failed",
  "message": "Business verification required. Max 5 recipients for unverified businesses."
}

Example error handling

Python

import requests

response = requests.post(
    'https://api.ping.co.zw/v1/notification/api/sms/send',
    headers={'X-Ping-Api-Key': api_key},
    json={'to_phone': phone, 'message': msg}
)

if response.status_code == 200:
    data = response.json()
    if data['result'] == 'success':
        print('SMS sent successfully')
    else:
        print(f"Error: {data['message']}")
elif response.status_code == 401:
    print('Invalid API key')
elif response.status_code == 429:
    retry_after = response.headers.get('Retry-After')
    print(f'Rate limited. Retry after {retry_after}s')
else:
    print(f'HTTP {response.status_code}')

Node.js

const response = await fetch(url, options)

if (!response.ok) {
  if (response.status === 401) {
    throw new Error('Invalid credentials')
  }
  if (response.status === 429) {
    const retryAfter = response.headers.get('Retry-After')
    throw new Error(`Rate limited. Retry after ${retryAfter}s`)
  }
}

const data = await response.json()
if (data.result === 'failed') {
  throw new Error(data.message)
}

Best practices

Handle errors gracefully

  1. Check HTTP status code first - Don't assume 200 means success
  2. Parse the error message - Contains actionable details
  3. Implement retry logic - With exponential backoff for 429 errors
  4. Log error details - Include code and error_info for debugging
  5. Display user-friendly messages - Don't show raw API errors to end users

Retry strategy

For transient errors (500, 429), implement exponential backoff:

  • First retry: Wait 1 second
  • Second retry: Wait 2 seconds
  • Third retry: Wait 4 seconds
  • Max retries: 3-5 attempts

For 429 errors, respect the Retry-After header if present.

Was this page helpful?