Python / Django Integration

Complete guide to integrate NestSMS into your Python and Django applications with practical examples for sending SMS, handling responses, and tracking delivery status.

Installation

Install the requests library:

pip install requests python-dotenv

Setup

Create a .env file:

# .env
NESTSMS_API_KEY=nsms_live_your_api_key_here

Python Examples

Send SMS

# send_sms.py
import os
import requests
from dotenv import load_dotenv

load_dotenv()

API_KEY = os.getenv('NESTSMS_API_KEY')
BASE_URL = 'https://api.nestsms.com/api/v1'

def send_sms(phone_number, message, sender_name='YourBrand'):
    """Send a single SMS"""
    url = f'{BASE_URL}/sms/send'

    headers = {
        'X-API-Key': API_KEY,
        'Content-Type': 'application/json'
    }

    payload = {
        'to': phone_number,
        'message': message,
        'sender_id': sender_name,
        'message_type': 'transactional'
    }

    try:
        response = requests.post(url, json=payload, headers=headers)
        response.raise_for_status()

        data = response.json()
        print('✓ SMS sent successfully!')
        print(f'Job ID: {data["job_id"]}')
        print(f'Cost: {data["cost"]}')

        return data

    except requests.exceptions.HTTPError as e:
        error_data = e.response.json()
        print(f'✗ Error: {error_data.get("error")}')
        print(f'Code: {error_data.get("code")}')
        raise
    except requests.exceptions.RequestException as e:
        print(f'✗ Request failed: {str(e)}')
        raise

# Usage
if __name__ == '__main__':
    send_sms('9840123456', 'Hello from NestSMS API!')

Send Bulk SMS

# send_bulk_sms.py
def send_bulk_sms(recipients, message, sender_name='YourBrand'):
    """Send bulk SMS to multiple recipients"""
    url = f'{BASE_URL}/sms/send'

    if not isinstance(recipients, list) or len(recipients) > 1000:
        raise ValueError('Recipients must be a list (max 1000)')

    headers = {
        'X-API-Key': API_KEY,
        'Content-Type': 'application/json'
    }

    payload = {
        'to': recipients,
        'message': message,
        'sender_id': sender_name,
        'message_type': 'promotional'
    }

    try:
        response = requests.post(url, json=payload, headers=headers)
        response.raise_for_status()

        data = response.json()
        print(f'✓ Bulk SMS sent to {data["recipients_count"]} recipients')
        print(f'Total cost: {data["cost"]}')

        return data

    except requests.exceptions.HTTPError as e:
        error_data = e.response.json()
        print(f'✗ Error: {error_data.get("error")}')
        raise

# Usage
recipients = ['9840123456', '9851234567', '9861345678']
send_bulk_sms(recipients, 'Special offer! Get 50% off today.')

Check SMS Status

# check_status.py
def check_sms_status(message_id):
    """Check delivery status of an SMS"""
    url = f'{BASE_URL}/sms/status/{message_id}'

    headers = {
        'X-API-Key': API_KEY
    }

    try:
        response = requests.get(url, headers=headers)
        response.raise_for_status()

        data = response.json()
        print(f'Status: {data["status"]}')
        print(f'Sent at: {data["sent_at"]}')
        print(f'Delivered at: {data.get("delivered_at", "N/A")}')

        return data

    except requests.exceptions.HTTPError as e:
        error_data = e.response.json()
        print(f'✗ Error: {error_data.get("error")}')
        raise

# Usage
check_sms_status('550e8400-e29b-41d4-a716-446655440000')

Get Account Balance

# get_balance.py
def get_balance():
    """Get account balance and usage stats"""
    url = f'{BASE_URL}/sms/balance'

    headers = {
        'X-API-Key': API_KEY
    }

    try:
        response = requests.get(url, headers=headers)
        response.raise_for_status()

        data = response.json()
        print(f'Balance: {data["balance"]} {data["currency"]}')
        print(f'SMS sent today: {data["sms_sent_today"]}')
        print(f'SMS sent this month: {data["sms_sent_this_month"]}')

        return data

    except requests.exceptions.HTTPError as e:
        error_data = e.response.json()
        print(f'✗ Error: {error_data.get("error")}')
        raise

