Whatify LogoWhatify
SDKs

Python SDK

Official Whatify SDK for Python applications

The official Whatify SDK for Python applications with support for Flask, Django, FastAPI, and more.

Before using this SDK, sign up at https://whatify.dev to get your API token.

Installation

Install via pip:

pip install whatify

Or with Poetry:

poetry add whatify

Quick Start

from whatify import WhatifyClient

client = WhatifyClient(api_token="your-api-token")

# Send a message
result = client.send_text("1234567890", "Hello from Python!")
print(result)

Configuration

from whatify import WhatifyClient

client = WhatifyClient(api_token="your-api-token")

Using API Key

client = WhatifyClient(api_key="your-api-key")

From Environment Variables

import os
from whatify import WhatifyClient

client = WhatifyClient(api_token=os.getenv("WHATIFY_API_TOKEN"))

API Methods

Connect Device

Connect your WhatsApp device.

try:
    result = client.connect()
    print(f"QR Code: {result['qrCode']}")
    print(f"Status: {result['status']}")
except WhatifyError as e:
    print(f"Error: {e}")

Response:

{
    'success': True,
    'qrCode': 'data:image/png;base64,...',
    'status': 'qr',
    'message': 'Scan QR code to connect'
}

Disconnect Device

try:
    result = client.disconnect()
    print(f"Disconnected: {result['success']}")
except WhatifyError as e:
    print(f"Error: {e}")

Check Status

try:
    result = client.status()
    print(f"Status: {result['status']}")
    print(f"Connected: {result['isConnected']}")
except WhatifyError as e:
    print(f"Error: {e}")

Response:

{
    'success': True,
    'status': 'connected',
    'isConnected': True,
    'message': 'Device is connected'
}

Send Messages

Send Text Message

# Single recipient
result = client.send_text("1234567890", "Hello World!")

# Multiple recipients
result = client.send_text(
    ["1234567890", "0987654321"],
    "Bulk message to everyone"
)

Send Attachment

result = client.send_attachment(
    to="1234567890",
    attachment={
        "type": "image",
        "url": "https://example.com/image.jpg",
        "caption": "Check this out!"
    }
)

Send Text with Attachment

result = client.send(
    to="1234567890",
    text="Here is your document",
    attachment={
        "type": "document",
        "url": "https://example.com/report.pdf",
        "filename": "report.pdf"
    }
)

Using Dictionary Syntax

result = client.send({
    "to": "1234567890",
    "text": "Hello!",
    "attachment": {
        "type": "video",
        "url": "https://example.com/video.mp4"
    }
})

Attachment Types:

  • image - Image files (jpg, png, gif)
  • video - Video files (mp4, avi, mov)
  • audio - Audio files (mp3, wav)
  • document - Documents (pdf, docx, xlsx)

Response:

{
    'success': True,
    'message': 'Message sent successfully',
    'messageIds': ['msg_123', 'msg_124']
}

Send OTP

Send One-Time Password (OTP) codes via WhatsApp with multi-language support.

Send OTP (English)

try:
    result = client.send_otp(
        phone="1234567890",
        code="123456",
        language="English"
    )
    print(f"OTP sent: {result['data']}")
except WhatifyError as e:
    print(f"Failed to send OTP: {e}")

Send OTP (Arabic)

result = client.send_otp(
    phone="1234567890",
    code="654321",
    language="Arabic"
)

Generate and Send OTP

import random

# Generate a random 6-digit OTP
otp_code = str(random.randint(100000, 999999))

result = client.send_otp(
    phone="1234567890",
    code=otp_code,
    language="English"
)

# Store OTP in database for verification
import datetime
db.otp.create({
    'phone': '1234567890',
    'code': otp_code,
    'expires_at': datetime.datetime.now() + datetime.timedelta(minutes=10)
})

Using Dictionary Syntax

result = client.send_otp({
    "phone": "1234567890",
    "code": "123456",
    "language": "English"
})

