GET/api/v1/sms-api/messages/{message_id}/

Get Message Status

Check the delivery status of a specific SMS message using its unique message ID. Track delivery progress, timestamps, and provider responses.

Authentication Required

This endpoint requires a Bearer token. Get your API key from your dashboard.

Message Status Lifecycle

pending

Message queued for delivery, awaiting processing

sent

Message submitted to carrier, delivery in progress

delivered

Message successfully delivered to recipient

failed

Delivery failed (invalid number, network issue, etc.)

rejected

Message rejected by carrier (blocked number, spam filter, etc.)

Request Parameters

ParameterTypeRequiredDescription
message_iduuidRequiredThe unique message ID returned when SMS was sent (path parameter)
Example: f2672215-c20b-452a-9b6b-1ba51884c55e

Note: The message_id is included in the URL path, not as a query parameter. You receive this ID when you send an SMS.

Code Examples

import requests
from typing import Optional

# Configuration
# Get your API key from: https://roycetalk.com
# Format: XXXX_XXXXXXXXXXXXXXXXXXXXXXXX_
API_BASE_URL = "https://roycetalk.com/api/v1/sms-api/messages"
BEARER_TOKEN = "XXXX_XXXXXXXXXXXXXXXXXXXXXXXX_"

def get_message_status(message_id: str) -> Optional[dict]:
    """
    Get delivery status of a specific SMS message
    
    Args:
        message_id: The unique message ID returned when SMS was sent
        
    Returns:
        dict: API response with message status, or None on failure
    """
    
    # Construct URL with message ID
    url = f"{API_BASE_URL}/{message_id}/"
    
    # Request headers with Bearer token
    headers = {
        "Authorization": f"Bearer {BEARER_TOKEN}",
    }
    
    try:
        # Make GET request
        response = requests.get(url, headers=headers)
        
        # Check if request was successful
        if response.status_code == 200:
            result = response.json()
            message = result['data']
            
            print("✓ Message status retrieved!")
            print(f"Message ID: {message['id']}")
            print(f"Recipient: {message['recipient_phone']}")
            print(f"Status: {message['status_display']}")
            print(f"Sent at: {message['sent_at']}")
            
            if message['delivered_at']:
                print(f"Delivered at: {message['delivered_at']}")
            
            print(f"SMS Units: {message['sms_units_consumed']}")
            
            return result
            
        elif response.status_code == 404:
            error = response.json()
            print(f"✗ Message Not Found: {error.get('message', 'Invalid message ID')}")
            return None
            
        elif response.status_code == 403:
            error = response.json()
            print(f"✗ Authentication Error: {error['error']['message']}")
            return None
            
        else:
            print(f"✗ Unexpected Error: {response.status_code}")
            print(response.text)
            return None
            
    except requests.exceptions.RequestException as e:
        print(f"✗ Request failed: {str(e)}")
        return None

def wait_for_delivery(message_id: str, max_attempts: int = 10, interval: int = 5):
    """
    Poll message status until delivered or failed
    
    Args:
        message_id: The unique message ID
        max_attempts: Maximum number of status checks
        interval: Seconds to wait between checks
    """
    import time
    
    for attempt in range(max_attempts):
        print(f"\nCheck {attempt + 1}/{max_attempts}...")
        status_info = get_message_status(message_id)
        
        if not status_info:
            break
            
        status = status_info['data']['status']
        
        if status in ['delivered', 'failed', 'rejected']:
            print(f"\n✓ Final status: {status}")
            return status_info
        
        if attempt < max_attempts - 1:
            print(f"Status: {status} - waiting {interval}s...")
            time.sleep(interval)
    
    print("\n✗ Max attempts reached")
    return None

# Example usage
if __name__ == "__main__":
    # Check specific message
    message_id = "f2672215-c20b-452a-9b6b-1ba51884c55e"
    status = get_message_status(message_id)
    
    # Or wait for delivery
    # wait_for_delivery(message_id)

Response Examples

{
  "success": true,
  "data": {
    "id": "f2672215-c20b-452a-9b6b-1ba51884c55e",
    "recipient_phone": "+254712345678",
    "message_text": "Hello from RoyceBulkSMS API",
    "sender_id": "RoyceLTD",
    "status": "delivered",
    "status_display": "Delivered",
    "sms_units_consumed": 1,
    "created_at": "2025-11-28T13:49:13.265444+00:00",
    "sent_at": "2025-11-28T13:49:15.123456+00:00",
    "delivered_at": "2025-11-28T13:49:20.789012+00:00",
    "provider_message_id": "8290842",
    "provider_response_description": "Success",
    "metadata": {
      "callback_url": "https://yourapp.com/webhooks/sms",
      "client_ref": "order-12345"
    }
  },
  "message": "Message status retrieved successfully",
  "meta": {
    "timestamp": "2025-11-28T14:30:00.000000+00:00",
    "api_version": "v1"
  },
  "errors": []
}

Response Fields

status

Current delivery status: pending, sent, delivered, failed, or rejected

created_at

When the message was created/queued in our system

sent_at

When the message was submitted to the carrier (null if still pending)

delivered_at

When the message was delivered to recipient (null if not yet delivered)

provider_message_id

Carrier's unique ID for this message (for carrier support inquiries)

metadata

Your custom data (callback_url, client_ref) provided when sending

Common Use Cases

Order confirmation tracking
OTP delivery verification
Campaign delivery monitoring
Failed message investigation
Delivery time analytics
Customer support queries

Best Practices

Use Webhooks for Real-Time Updates

Instead of polling this endpoint, provide a callback_url when sending SMS. You'll receive automatic delivery updates via webhook, which is more efficient.

Store Message IDs

Always store the message_id returned when sending SMS. This is your only way to check status later. Link it to your order/user IDs.

Handle Different Statuses

Delivery takes 5-30 seconds typically. If status is "pending" or "sent", check again after a few seconds. If "failed" or "rejected", review the error description.

Don't Poll Excessively

Avoid checking status every second. Use exponential backoff: check after 5s, 10s, 30s, 60s. Better yet, use webhooks to avoid polling entirely.

Tracking Multiple Messages

For bulk sends, use these endpoints instead of checking each message individually:

  • GET /batches/{batch_id}/Get status summary for entire batch
  • GET /messages/?batch_id=...List all messages in a batch

Rate Limiting

Status checks count toward your API rate limits. For high-volume applications, use webhooks instead of polling to avoid hitting rate limits.