# Usage
get_balance()

Complete Python Client

# nestsms_client.py
import requests
from typing import List, Dict, Optional

class NestSMSClient:
    """Complete NestSMS API client"""

    def __init__(self, api_key: str, base_url: str = 'https://api.nestsms.com/api/v1'):
        self.api_key = api_key
        self.base_url = base_url
        self.session = requests.Session()
        self.session.headers.update({
            'X-API-Key': api_key,
            'Content-Type': 'application/json'
        })

    def send_sms(
        self,
        to: str,
        message: str,
        sender_id: str = 'YourBrand',
        message_type: str = 'transactional',
        encoding: str = 'text'
    ) -> Dict:
        """Send a single SMS"""
        url = f'{self.base_url}/sms/send'

        payload = {
            'to': to,
            'message': message,
            'sender_id': sender_id,
            'message_type': message_type,
            'type': encoding
        }

        return self._make_request('POST', url, json=payload)

    def send_bulk_sms(
        self,
        recipients: List[str],
        message: str,
        sender_id: str = 'YourBrand',
        message_type: str = 'promotional'
    ) -> Dict:
        """Send bulk SMS to multiple recipients"""
        if not isinstance(recipients, list) or len(recipients) > 1000:
            raise ValueError('Recipients must be a list (max 1000)')

        url = f'{self.base_url}/sms/send'

        payload = {
            'to': recipients,
            'message': message,
            'sender_id': sender_id,
            'message_type': message_type
        }

        return self._make_request('POST', url, json=payload)

    def check_status(self, message_id: str) -> Dict:
        """Check delivery status of an SMS"""
        url = f'{self.base_url}/sms/status/{message_id}'
        return self._make_request('GET', url)

    def get_balance(self) -> Dict:
        """Get account balance and usage stats"""
        url = f'{self.base_url}/sms/balance'
        return self._make_request('GET', url)

    def _make_request(self, method: str, url: str, **kwargs) -> Dict:
        """Internal method to make HTTP requests"""
        try:
            response = self.session.request(method, url, **kwargs)
            response.raise_for_status()
            return response.json()

        except requests.exceptions.HTTPError as e:
            error_data = e.response.json()
            error_msg = f"API Error ({e.response.status_code}): {error_data.get('error')} [{error_data.get('code')}]"
            raise Exception(error_msg)

        except requests.exceptions.RequestException as e:
            raise Exception(f'Network error: {str(e)}')

# Usage Example
if __name__ == '__main__':
    client = NestSMSClient(os.getenv('NESTSMS_API_KEY'))

    # Send SMS
    result = client.send_sms('9840123456', 'Hello from NestSMS!')
    print(result)

    # Check balance
    balance = client.get_balance()
    print(balance)

Django Integration

Django Settings

# settings.py
import os
from dotenv import load_dotenv

load_dotenv()

# NestSMS Configuration
NESTSMS_API_KEY = os.getenv('NESTSMS_API_KEY')
NESTSMS_BASE_URL = 'https://api.nestsms.com/api/v1'
NESTSMS_SENDER_ID = 'YourBrand'

Django Service Class

# sms/services.py
from django.conf import settings
import requests
from typing import List, Dict