OTP Options:

  • phone (string, required) - Recipient phone number with country code
  • code (string|number, required) - OTP code (4-8 digits)
  • language (string, optional) - Message language: "English" or "Arabic" (default: "English")

Response:

{
    'success': True,
    'message': 'OTP sent successfully',
    'data': {
        'messageId': 'msg_xxx',
        'whatsappMessageId': 'wamid_xxx',
        'phone': '1234567890',
        'code': '123456',
        'language': 'English',
        'sentAt': '2024-01-01T12:00:00.000Z'
    }
}

Expamle OTP Message:

  • English: 123456 is your verification code. For your security, do not share this code
  • Arabic: 123456 هذا هو رمز التحقق الخاص بك. لحمايتك، يرجى عدم مشاركة هذا الرمز.

Error Handling

Exception Types

The SDK provides specific exception types for better error handling:

from whatify import WhatifyError, WhatifyAPIError, WhatifyConnectionError

try:
    result = client.send_text("1234567890", "Hello!")
except WhatifyAPIError as e:
    # API returned an error (4xx, 5xx)
    print(f"API Error: {e}")
    print(f"Status Code: {e.status_code}")
except WhatifyConnectionError as e:
    # Network/connection error
    print(f"Connection Error: {e}")
except WhatifyError as e:
    # Base exception for all Whatify errors
    print(f"Error: {e}")

Try-Except Pattern

try:
    result = client.send_text("1234567890", "Hello!")
    if result['success']:
        print("Message sent!")
except WhatifyError as e:
    print(f"Failed to send: {e}")

Framework Integration

Flask

from flask import Flask, request, jsonify
from whatify import WhatifyClient, WhatifyError
import os

app = Flask(__name__)

client = WhatifyClient(api_token=os.getenv("WHATIFY_API_TOKEN"))

@app.route('/send-message', methods=['POST'])
def send_message():
    data = request.json

    try:
        result = client.send_text(
            data['phone'],
            data['message']
        )
        return jsonify(result), 200
    except WhatifyError as e:
        return jsonify({'error': str(e)}), 500

@app.route('/status', methods=['GET'])
def check_status():
    try:
        result = client.status()
        return jsonify(result), 200
    except WhatifyError as e:
        return jsonify({'error': str(e)}), 500

if __name__ == '__main__':
    app.run(debug=True)

Django

Create a service class in services.py:

# myapp/services.py
from whatify import WhatifyClient, WhatifyError
from django.conf import settings

class WhatsAppService:
    def __init__(self):
        self.client = WhatifyClient(api_token=settings.WHATIFY_API_TOKEN)

    def send_notification(self, phone: str, message: str):
        try:
            return self.client.send_text(phone, message)
        except WhatifyError as e:
            raise Exception(f"Failed to send WhatsApp: {e}")

    def check_connection(self):
        try:
            return self.client.status()
        except WhatifyError as e:
            raise Exception(f"Failed to check status: {e}")

# Initialize singleton
whatsapp_service = WhatsAppService()

Use in views:

# myapp/views.py
from django.http import JsonResponse
from django.views import View
from .services import whatsapp_service

class SendMessageView(View):
    def post(self, request):
        phone = request.POST.get('phone')
        message = request.POST.get('message')

        try:
            result = whatsapp_service.send_notification(phone, message)
            return JsonResponse(result)
        except Exception as e:
            return JsonResponse({'error': str(e)}, status=500)

Add to settings.py:

# settings.py
WHATIFY_API_TOKEN = os.getenv('WHATIFY_API_TOKEN')

FastAPI

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from whatify import WhatifyClient, WhatifyError
import os

app = FastAPI()

client = WhatifyClient(api_token=os.getenv("WHATIFY_API_TOKEN"))

class SendMessageRequest(BaseModel):
    phone: str
    message: str

@app.post("/send-message")
async def send_message(request: SendMessageRequest):
    try:
        result = client.send_text(request.phone, request.message)
        return result
    except WhatifyError as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.get("/status")
async def check_status():
    try:
        result = client.status()
        return result
    except WhatifyError as e:
        raise HTTPException(status_code=500, detail=str(e))