class SMSService:
    """Django SMS service for NestSMS"""

    def __init__(self):
        self.api_key = settings.NESTSMS_API_KEY
        self.base_url = settings.NESTSMS_BASE_URL
        self.sender_id = settings.NESTSMS_SENDER_ID

    def send_sms(self, phone_number: str, message: str, message_type: str = 'transactional') -> Dict:
        """Send a single SMS"""
        url = f'{self.base_url}/sms/send'

        headers = {
            'X-API-Key': self.api_key,
            'Content-Type': 'application/json'
        }

        payload = {
            'to': phone_number,
            'message': message,
            'sender_id': self.sender_id,
            'message_type': message_type
        }

        try:
            response = requests.post(url, json=payload, headers=headers)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            # Log error
            raise Exception(f'Failed to send SMS: {str(e)}')

    def send_bulk_sms(self, recipients: List[str], message: str) -> Dict:
        """Send bulk SMS"""
        url = f'{self.base_url}/sms/send'

        headers = {
            'X-API-Key': self.api_key,
            'Content-Type': 'application/json'
        }

        payload = {
            'to': recipients,
            'message': message,
            'sender_id': self.sender_id,
            'message_type': 'promotional'
        }

        try:
            response = requests.post(url, json=payload, headers=headers)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            raise Exception(f'Failed to send bulk SMS: {str(e)}')

    def check_status(self, message_id: str) -> Dict:
        """Check SMS delivery status"""
        url = f'{self.base_url}/sms/status/{message_id}'

        headers = {
            'X-API-Key': self.api_key
        }

        try:
            response = requests.get(url, headers=headers)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            raise Exception(f'Failed to check status: {str(e)}')

Django Views

# sms/views.py
from django.http import JsonResponse
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from .services import SMSService
import json

@method_decorator(csrf_exempt, name='dispatch')
class SendSMSView(View):
    """API endpoint to send SMS"""

    def post(self, request):
        try:
            data = json.loads(request.body)
            phone_number = data.get('to')
            message = data.get('message')

            if not phone_number or not message:
                return JsonResponse({
                    'success': False,
                    'error': 'Missing required fields'
                }, status=400)

            sms_service = SMSService()
            result = sms_service.send_sms(phone_number, message)

            return JsonResponse({
                'success': True,
                'job_id': result['job_id'],
                'cost': result['cost']
            })

        except Exception as e:
            return JsonResponse({
                'success': False,
                'error': str(e)
            }, status=500)

@method_decorator(csrf_exempt, name='dispatch')
class BulkSMSView(View):
    """API endpoint to send bulk SMS"""

    def post(self, request):
        try:
            data = json.loads(request.body)
            recipients = data.get('recipients', [])
            message = data.get('message')

            if not recipients or not message:
                return JsonResponse({
                    'success': False,
                    'error': 'Missing required fields'
                }, status=400)

            sms_service = SMSService()
            result = sms_service.send_bulk_sms(recipients, message)

            return JsonResponse({
                'success': True,
                'job_id': result['job_id'],
                'recipients_count': result['recipients_count'],
                'cost': result['cost']
            })

        except Exception as e:
            return JsonResponse({
                'success': False,
                'error': str(e)
            }, status=500)

class SMSStatusView(View):
    """API endpoint to check SMS status"""

    def get(self, request, message_id):
        try:
            sms_service = SMSService()
            result = sms_service.check_status(message_id)

            return JsonResponse({
                'success': True,
                'status': result['status'],
                'sent_at': result['sent_at'],
                'delivered_at': result.get('delivered_at')
            })

        except Exception as e:
            return JsonResponse({
                'success': False,
                'error': str(e)
            }, status=500)

Django URLs

# sms/urls.py
from django.urls import path
from .views import SendSMSView, BulkSMSView, SMSStatusView

urlpatterns = [
    path('send/', SendSMSView.as_view(), name='send_sms'),
    path('bulk/', BulkSMSView.as_view(), name='bulk_sms'),
    path('status/<str:message_id>/', SMSStatusView.as_view(), name='sms_status'),
]

# In main urls.py
# from django.urls import path, include
# urlpatterns = [
#     path('api/sms/', include('sms.urls')),
# ]

OTP Verification in Django

OTP Service

# sms/otp_service.py
import random
from django.core.cache import cache
from .services import SMSService

class OTPService:
    """OTP verification service"""

    def __init__(self):
        self.sms_service = SMSService()
        self.otp_expiry = 300  # 5 minutes in seconds

    def generate_otp(self) -> str:
        """Generate 6-digit OTP"""
        return str(random.randint(100000, 999999))

    def send_otp(self, phone_number: str) -> Dict:
        """Send OTP to phone number"""
        otp = self.generate_otp()

        # Store OTP in cache
        cache_key = f'otp_{phone_number}'
        cache.set(cache_key, otp, self.otp_expiry)

        # Send SMS
        message = f'Your verification code is {otp}. Valid for 5 minutes.'
        result = self.sms_service.send_sms(
            phone_number,
            message,
            message_type='otp'
        )

        return {
            'success': True,
            'job_id': result['job_id']
        }

    def verify_otp(self, phone_number: str, entered_otp: str) -> Dict:
        """Verify OTP"""
        cache_key = f'otp_{phone_number}'
        stored_otp = cache.get(cache_key)

        if not stored_otp:
            return {
                'success': False,
                'error': 'OTP expired or not found'
            }

        if stored_otp != entered_otp:
            return {
                'success': False,
                'error': 'Invalid OTP'
            }

        # Clear OTP after successful verification
        cache.delete(cache_key)

        return {
            'success': True,
            'message': 'OTP verified successfully'
        }

OTP Views

# sms/views.py (add these views)
from .otp_service import OTPService

@method_decorator(csrf_exempt, name='dispatch')
class SendOTPView(View):
    """Send OTP to phone number"""

    def post(self, request):
        try:
            data = json.loads(request.body)
            phone_number = data.get('phone_number')

            if not phone_number:
                return JsonResponse({
                    'success': False,
                    'error': 'Phone number required'
                }, status=400)

            otp_service = OTPService()
            result = otp_service.send_otp(phone_number)

            return JsonResponse(result)

        except Exception as e:
            return JsonResponse({
                'success': False,
                'error': str(e)
            }, status=500)

@method_decorator(csrf_exempt, name='dispatch')
class VerifyOTPView(View):
    """Verify OTP"""

    def post(self, request):
        try:
            data = json.loads(request.body)
            phone_number = data.get('phone_number')
            otp = data.get('otp')

            if not phone_number or not otp:
                return JsonResponse({
                    'success': False,
                    'error': 'Phone number and OTP required'
                }, status=400)

            otp_service = OTPService()
            result = otp_service.verify_otp(phone_number, otp)

            return JsonResponse(result)

        except Exception as e:
            return JsonResponse({
                'success': False,
                'error': str(e)
            }, status=500)

Django Management Command

# sms/management/commands/send_sms.py
from django.core.management.base import BaseCommand
from sms.services import SMSService

class Command(BaseCommand):
    help = 'Send SMS from command line'

    def add_arguments(self, parser):
        parser.add_argument('phone', type=str, help='Phone number')
        parser.add_argument('message', type=str, help='Message to send')

    def handle(self, *args, **options):
        phone = options['phone']
        message = options['message']

        sms_service = SMSService()

        try:
            result = sms_service.send_sms(phone, message)
            self.stdout.write(
                self.style.SUCCESS(f'✓ SMS sent! Job ID: {result["job_id"]}')
            )
        except Exception as e:
            self.stdout.write(
                self.style.ERROR(f'✗ Error: {str(e)}')
            )

# Usage:
# python manage.py send_sms 9840123456 "Hello from Django!"

Response Examples

Successful Response

{
  "success": true,
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "message": "SMS queued successfully",
  "recipients_count": 1,
  "cost": 1.5
}

Error Response

{
  "success": false,
  "error": "Insufficient balance",
  "code": "INSUFFICIENT_BALANCE"
}

Best Practices

  1. Use environment variables for API keys
  2. Implement proper error handling and logging
  3. Use Django cache for OTP storage (Redis recommended)
  4. Add rate limiting to prevent abuse
  5. Log all SMS activities for audit trails
  6. Use Celery for async SMS sending in production
  7. Implement retry logic with exponential backoff

Next Steps