Celery Task (Async Processing)

from celery import Celery
from whatify import WhatifyClient, WhatifyError
import os

app = Celery('tasks', broker='redis://localhost:6379/0')

client = WhatifyClient(api_token=os.getenv("WHATIFY_API_TOKEN"))

@app.task(bind=True, max_retries=3)
def send_whatsapp_message(self, phone: str, message: str):
    try:
        result = client.send_text(phone, message)
        return result
    except WhatifyError as e:
        # Retry after 60 seconds
        raise self.retry(exc=e, countdown=60)

@app.task
def send_bulk_messages(recipients: list, message: str):
    for phone in recipients:
        send_whatsapp_message.delay(phone, message)

Usage:

# Send immediately
send_whatsapp_message.delay("1234567890", "Hello!")

# Send bulk
send_bulk_messages.delay(
    ["1234567890", "0987654321"],
    "Important announcement"
)

Advanced Examples

Retry Logic with Backoff

import time
from whatify import WhatifyClient, WhatifyAPIError

def send_with_retry(client, phone, message, max_retries=3):
    for attempt in range(max_retries):
        try:
            return client.send_text(phone, message)
        except WhatifyAPIError as e:
            if e.status_code == 429 and attempt < max_retries - 1:
                # Rate limited, wait and retry
                wait_time = 2 ** attempt  # Exponential backoff
                time.sleep(wait_time)
                continue
            raise

client = WhatifyClient(api_token="your-token")
result = send_with_retry(client, "1234567890", "Hello!")

Context Manager

from whatify import WhatifyClient

class WhatifyContext:
    def __init__(self, api_token):
        self.client = WhatifyClient(api_token)

    def __enter__(self):
        # Check connection on enter
        status = self.client.status()
        if not status['isConnected']:
            raise Exception("WhatsApp not connected")
        return self.client

    def __exit__(self, exc_type, exc_val, exc_tb):
        # Cleanup if needed
        pass

# Usage
with WhatifyContext(api_token="your-token") as client:
    client.send_text("1234567890", "Hello!")

Batch Processing

from whatify import WhatifyClient
import time

client = WhatifyClient(api_token="your-token")

def send_batch(recipients, message, delay=1):
    """Send messages to multiple recipients with delay"""
    results = []

    for phone in recipients:
        try:
            result = client.send_text(phone, message)
            results.append({
                'phone': phone,
                'success': True,
                'result': result
            })
        except Exception as e:
            results.append({
                'phone': phone,
                'success': False,
                'error': str(e)
            })

        time.sleep(delay)  # Rate limiting

    return results

# Send to multiple users
recipients = ["1234567890", "0987654321", "5555555555"]
results = send_batch(recipients, "Bulk notification")

# Check results
for r in results:
    if r['success']:
        print(f"Sent to {r['phone']}")
    else:
        print(f"Failed {r['phone']}: {r['error']}")

Type Hints

The SDK includes comprehensive type hints:

from typing import Union, List, Dict, Any
from whatify import WhatifyClient

client: WhatifyClient = WhatifyClient(api_token="token")

# Type-safe function
def send_notification(
    phone: Union[str, List[str]],
    message: str
) -> Dict[str, Any]:
    return client.send_text(phone, message)

Rate Limits

  • 100 requests per minute per API token
  • 1000 requests per hour per API token

Implement rate limiting:

import time
from collections import deque

class RateLimiter:
    def __init__(self, max_calls, period):
        self.max_calls = max_calls
        self.period = period
        self.calls = deque()

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            now = time.time()

            # Remove old calls
            while self.calls and self.calls[0] < now - self.period:
                self.calls.popleft()

            # Check limit
            if len(self.calls) >= self.max_calls:
                sleep_time = self.period - (now - self.calls[0])
                time.sleep(sleep_time)

            self.calls.append(time.time())
            return func(*args, **kwargs)

        return wrapper

# Usage
@RateLimiter(max_calls=100, period=60)
def send_message(phone, text):
    return client.send_text(phone, text)

Support